Comment rendre le client WCF conforme à WS-Security spécifique – signer UsernameToken et SecurityTokenReference

Je dois créer un client wcf pour appeler un service sur lequel je n’ai aucun contrôle.

On m’a donné un projet de travail et un projet de travail.

Le service utilise un nom d’utilisateur / mot de passe et un certificate x509.


METTRE À JOUR

Je comprends maintenant quel est le problème, mais je ne sais toujours pas quelles mesures je dois prendre pour pouvoir créer le message requirejs. Toute aide serait donc la bienvenue.

Je dois signer à la fois UsernameToken et SecurityTokenReference.

Le code que j’ai dû créer pour la liaison personnalisée a été supprimé de cet article car il n’est plus utilisé. Je n’ajoute plus d’élément SecurityBindingElement à la liaison, mais un nouveau comportement qui écrit l’élément de sécurité dans l’en-tête.

Ainsi, le nœud de sécurité est créé à partir de rien en sous-classant la classe SignedXml, en ajoutant des références de signature, puis en appelant ComputeSignature pour créer le nœud Signature dans l’en-tête de sécurité.

Vous devez passer le xml pour vous connecter au constructeur SignedXml pour que cela fonctionne. Ce n’est pas un problème de passer UsernameToken et cela semble être signé correctement.

Le problème est que SecurityTokenReference est créé uniquement lorsque ComputeSignature () est appelé. Par conséquent, je ne peux pas append de référence de signature à cet élément, car il n’existe pas au moment où il est requirejs (dans la méthode GetIdElement remplacée de SignedXml qui s’appelle avant ComputeSignature ())


Le code que j’utilise pour créer le bloc de signature à insérer dans l’en-tête de sécurité est le suivant

ssortingng certificateePath = System.Windows.Forms.Application.StartupPath + "\\" + "Certs\\sign-and- enc.p12"; XmlDocument xd = new XmlDocument(); xd.LoadXml(xml); // Set Certificate System.Security.Cryptography.X509Certificates.X509Certificate2 cert = new X509Certificate2(certificateePath, "password"); MySignedXml signedXml = new MySignedXml(xd); signedXml.SigningKey = cert.PrivateKey; // Create a new KeyInfo object. KeyInfo keyInfo = new KeyInfo(); keyInfo.Id = ""; MemoryStream keyInfoStream = new MemoryStream(); XmlWriter keyInfoWriter = XmlWriter.Create(keyInfoStream); WSSecurityTokenSerializer.DefaultInstance.WriteKeyIdentifierClause(keyInfoWriter, new LocalIdKeyIdentifierClause("token_reference", typeof(X509SecurityToken))); keyInfoWriter.Flush(); keyInfoStream.Position = 0; XmlDocument keyInfoDocument = new XmlDocument(); keyInfoDocument.Load(keyInfoStream); XmlAtsortingbute atsortingb = keyInfoDocument.CreateAtsortingbute("ValueType"); atsortingb.InnerText = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"; keyInfoDocument.ChildNodes[1].ChildNodes[0].Atsortingbutes.Append(atsortingb); KeyInfoNode keyInfoNode = new KeyInfoNode(); keyInfoNode.LoadXml(keyInfoDocument.DocumentElement); keyInfo.AddClause(keyInfoNode); // Add the KeyInfo object to the SignedXml object. signedXml.KeyInfo = keyInfo; // Need to use External Canonicalization method. signedXml.SignedInfo.CanonicalizationMethod = "http://www.w3.org/2001/10/xml-exc-c14n#"; // Create a reference to be signed. Reference reference = new Reference(); reference.Uri = "#UsernameToken-1"; XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform(); env.Algorithm = "http://www.w3.org/2001/10/xml-exc-c14n#"; reference.AddTransform(env); reference.DigestMethod = "http://www.w3.org/2000/09/xmldsig#sha1"; signedXml.AddReference(reference); Reference reference2 = new Reference(); reference2.Uri = "#token_reference"; XmlDsigEnvelopedSignatureTransform env2 = new XmlDsigEnvelopedSignatureTransform(); env2.Algorithm = "http://www.w3.org/2001/10/xml-exc-c14n#"; reference2.AddTransform(env2); reference2.DigestMethod = "http://www.w3.org/2000/09/xmldsig#sha1"; signedXml.AddReference(reference2); // Add the Signature Id signedXml.Signature.Id = "MYSIG_ID"; // Compute the signature. signedXml.ComputeSignature(); XmlElement xmlDigitalSignature = signedXml.GetXml(); 

où la variable xml est la chaîne xml UsernameToken et la classe MySignedXml est un sous-classé SignedXml avec la méthode GetIdElement remplacée (pour essayer de trouver et de référencer correctement le SecurityTokenReference non existant existant)

