Indledning

Det primære mål for SOSI-biblioteket er at indkapsle det meste af den komplekse logik i SOSI-konceptet bag et meget simpelt API.
Det har været vores mål at konstruere et enkelt indgangspunkt for alle udviklere (en factory-klasse), hvorfra det er muligt at erhverve enkle modelobjekter (POJO's

Plain Old Java Objects - https://en.wikipedia.org/wiki/Plain_old_Java_object

), der repræsenterer kernekoncepterne i SOSI-skemaet, f.eks. en meddelelse eller et ID-kort.
Når udviklerne har konstrueret disse modelobjekter, er det muligt at "serialisere" dem til XML og vice versa. Deserialisering (fra XML til modelobjekter) udføres også gennem factory-klassen.  Nedenfor vises et meget simpelt flow, hvor en serviceforbruger (f.eks. et medicinsystem) opretter en anmodning, indsætter data i anmodningen, serialiserer den til XML og sender den til en serviceforbruger.

Moduler

I forbindelse med version 3 af Seal.java er det besluttet at opdele koden, så den bliver fordelt i flere moduler. Det gøres bl.a. for at undgå at serviceudbydere behøver at hente al Seal.java koden ned, hvis man kun har behøv for at udstille en DGWS service.

Serviceudbydere kan desuden hente DGWS og IDWS modulerne uden at de samtidigt får WSS4J med ned, da dette modul, for nogen har givet store problemer.


Seal-common

Indeholder kode som bruges på tværs af IDWS og DGWS (og evt. fremtidige formater).

Seal-dgws

Indeholder den kode som en serviceudbyder behøver for at kunne udstille en DGWS service.

Seal-idws

Indeholder den kode, som en serviceudbyder behøver for at kunne udstille en IDWS service.

Seal-common-exchange

Koden som bruges til billetomveksling for både DGWS og IDWS.

Seal-dgws-exchange

Bruges hvis man ønsker at billetomveksle DGWS.

Dette gælder f.eks. JWTToOIOSAMLAssertion, OIOSAMLAssertionToIDCard og IDCardToOIOSAMLAssertion

Seal-idws-exchange

Bruges hvis man ønsker at billetomveksle IDWS.

Dette gælder f.eks. OIOBootstrapToIdentityToken, JWTToIdentityToken og EncryptedOIOSAMLAssertionToIdentityToken

Seal-sts

Bruges af STS, og det indeholder alt fra ovenståense moduler og et par ekstra klasser.

Seal-testtools

Bruger Seal-sts til at kunne lave tests mod alle modulerne

Snitfladeændringer

For at kunne lave opdelingen i moduler har vi været nød til at lave nogle ændringer til snitfladen.

SOSIFactory

SOSIFactory findes nu i 3 udgaver:


Biblioteker

Standard SOSI-factory


Fra version 2.6.1 er det muligt at anvende SOSIFacotry uden at bruge en SigntureProvider eller CredentialVault, så længe man ikke bruger funktionalitet der anvender signering. Ved forsøg på at gøre dette får man fejlbeskeden:

You cannot sign an IDCard using a SOSIFactory constructed without SignatureProvider or CredentialVault!

IDWSH-factory

Fra version 2.1 inkluderer Seal.Java nu IDWSHFactory. Version 2.1 markerer begyndelsen af IDWSH-support. Det formodes at fremdidige versioner af Seal.Java vil udvide support og arbejdsgang ved hjælp af IDWSH.
Seal.Java 2.1 understøtter kun IDWSH IdentityToken. Strømmen til konstruktion og anvendelse af IdentityToken er vist nedenstående figur.

IDWSH Identity  token workflow

OIOSAML-factory

I Seal 2.1.4 blev OIOSAMLFactory introduceret, som giver funktionalitet til at oprette, analysere, underskrive og validere OIOWS-Trust-meddelelser, der bruges ved udveksling af OIOSAML-påstande, der er udstedt af og IdP til SOSI IDCards.

OIOIDWS-factory

Siden Seal 2.5.10 understøttes OIO IDWS.

Eksempel på brug af IDWS hjælpeklasser for service udbydere.

