Page History
...
Version | Dato | Ændring | Ansvarlig |
0.8 | 29-11-2012 | Oprettet | IO |
1.0 | 04-04-2013 | redigeret | IO |
3.0.0 | 03-01-2017 | Fjernet .net 3.5 referancer | FMO |
4.0.0 | 26-06-2017 | Tilføjet IdCards og OioSamlFactory | KRO og SKS |
4.0.1 | 13-10-2022 | Rettet "SOSI Gateway SBO" dokumentation så den passer til ændringerne i '4.2.5'. | KvalitetsIT |
5.0.0 | 09-11-2022 | Tilføj migreringsguide for version '5.0.0'. | KvalitetsIT |
...
...
| Table of Contents |
|---|
Indledning
Den Gode WebService (DGWS) er en profil for webservices, som bygger på flere WebService standarder fra WS* stakken. Det er ikke en triviel opgave at designe en klient eller en webservice implementering, der overholder DGWS profilen. Seal.Net Api'ets formål er, at sænke den tærskel der uvægerligt er forbundet med at med at udvikle software der overholder Den Gode WebService. Seal.Net indpakker alle DGWS specifikke detaljer abstraherer alle typer fra XML til objektform. API'et tager sig af validering og signering aktuelle klasser.
API'et er leveret som.Net assemblies og kan benyttes med forskellige sprogbindinger herunder f.eks. VB.NET, C#, etc.
Seal.Net kan ligeledes også installeres som NuGet pakke.
...
...
Historik
Den Gode WebService er specificeret i tre versioner 1.0, 1.0.1 og 1.1. Ingen af versionerne er kompatible og der er tidligere udviklet individuelle Api'er til at understøtte disse versioner.
Dette Api understøtter alle nuværende versioner af DGWS i samme implementering for hhv. klient og service.
Der er dog væsentlige designforskelle mellem dette API og tidligere versioner.
...
Systemkrav
Api'et er bygget til WCF (Windows Communication Foundation), WIF (Windows Identity Foundation), og kræver I nyeste version som minimum .Net version 4.6.2.
Læseren af denne guide forudsættes at have indsigt i C#, WCF, WIF og XML.
...
...
Leverancer
Seal.Net leveres som to assemblies. En der assembly der indeholder datatyperne for til Seal og en der indeholder logikken.Anchor
...
for til Seal og en der indeholder logikken.
...
Seal.Net Api'et
...
...
Designvalg
Seal.net er bygget oven på WCF og WIF
Ovennævnte Api'er er valgt da de er supporteret af Microsoft og indgår som standard i .Net frameworket fra .Net version 3.5. Yderligere giver WCF udpræget mulighed for at benytte aspekt orienteret udvikling.
Grundet inkompatibilitet mellem Saml2.0 og Seal Saml har det været nødvendigt i enkelte tilfælde at gå uden om WCF.
I en WSDL der beskriver en snitflade til Den Gode Webservice (DGWS) indgår skemaer der beskriver de specifikke DGWS klasser. Når der genereres en proxy til hhv. klient eller server, dannes disse klasser på typestærk form i den autogenererede proxy. Eksempler på genererede klasser er Security, Assertion og Header.
Dette Api er designet benytte disse klasser i videst mulig omfang.
Designet tager yderlige højde for at alle klienter kan konfigureres både via kode og konfigurationsfiler
...
Ordforklaringer
SealAssertion
Dækker over typerne som er specificeret i Seal. Det er ikke helt kompatible med Saml2 standarden.
...
Er microsoft's implementering af Saml2 som er en del af WIF.
DGWSHeader
Den Gode Webservice specificerer en ekstra headertype. DGWSHeader implementerer denne typer.
...
DGWSHeader
Den Gode Webservice specificerer en ekstra headertype. DGWSHeader implementerer denne typer.
...
Objekter
Følgende figurer illustrerer de klasser der indgår Seal.Net inddelt i grupper:
...
Indeholder forskellige funktioner f.eks. til at signere og validere en signatur. Derudover kan man sende requests fra OioSamlFactory afsted uden brug af clienter.
...
...
Brug af Seal.Net Api
I dette afsnit beskrives overordnet hvordan Seal.Net benyttes til at opbygge en klient eller en service applikation.
...
CheckCrl specificerer om certifikatet skal checkes op mod en Certificate Revocation List. Skal slås fra ved self signed certifikater.anchor
<add key="CheckCrl" value="false"/>
</appSettings>
CheckCrl er per default False.
...
Klienteksempler
En klient til en WebService af typen Den Gode Webservice benyttes som en hver anden webservice.
I Visual Studio tilføjes en Service-Reference til den pågældende WSDL. Herefter genereres en proxy indeholdende alle datatyper for webservicen.
Det er også muligt at benytte WcfUtil.exe hvis Visual Studio ikke er tilstrækkelig.
Herefter kan der være flere klient-scenarier:
- Direkte kald af Service (ikke føderalt)Her oprettes et kort lokalt og Service Udbyderen kaldes med det oprettede kort. Det er så op til Service Udbyderen at autentificere brugeren,
- FøderaltEt kort oprettes lokalt valideres via et kald til en STS, som returnerer et kort der er digitalt underskrevet. Dette kort benyttes til fremtidige kald af webservices. Service Udbyderen skal nu kun autentificere STS'en.
- Via NemIdHvis en bruger allerede er logget på et system via NemLogin, kan dette login benyttes til at kalde en webservice.Til NemLogin er associeret en SamlToken. Denne token skal veksles til et IDKort til Den Gode Webservice. Når IDKortet er modtaget benyttes det til fremtidige kald.
- SOSI Gateway
...
- SOSI Gateway
...
Til efterfølgende eksempler benyttes nogle metoder til at oprette instanser af datatyper.
MakeHeader og MakeSecurity.
Disse metoder opretter instanser af de før nævnte klasser; Security og Header.
Der benyttes desuden variablerne callingSystem og user, som kan ses under " eksempler på generering af proxyklasser".
Eksemplerne viser et kald til en FKM webservice. Metodekaldet er GetMedicineCard_2015_06_01
...
Direkte kald
- var client = new MedicineCardPortTypeClient("MedicineCardPort");
- GenericCredentialVault vault = new GenericCredentialVault("FMKTestStore");
...
Følgende konfiguration er benyttes i ovenstående eksempel.
<endpoint
address="https://test2.fmk.netic.dk/fmk12/ws/MedicineCard"
behaviorConfiguration="sealbehavior"
binding="basicHttpBinding"
bindingConfiguration="MedicineCardBinding"
contract="MedicinCard.MedicineCardPortType"
name="MedicineCardPort"
/>
<basicHttpBinding>
<binding name="MedicineCardBinding">
<security mode="Transport"/>
</binding>
</basicHttpBinding>
<behavior name="sealbehavior" >
<sbhe/>
</behavior>
<behaviorExtensions>
<add name="sbhe" type="dk.nsi.seal.SealBehaviorExtentionElement, Seal" />
</behaviorExtensions>
Som det ses er benyttes der en customBinding uden sikkerhed og der benyttes SealBehaviorExtentionElement til at aktivere SealEndpointBehavior.Anchor
...
...
Føderalt
I et føderalt login skal en bruger først logges på føderationen
...
Konfigurationen er det samme som ved Direkte kald i forrige afsnit.Anchor
address="https://test2.fmk.netic.dk/fmk12/ws/MedicineCard"
behaviorConfiguration="sealbehavior"
binding="basicHttpBinding"
bindingConfiguration="MedicineCardBinding"
contract="MedicinCard.MedicineCardPortType"
name="MedicineCardPort"
/>
<basicHttpBinding>
<binding name="MedicineCardBinding">
<security mode="Transport"/>
</binding>
</basicHttpBinding>
<behavior name="sealbehavior" >
<sbhe/>
</behavior>
<behaviorExtensions>
<add name="sbhe" type="dk.nsi.seal.SealBehaviorExtentionElement, Seal" />
</behaviorExtensions>
...
...
Secure browser logon
Efterfølgende kode og konfiguration viser hvordan der oprettes en krypteret Assertion som kan benyttes til SBO.
...
1. Et IdCard oprettes fra SOSIFactory.
2. Et nyt IdCard oprettes underskrevet af STS.
3. En proxy til STS der veksler IdCard oprettes.
4. ClientCredentials sættes.
5. STS til konvertering af assertion kaldes.
6. Det genererede krypterede kort hentes som XML.
Konfiguration:
<endpoint address="http://test1.ekstern-test.nspop.dk:8080/sts/services/Sosi2OIOSaml"
binding="customBinding"
bindingConfiguration="Soap11Http"
behaviorConfiguration="SealSigning"
contract="System.ServiceModel.Security.IWSTrustChannelContract"
name="Seal2EncSaml" />
<customBinding>
<binding name="Soap11Http">
<textMessageEncoding messageVersion="Soap11WSAddressing10" writeEncoding="utf-8" />
<httpTransport />
</binding>
</customBinding>
<behavior name="SealSigning">
<SealSigningBE/>
</behavior>
<behaviorExtensions>
<add name="SealSigningBE" type="dk.nsi.seal.SealSigningBehaviorExtentionElement, Seal"/>
</behaviorExtensions>
...
...
SOSI Gateway
Nå SOSI Gateway skal benyttes skal der ført oprettes en service reference SOSI Gateway. Hvis ikke der allerede er oprettet oprettes også en service reference til servicen, i nedenstående eksempel FMK.
Følgende kode opretter et login på SOSI Gateway.
using GW = SealTest.SosiGWReference;
private static Assertion LoginToGateway(IdCard idc, X509Certificate2 certificate)
{
// Convert the dgwsType Assertion into a sosi gateway Assertion.
var assertion = idc.GetAssertion<GW.AssertionType>();
var security = new GW.Security
{
Timestamp = new GW.Timestamp { Created = DateTimeEx.UtcNowRound - TimeSpan.FromMinutes(5) },
Assertion = assertion
};
using (var gwClient = new GW.SosiGWFacadeClient())
{
// Get an digest form the sosi gateway that should be signed
var dig = gwClient.requestIdCardDigestForSigning(security, "whatever");
// Create SHA1 hash of digest
var sha1Managed = new SHA1Managed();
var computeHash = sha1Managed.ComputeHash(dig.DigestValue);
// Get the private key
var privateKey = (RSACryptoServiceProvider) certificate.PrivateKey;
// Calculate the needed signature
var signatureValue = privateKey.SignHash(computeHash, CryptoConfig.MapNameToOID("SHA1"));
var cardRequestBody = new GW.signIdCardRequestBody
{
SignatureValue = signatureValue,
KeyInfo = new GW.KeyInfo
{
Item = new GW.X509Data {Item = certificate.Export(X509ContentType.Cert)}
}
};
// The unsigned id-card in the cache is combined with the signature and the certificate and is sent to STS.
// The STS signed card is saved in the sosiGw cache and is used for future calls through the proxy
var res = gwClient.signIdCard(security, cardRequestBody);
if (res != GW.signIdCardResponse.ok)
{
throw new Exception("Gateway logon error");
}
// Convert the GW Assertion to a dgwsType Assertion for later use.
idc.Xassertion = SerializerUtil.Serialize(security.Assertion).Root;
return idc.GetAssertion<Assertion>(typeof(GW.AssertionType).Name);
}
...
1. Opretter en SosiFactory og IdCardRequest
2. Kalder LoginToGateway som har inline kommentar
3. FMK klient oprettes.
4. Service kaldes
Konfiguration:
SOSIGW
<endpoint address="http://test2.ekstern-test.nspop.dk:8080/sosigw/service/sosigw"
binding="basicHttpBinding"
contract="SosiGwService.SosiGWFacade"
name="SosiGWSoapBinding"/>
FMK
<endpoint address="https://test2.fmk.netic.dk/fmk12/ws/MedicineCard"
behaviorConfiguration="AddressingBehavior"
binding="customBinding"
bindingConfiguration="Soap11Http"
contract="MedicinCard.MedicineCardPortType"
name="SosiGWFMK"/>
<customBinding>
<binding name="Soap11Http">
<textMessageEncoding messageVersion="Soap11WSAddressing10" writeEncoding="utf-8" />
<httpTransport />
</binding>
</customBinding>
<behavior name="AddressingBehavior">
<clientVia viaUri="http://test2.ekstern-test.nspop.dk:8080/sosigw/proxy/soap-request"/>
</behavior>
...
SOSI Gateway SBO
En token til SBO kan hentes fra SOSI Gateway på nedenstående måde. Det forudsættes at der er logget på SOSI Gateway og dermed er assertion initieret.
Endpoint refererer til STS og ClientVia refererer til SOSI Gateway.
using (var stsClient = new Seal2SamlStsClient("GWFetchCard"))
using (var scope = new OperationContextScope((IContextChannel)stsClient.Channel.Channel))
{
var factory = CreateFactory();
OperationContext.Current.OutgoingMessageHeaders.Add(new IdCardMessageHeader( factory.DeserializeIdCard(assertion)));
var d = stsClient.ExchangeAssertionViaGW( "http://sundhed.dk/") as GenericXmlSecurityToken;
var elm = d.TokenXml;
}
...
<configuration>
<system.serviceModel>
<bindings>
<customBinding>
<bindingname="Soap11Http">
<textMessageEncodingmessageVersion="Soap11"writeEncoding="utf-8" />
<httpTransport /> <!-- If client certificate is required, use '<httpsTransport requireClientCertificate="true" />' instead, -->
</binding>
</customBinding>
</bindings>
<client>
<endpoint
address="http://test1.ekstern-test.nspop.dk:8080/sts/services/Sosi2OIOSam"
behaviorConfiguration="AddressingBehavior"
binding="customBinding"
bindingConfiguration="Soap11Http"
contract="System.ServiceModel.Security.IWSTrustChannelContract"
name="GWFetchCard"
/>
</client>
<behaviors>
<endpointBehaviors>
<behaviorname="AddressingBehavior">
<clientViaviaUri="http://test2.ekstern-test.nspop.dk:8080/sosigw/proxy/soap-request" />
<clientCredentials>
<clientCertificatefindValue="Insert certificate thumprint here"storeLocation="LocalMachine"storeName="My"x509FindType="FindByThumbprint" />
</clientCredentials>
<DccSealBehaviorBE />
</behavior>
</endpointBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<addname="DccSealBehaviorBE"type="dk.nsi.seal.DccSealBehaviorExtentionElement, Seal" />
</behaviorExtensions>
</extensions>
</system.serviceModel>
</configuration>
...
configuration>
Eksempler ved brug af OioSamlFactory
...
...
Brug af NemId:
Efterfølgende kode viser hvordan en NemidLogin Saml2Assertion konverteres til en DGWSAssertion. For herefter at oprette en Security instans der indeholder denne Assertion og kalde en FMK service.
...
Sådanne bootstraptokens kan enten være for medarbejdere (hertil benyttes createOIOBSTSAMLAssertion(...)) eller borgere (hertil benyttes createOIOBSTCitizenSAMLAssertion(...)), og identificeres ud fra deres spec version attribut.
...
Eksempler på generering af objekter fra proxy
Nedenstående eksempler viser initieringer at klasser der er autogenereret ud fra en DGWS WSDL.
...
...
CallingSystem og User
var callingSystem = new CallingSystem(
systemName: "Seal.Net.Fmk.Demo",
careProviderId: "30808460",
careProviderName: "TRIFORK SERVICES A/S",
careProviderIdFormat: dk.nsi.seal.dgwstypes.SubjectIdentifierType.medcomcvrnumber,
certificate: new X509Certificate2("Resources/certificates/Statens_Serum_Institut_FOCES.p12", "Test1234"));
var user = new User(
cpr: "1802602810",
givenName: "Stine",
surName: "Svendsen",
email: "stineSvendsen@example.com",
role: "læge",
authCode: "ZXCVB",
occupation: "Overlæge",
certificate: new X509Certificate2("Resources/certificates/MOCES_cpr_gyldig.p12", "Test1234"));
...
Security
Nedenstående eksempel opretter en Security element Timestamp sættes til at være 5 minutter gammel.
static Security MakeSecurity( Assertion assertion)
{
return new Security
{
id = Guid.NewGuid().ToString("D"),
Timestamp = new Timestamp { Created = DateTime.Now - TimeSpan.FromMinutes(5) },
Assertion = assertion
};
}
...
...
Header
static Header MakeHeader()
{
return new Header
{
SecurityLevel = 3,
TimeOut = TimeOut.Item1440,
TimeOutSpecified = true,
Linking = new Linking
{
FlowID = Guid.NewGuid().ToString("D"),
MessageID = Guid.NewGuid().ToString("D")
},
FlowStatus = FlowStatus.flow_running,
FlowStatusSpecified = true,
Priority = Priority.RUTINE,
RequireNonRepudiationReceipt = RequireNonRepudiationReceipt.yes
};
}
...
...
Make NemId Assertion
public static Saml2Assertion MakeNemIdAssertion(
X509Certificate2 userCertificate,
X509Certificate2 signingCertificate,
CallingSystem system,
User user
)
{
var ass = new Saml2Assertion(new Saml2NameIdentifier("https://saml.test-nemlog-in.dk/"))
{
Conditions = new Saml2Conditions
{
NotOnOrAfter = DateTime.Now + TimeSpan.FromHours(8),
NotBefore = DateTime.Now
},
Subject = new Saml2Subject(new Saml2NameIdentifier(userCertificate.SubjectName.Name))
};
ass.Subject.SubjectConfirmations.Add(
new Saml2SubjectConfirmation(new Uri("urn:oasis:names:tc:SAML:2.0:cm:bearer"))
{
SubjectConfirmationData = new Saml2SubjectConfirmationData
{
NotOnOrAfter = DateTime.Now + TimeSpan.FromHours(8),
Recipient = new Uri("https://staging.fmk-online.dk/fmk/saml/SAMLAssertionConsumer")
}
});
IList<Saml2Attribute> q = new List<Saml2Attribute>();
// Spec
q.Add(new Saml2Attribute(OioSamlAttributes.SpecVersion, SpecVersion) {NameFormat = BasicNameFormat});
// User
q.Add(new Saml2Attribute(OioSamlAttributes.CommonName, user.GivenName) {NameFormat = BasicNameFormat});
q.Add(new Saml2Attribute(OioSamlAttributes.Surname, user.SurName) {NameFormat = BasicNameFormat});
q.Add(new Saml2Attribute(OioSamlAttributes.Email, user.Email) {NameFormat = BasicNameFormat});
q.Add(new Saml2Attribute(OioSamlAttributes.CprNumber, user.Cpr) {NameFormat = BasicNameFormat});
q.Add(new Saml2Attribute(OioSamlAttributes.AssuranceLevel, "4") {NameFormat = BasicNameFormat});
q.Add(new Saml2Attribute(OioSamlAttributes.UserCertificate, Convert.ToBase64String(userCertificate.RawData)) {NameFormat = BasicNameFormat});
// Organization
q.Add(new Saml2Attribute(OioSamlAttributes.CvrNumber, system.CareProviderId) {NameFormat = BasicNameFormat});
q.Add(new Saml2Attribute(OioSamlAttributes.OrganizationName, system.CareProviderName) {NameFormat = BasicNameFormat});
// Certificate
var subjectSerialNumber = userCertificate.SubjectName.Name;
q.Add(new Saml2Attribute(OioSamlAttributes.CertificateSerial, userCertificate.GetSerialNumberString()) {NameFormat = BasicNameFormat});
q.Add(new Saml2Attribute(OioSamlAttributes.CertificateIssuer, userCertificate.IssuerName.Name) {NameFormat = BasicNameFormat});
q.Add(new Saml2Attribute(OioSamlAttributes.Uid, ExtractUidNumber(subjectSerialNumber)) {NameFormat = BasicNameFormat});
q.Add(new Saml2Attribute(OioSamlAttributes.RidNumber, ExtractRidNumber(subjectSerialNumber)) {NameFormat = BasicNameFormat});
ass.Statements.Add(new Saml2AttributeStatement(q));
ass.Statements.Add(
new Saml2AuthenticationStatement(
new Saml2AuthenticationContext(new Uri("element:urn:oasis:names:tc:SAML:2.0:ac:classes:X509")),
DateTime.Now));
ass.SigningCredentials = new X509SigningCredentials(signingCertificate, SignedXml.XmlDsigRSASHA1Url, SignedXml.XmlDsigSHA1Url);
return ass;
}
...
I klassen SosiGwTest er der tests af kald til SOSI Gateway. Her er der både test af kald mod createIdCardFromBST og requestIdCardDigestForSigning.
...
Referencer
Forkortelse i teksten | Henvisning | ||
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="a906cdd8-a856-48fa-b3af-9e69d149de6f"><ac:plain-text-body><![CDATA[ | [SAML2.0] | https://www.oasis-open.org/committees/tc_home.php?wg_abbrev=security | ]]></ac:plain-text-body></ac:structured-macro> |
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="7e8fddac-a1c4-4c1e-893d-d02f891e609d"><ac:plain-text-body><![CDATA[ | [OIOSAML] | http://digitaliser.dk/resource/2377872 | ]]></ac:plain-text-body></ac:structured-macro> |
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="923053d9-c34e-46e7-879d-75fa83f9df93"><ac:plain-text-body><![CDATA[ | [OIOIDWS] | http://digitaliser.dk/resource/526486 | ]]></ac:plain-text-body></ac:structured-macro> |