J’ai passé des jours à rechercher et à tester cela, et malheureusement, la société fournissant le service n’est d’aucune aide, mais je dois utiliser leur service.

Message de travail complet (projet soapUI)

    MIIEnDCCBAWgAwIBAgIBAjANBgkqhkiG/RhRiqsopbUrb8AU1ClpfhbH2K68kg7V91VAY0qrwNxP+pPPo1vYKMU6pT38qJGQzffr+iV2BCJshZvSk9E7QSWO5mFNstdg19xc+5ST1Lgb3fefuRG2KZVxPx0sCAwEAAaOCAZUwggGRMAkGA1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQDAgZAMDQGCWCGSAGG+EIBDQQnFiVFYXN5LVJTQSBHZW5lcmF0ZWQgU2VydmVyIENlcnRpZmljYXRlMB0GA1UdDgQWBBSczRKXKPe3Sin7eFrVXfI7MXckzzCB+QYDVR0jBIHxMIHugBSLWxPSZd9otEj16vhLyovMCI9OMaGByqSBxzCBxDELMAkGA1UEBhMCTloxDTALBgNVBAgTBFdHVE4xEzARBgNVBAcTCldlbGxpbmd0b24xGDAWBgNVBAoTD0lSRFN0dWR5bGlua0IyQjEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxHjAcBgNVBAMTFWRldmNhLmIyYi5pcmQuZ292dC5uejEXMBUGA1UEKRMORGV2ZWxvcG1lbnQgQ0ExKDAmBgkqhkiG9w0BCQEWGWNocmlzLnNjaHVsdHpAaXJkLmdvdnQubnqCCQDL/qDdlx2j6DATBgNVHSUEDDAKBggrBgEFBQcDATALBgNVHQ8EBAMCBaAwDQYJKoZIhvcNAQEFBQADgYEAS4ZPIVVpgTOGN4XcIC3SiYlxF8wYg7qnUhH5wJsAD3VCKfj68j9FSJtdBWLlWvvRxEoDP2lZ0IbFl6Rjnl+2ibzFnyac2G1AVm5mwPrNKHBQJ9J5eDJi0iUVY7Wphz86tKnqj34GvlHPNXmrF7oGEcDhPwK0T8zRdE/pvKIUiJc=          3iVAUEAt8vAb7Ku+jf2gwSkSm0Q=       r4HLEAWJldJwmEmcAqV6Y8rnTPE=    YGh2I3VcukqjT0O7hKItiykWN5tlID18ZXRCwQjXriHmnVsO4wGcHjWfmhuNDecq+xRN+SjG8E7M 2Rx/5/BbFKbVlNOkQOSbSxIs1YT9GaThK16pMrX5KRkkJme1W3R0pGIIQh6gGRSUf79RZUIYxyVl LqdIe561TXXDdtbt/6Q=        XXXXXX XXXXXXX      2002-02-02T14:18:02.0Z bf9433d9-c6e9-4c12-9c98-724008a09c21    Trading Partner X Provider   XXXXXXX Requestor    urn:XXXXXXXXX urn:XXXXXXXX customerInformation e302426a-b2d9-4ff1-a14b-fbbc2f40a017       Bonjour    

Nous avons reçu de la documentation. La section de sécurité est copiée ci-dessous

La sécurité suivante doit être appliquée à chaque demande dans l’ordre suivant: Un wsse: UsernameToken doit être inclus et contient:

  1. Nom d’utilisateur du portail de l’agent pour la valeur de l’élément wsse: Nom d’utilisateur
  2. Mot de passe du portail de l’agent pour la valeur de wsse: PasswordText (texte en clair) dans l’élément Mot de passe. Bloc de signature: Les signatures numériques sont créées à l’aide de certificates x509. Chaque fournisseur de logiciel est tenu de conserver et de protéger un certificate valide et une clé privée délivrés par [À déterminer]. À chaque demande de service, le logiciel doit:

  3. Incluez le certificate qui correspond à la clé privée utilisée pour la signature en tant que wsse: BinarySecurityToken et utilisez-le comme wsse: SecurityTokenReference pour la signature.

  4. Signer le wsse: BinarySecurityToken
  5. Signer le wsse: Nom d’utilisateurToken
  6. Utiliser l’algorithme de la méthode de canonisation: http://www.w3.org/2001/10/xml-exc-c14n#
  7. Utiliser l’algorithme de la méthode de signature: http://www.w3.org/2000/09/xmldsig#rsa-sha1
  8. Utiliser l’algorithme de méthode Digest: http://www.w3.org/2000/09/xmldsig#sha1