/* Server will setup OIOIDWSFactory */
final Federation federation = new SOSITestFederation(SignatureUtil.setupCryptoProviderForJVM());
final CredentialVault serverTestVault = CredentialVaultTestUtil.getCredentialVaultFromResource(System.getProperties(), "fmk_idws_test.p12");

final OIOIDWSFactory oioidwsFactory = new OIOIDWSFactory(federation, serverTestVault);


// ****************** HANDLING INPUT ******************
// Server side input/request document
SOAPPart soapPart = soapRequestMessage.getSOAPPart();
SOAPEnvelope soapEnvelope = soapPart.getEnvelope();
SOAPBody soapBody = soapRequestMessage.getSOAPBody();
SOAPHeader soapHeader = soapRequestMessage.getSOAPHeader();

/*Parse request document into IDWS request*/
/*Validation is done in parsing. So if any error - no request object is created*/
LibertyRequest request = oioidwsFactory.createRequest(soapEnvelope);

/*Work on request*/
String messageID = request.getMessageID();
assertNotNull(messageID); // Client did not set MessageID - but in cannot be null
String to = request.getTo();
assertEquals("https://myApp", to);

/*Work with CitizenIdentityToken*/
CitizenIdentityToken identityToken = request.getIdentityToken();
assertNotNull(identityToken); // Must be there!
String cpr = identityToken.getCpr();
assertEquals("2512484916", cpr);
BasicPrivileges basicPriviliges = identityToken.getPrivileges();
assertNotNull(basicPriviliges);
/*Work with priviliges*/
Set<String> scopes = basicPriviliges.getScopes();
for (String scope : scopes) {
List<String> privileges = basicPriviliges.getPrivileges(scope);

assertEquals("urn:dk:healthcare:saml:actThroughProcurationBy:cprNumberIdentifier:1111111118", scope);

assertEquals("urn:dk:healthcare:fmk:read", privileges.get(0));
assertEquals("urn:dk:healthcare:fmk:write", privileges.get(1)); 
}

List<String> privilegeList = basicPriviliges.getPrivileges("urn:dk:healthcare:saml:actThroughProcurationBy:cprNumberIdentifier:1111111118");
assertNotNull(privilegeList);
assertEquals(2, privilegeList.size());

/*Work in my app*/
Tag myAppTag = TagUtil.create("http://demo.dk/custom", "myApp","App");
Element myApp = TagUtil.getFirstChildElementNS(soapBody, myAppTag);
String myAppRequestAsString = XmlUtil.node2String(myApp, false, false);
assertEquals("<myApp:App xmlns:myApp=\"http://demo.dk/custom\"><myApp:Field_1>value1</myApp:Field_1><myApp:Field_2 custom_attribute_2=\"value2\"/></myApp:App>", myAppRequestAsString);

Tag myAppHeaderTag = TagUtil.create("http://demo.dk/custom", "myApp","Header");
Element myHeader = TagUtil.getFirstChildElementNS(soapHeader, myAppHeaderTag);
String myAppRequestHeaderAsString = XmlUtil.node2String(myHeader, false, false);
assertEquals("<myApp:Header xmlns:myApp=\"http://demo.dk/custom\">my_app_header</myApp:Header>", myAppRequestHeaderAsString);

// ****************** CREATING OUTPUT ******************
Document outputDocument = XmlUtil.readXml(System.getProperties(), MY_SOAP_APP_REPONSE, false);

/*Sign repsonse with test vault*/
LibertyMessageDOMEnhancer enhancer = oioidwsFactory.createResponseDomEnhancer(outputDocument, true);
enhancer.setWSAddressingAction("myApp.response"); //Required
//enhancer.setWSAddressingMessageID(); //Required - one will be made if not set be server
enhancer.setWSAddressingRelatesTo(request.getTo()); //Required - Validation will fail if not set
//enhancer.setWSAddressingTo("client"); //Optional

enhancer.enhanceAndSign(); // Enhance and sign output document into IDWS valid reponse