Mettre à jour

Image de la configuration SoapUI qui m’a été initialement donnée SoapUIConfig

Enfin réglé le problème aujourd’hui. En termes de terminologie, ce n’est pas le SecurityTokenReference que je dois signer, mais le jeton de sécurité binary.

Pour ce faire, je devais masquer les certificates pour l’initiateur et le destinataire et append un jeton de prise en charge signé.

Je suis revenu à l’utilisation de la configuration pour créer et signer le message, plutôt que d’essayer d’append la signature manuellement.

Un autre problème qui aurait empêché cela de fonctionner est que j’avais un espace de nom incorrect sur mon en-tête ‘Messaging’ personnalisé. Soyez donc conscient des espaces de nom, je ne pensais pas qu’ils seraient aussi importants que ce qu’ils sont.

Le code pour créer la liaison suit

  private System.ServiceModel.Channels.Binding GetCustomBinding() { System.ServiceModel.Channels.AsymmesortingcSecurityBindingElement asbe = new AsymmesortingcSecurityBindingElement(); asbe.MessageSecurityVersion = MessageSecurityVersion.WSSecurity11WSTrust13WSSecureConversation13WSSecurityPolicy12; asbe.InitiatorTokenParameters = new System.ServiceModel.Security.Tokens.X509SecurityTokenParameters { InclusionMode = SecurityTokenInclusionMode.Never }; asbe.RecipientTokenParameters = new System.ServiceModel.Security.Tokens.X509SecurityTokenParameters { InclusionMode = SecurityTokenInclusionMode.Never }; asbe.MessageProtectionOrder = System.ServiceModel.Security.MessageProtectionOrder.SignBeforeEncrypt; asbe.SecurityHeaderLayout = SecurityHeaderLayout.Ssortingct; asbe.EnableUnsecuredResponse = true; asbe.IncludeTimestamp = false; asbe.SetKeyDerivation(false); asbe.DefaultAlgorithmSuite = System.ServiceModel.Security.SecurityAlgorithmSuite.Basic128Rsa15; asbe.EndpointSupportingTokenParameters.Signed.Add(new UserNameSecurityTokenParameters()); asbe.EndpointSupportingTokenParameters.Signed.Add(new X509SecurityTokenParameters()); CustomBinding myBinding = new CustomBinding(); myBinding.Elements.Add(asbe); myBinding.Elements.Add(new TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8)); HttpsTransportBindingElement httpsBindingElement = new HttpsTransportBindingElement(); httpsBindingElement.RequireClientCertificate = true; myBinding.Elements.Add(httpsBindingElement); return myBinding; } 

Lors de l’utilisation de la liaison, je définis les nom d’utilisateur ClientCredentials, ServiceCertificate et ClientCertificate, et tout fonctionne comme prévu.


Utiliser le code est comme suit

 using (CredentialingService.SOAPPortTypeClient client = GetCredentialingClient()) { client.Open(); etc.... } private static CredentialingService.SOAPPortTypeClient GetCredentialingClient() { CredentialingService.SOAPPortTypeClient client = new CredentialingService.SOAPPortTypeClient(GetCustomBinding(), new EndpointAddress(new Uri(Settings.AppSettings.B2BUrl), new DnsEndpointIdentity(Settings.AppSettings.B2BDNSEndpoint), new AddressHeaderCollection())); client.Endpoint.Contract.ProtectionLevel = System.Net.Security.ProtectionLevel.None; SetClientCredentialsSecurity(client.ClientCredentials); return client; } 

où GetCustomBinding est spécifié dans mon message

SetClientCredentialsSecurity est l’endroit où le certificate est défini, et se présente comme suit

 private static void SetClientCredentialsSecurity(ClientCredentials clientCredentials) { clientCredentials.UserName.UserName = Settings.AppSettings.B2BUserName; clientCredentials.UserName.Password = Settings.AppSettings.B2BPassword; ssortingng directoryName = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); clientCredentials.ServiceCertificate.DefaultCertificate = new X509Certificate2(Path.Combine(directoryName, Settings.AppSettings.B2BServerCertificateName)); clientCredentials.ClientCertificate.Certificate = new X509Certificate2(Path.Combine(directoryName, Settings.AppSettings.B2BClientCertificateName), Settings.AppSettings.B2BClientCertificatePassword); } 

Espérons que cela rend un peu plus clair

C’est peut-être parce que votre certificate n’est pas valide publiquement. Essaye ça:

 System.Net.ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(delegate { return true; }); //... using (var client = new SrvClient()) { client.ClientCredentials.UserName.UserName = "usr"; client.ClientCredentials.UserName.Password = "psw"; //... }