/*create response message*/
MessageFactory messageFactory = MessageFactory.newInstance(SOAPConstants.SOAP_1_1_PROTOCOL);
soapResponseMessage = messageFactory.createMessage(null, new ByteArrayInputStream(XmlUtil.node2String(outputDocument).getBytes()));
//return responseSoapMessage
//System.out.println(XmlUtil.node2String(responseSoapMessage.getSOAPPart().getEnvelope(), true, false));

Federations

Når SOSIFactory er oprettet, er det let at komme i gang. Overvej følgende kodestykker, der indeholder kode til opbygning af en anmodning (request) som sendes til en tjenesteudbyder (Service provider).

Service Consumer

Properties properties = ...;
SOSITestFederation testFederation = new SOSITestFederation(properties);
CredentialVault credentialVault = ...; // construct or resolve credentialvault here

SOSIFactory factory = new SOSIFactory(testFederation, credentialVault, properties);
Request request = factory.createNewRequest(
		false, // don’t require non-repudiation receipt
		null // Optional flow-ID (not used here)
);

IDCard idCard = ...; // resolve ID-card here

request.setIDCard(idCard);

Element body = ...; // build body DOM element here
request.setBody(body);

Document domDocument = request.serialize2DOMDocument();
String xml = XmlUtil.node2String(domDocument, false, true);
//Send xml to Service provider here

Det reelle arbejde ligger i følgende linier:

Linie 1: Specificering af egenskaber for SOSI, se senere for reference til SOSI-egenskaber
Linie 3: Opbygge/finde instans af Credential Vault.
Linie 11:  Opbygge/finde instans af ID-kort.
Linie 15: Opbygning af indhold til XML
Linie 20: Sende XML til  serviceprovider

Service  Provider

På den "anden side" hos serviceudbyderen bruges biblioteket således. Også her håndterer udvikleren kun ting, der er relateret til forretningsopgaven.

Properties properties = ...;
Federation federation = new SOSIFederation(properties);
CredentialVault credentialVault = ...; // construct or resolve credentialvault here
SOSIFactory factory = new SOSIFactory(federation, credentialVault, properties);

// This implicitely verifies the STS signature on the ID card etc.
Request request = factory.deserializeRequest(xml);

IDCard idCard = request.getIDCard();

// use ID card attributes for authorization here

Element body = request.getBody();

// use information in body for business logic here

Reply reply = factory.createNewReply(
		request,	// dgws version and”In response to” ID
		null 		// Optional flow-ID set to null
);

reply.setIDCard(idCard);

Element replyBody = ...; // build reply body DOM element here
reply.setBody(replyBody);

Document domDocument = reply.serialize2DOMDocument();
String replyXML = XmlUtil.node2String(domDocument, false, true);
// Send replyXML to Service provider here


Konvertering af OCES3-certifikater

OCES3-certifikaterne bruger krypteringsalgoritment AES256 i stedet for 3DES der bruges i OCES2-certifikater. Java 8 understøtter ikke denne krypteringsalgoritme og det er den Java version skal anvendes på NSP.

 Se indholdet af et OCES3-certifikat

Hvis man blot vil se certifikatet, så kan man gøre det vha. OpenSSL og dermed bruge Java 8:

openssl pkcs12 -nodes -in NSTSSnullAnull_Olsen.p12

Indtast nu certifikatets password.

Skift password af et OCES3-cetifikat

Det er muligt at transformere certifikatet fra AES256 til 3DES vha. OpenSSL og man kan samtidig skifte certifikatets password. Det kan gøres i to skridt:

openssl pkcs12 -in NSTSSnullAnull_Olsen.p12 -out NSTSSnullAnull_Olsen.pem -nodes

Indtast nu det medsendte password der blev udleveret sammen med certifikatet.

Indtast nu sidste del af kommandoen:

openssl pkcs12 -export -out NSTSSnullAnull_Olsen-new.p12 -in NSTSSnullAnull_Olsen.pem

Der bliver spurgt om certifikatets nye password - brug f.eks. Test1234.

Det nye certifikat findes i filen NSTSSnullAnull_Olsen-new.p12


Det er nu muligt at se certifikatet med keytool. Eksempel hvor certifikatets password er Test1234:

keytool -v -list -keystore NSTSSnullAnull_Olsen-new.p12 -storepass Test1234