C# 使用X.509证书将SOAP消息从WCF服务签名到Java webservice

C# 使用X.509证书将SOAP消息从WCF服务签名到Java webservice,c#,wcf,web-services,wcf-security,x509certificate,C#,Wcf,Web Services,Wcf Security,X509certificate,这是我在网上的第一个问题。希望这会有意义 我在网上看到过几个与这个问题相关的博客,我尝试了其中的一些想法,但都没有成功。我的情况如下: 我有一个web应用程序调用WCF web服务,然后调用Java web服务。它们都在不同的服务器上。WCF web服务与java web服务之间的调用不是通过https进行的,因为证书将足以识别调用方(因此消息安全性) Java web服务(黑盒) Java web服务需要接收签名消息,并按照以下方式工作: 在处理每个请求之前,处理程序会截获所有传入消息并执

这是我在网上的第一个问题。希望这会有意义

我在网上看到过几个与这个问题相关的博客,我尝试了其中的一些想法,但都没有成功。我的情况如下:

我有一个web应用程序调用WCF web服务,然后调用Java web服务。它们都在不同的服务器上。WCF web服务与java web服务之间的调用不是通过https进行的,因为证书将足以识别调用方(因此消息安全性)

  • Java web服务(黑盒)
Java web服务需要接收签名消息,并按照以下方式工作:
在处理每个请求之前,处理程序会截获所有传入消息并执行以下验证规则:
1.消息是否包含安全标头
2.消息是否包含正确的安全标头ID
3.消息是否已正确签名
4.消息是否包含KeyInfo x.509证书
5.证书是否由受信任的CA颁发-基于配置
6.证书是否有效(未过期、已吊销)
7.证书是否包含正确的策略OID

一旦确认了所有这些步骤,就可以处理消息,如果任何步骤失败,就会返回soap消息异常

SOAP安全标头应根据xxx…w3.org/TR/SOAP-dsig/数字签名规范进行验证

最完整的描述可以在这里找到xxx…ibm.com/developerworks/webservices/library/ws-security.html这篇ibm文章列出了每个ws-security头的详细信息,另外还提供了一个签名SOAP消息示例

在对SOAP消息进行签名时,还必须将x.509证书添加到消息KeyInfo中,这是证书验证所必需的

SOAP请求应该是这样的:

<?xml version="1.0" encoding="UTF-8"?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Header>
<ds:Signature xmlns:ds="xxx...w3.org/2000/09/xmldsig#" Id="Signature001">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="xxx...w3.org/TR/2001/REC-xml-c14n-20010315"/>
<ds:SignatureMethod Algorithm="xxx...w3.org/2000/09/xmldsig#rsa-sha1"/>
<ds:Reference URI="">
<ds:Transforms>
<ds:Transform Algorithm="xxx...w3.org/2000/09/xmldsig#enveloped-signature"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="xxx...w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>soe1PnaGXVGrsauC61JSHD+uqGw=</ds:DigestValue>
</ds:Reference>
<ds:Reference URI="#KeyInfo001">
<ds:DigestMethod Algorithm="xxx...w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>Y9SRPQ9TcDu+GazO3LFwodEdhaA=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>jBX/8XkY2aCte7qgXEp1sbNWmQcK/90iVL58sAvwYAEcBABGzOk2agxR0HvWrNa6ixkocAQ205lggwOxnxZJvoVozVYAAjcLtayPBOUYrnSEBFrwKWP/vxgvUDRIdXeIuw5GLY87NrTQMm1Ehf/HvMX9hTBJn4Nm8RdDiUmPcIo=</ds:SignatureValue>
<ds:KeyInfo Id="KeyInfo001">
<ds:X509Data>
<ds:X509Certificate>MIIEbZCCA1WgAwIBAgIES1XpMjANBgkqhkiG9w0BAQUFADBYMRUwEwYKCZImiZPyLGQBGRYFbG9jYWwxFzAVBgoJkiaJk/IsZAEZFgdlbnRydXN0MRIwEAYDVQQDEwllbnRydXN0U00xEjAQBgNVBAMTCWVudHJ1c3RDQTAeFw0xMDA0MjIxMDQ4MDBaFw0xMzA0MjIxMTE4MDBaMGoxFTATBgoJkiaJk/IsZAEZFgVsb2NhbDEXMBUGCgmSJomT8ixkARkWB2VudHJ1c3QxEjAQBgNVBAMTCWVudHJ1c3RTTTESMBAGA1UEAxMJZW50cnVzdENBMRAwDgYDVQQDEwdSYnMgUmJzMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMf88L2JjLPG1hNmTA/KBiC53WVwS2WU9Jh3lC1Rob6RMzOojomZ/dNrvSRB6nzWeXJpZXwik4XFrsAq24By2SZpLTO4p8Vcq71mTAfDu33cnO49Au2pwNvcMn5qIKBk1Xx+oVb4fzK9ncTRu7bW46HsIYth+qkGhbI2JEHwr/zwIDAQABo4IBrzCCAaswCwYDVR0PBAQDAgeAMCsGA1UdEAQkMCKADzIwMTAwNDIyMTA0ODAwWoEPMjAxMjA1MjgxNTE4MDBaMCMGA1UdIAQcMBowCwYJYIZIAYb6awoEMAsGCSqGSIb2fQdLAzAbBgNVHQkEFDASMBAGCSqGSIb2fQdEHTEDAgEBMIHGBgNVHR8Egb4wgbswb6BtoGukaTBnMRUwEwYKCZImiZPyLGQBGRYFbG9jYWwxFzAVBgoJkiaJk/IsZAEZFgdlbnRydXN0MRIwEAYDVQQDEwllbnRydXN0U00xEjAQBgNVBAMTCWVudHJ1c3RDQTENMAsGA1UEAxMEQ1JMMTBIoEagRIZCZmlsZTovLy8vTVNJREhVLTQ0NUE0RkVFL0NSTC9lbnRydXN0Y2FfZW50cnVzdHNtX2xvY2FsX2NybGZpbGUuY3JsMB8GA1UdIwQYMBaAFBvSL6cPz8L5shubV58yf0pczKzuMB0GA1UdDgQWBBT1/j6OSS8FTjwqluvew16sv7h+VzAJBgNVHRMEAjAAMBkGCSqGSIb2fQdBAAQMMAobBFY4LjADAgSwMA0GCSqGSIb3DQEBBQUAA4IBAQBXxRIA4HUvGSw4L+4uaR51pY4ISjUQWo2Fh7FYBMt29NsKCTdur1OWVVdndt1yjXP4yWXxoAhHtvZL+XNALUFlR2HAWiXuL1nRcxHkB98N5gPqQzW/lJk9cLtL4hVp28EiEpgmKT3I3NP2Pdb2G5MMOdvQ/GFb2y6OwblR8ViPQ8B2aHWzXMrH+0qadPAuBhXyAohwb+mMuYT/ms6xpGi1NMYuYMf6XONz9GkZgnGnMwa+9CCQws1HNz8WYHtmFIxLsVuEWc/0a1vg4IYX1Ds/ttyhJGTVXOSJSkBz8kRyj1pNBDdc1KeG8M++O8m8VgRTJvYaPc7NMiclISukGpea</ds:X509Certificate> </ds:X509Data>
</ds:KeyInfo>
</ds:Signature>
</S:Header>
<S:Body Id="ABC">
<ns2:createUser xmlns:ns2="http://webservice.rbs.emea.ps.entrust.com/" xmlns:ns3="http://webservice.rbs.emea.ps.entrust.com/types/CertificateException" xmlns:ns4="http://webservice.rbs.emea.ps.entrust.com/types/UserException">
<userID>0061020051</userID>
</ns2:createUser>
</S:Body>
</S:Envelope>

soe1PnaGXVGrsauC61JSHD+uqGw=
Y9SRPQ9TcDu+GazO3LFwodEdhaA=
jBX/8XkY2aCte7qgXEp1sbNWmQcK/90IVL58SAVYAECBABGZOK2AGXR0HVWRNA6IXKOCAQ205LGGWOxNxZJVOVOZVYAJCLTAYPBOUYRNSEBFRWKWP/VxGVUdridXEUW5GLY87NRTQMM1EHF/HVMX9HTBJN4NM8RDDUMPCIO=
Miiebzcca1WgAwgIbg1WgUwwykCzimizPyGbGfBg9JyWxZavGojkiajk/IszaezfgdlBnRyDxN0mriyWyDvQdWn0U00XeJaqbGnWbGnVuDj1RdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQdQMJZMIGFMA0GCSQGSIB3DQEBAQA4NADCBIKBGQDMF88L2JJJLPG1HNMTA/KBIC53WVWS2WU9JH3LC1ROB6ZOOJOMZ/DNRVSRB6NZWEXJPZWIK4FRSAQ24B2ZPL4P8VCQ71MTAFDU33CNO49AU2WNCMN5QIBKK1XX+OVB4FZK9NCTRU7W46HYSYYTH+qkGhbI2JEHwr/ZWIDAQWIDAKZWIDZWIKWIKWIKWIKWIKZWIKWIKZWIKZWIK4FR2WZWIKZWIK4FRZWIK4FRZWIKZWIKZWIK4M7WZWIKZWIKZWIKZWIKZWIK4B4B7WZWIK4B7WZWIKZWIK4B4B4B2FQDlazabbgnvhqkefdasmbcSqgSib2FqdEgB4WgbSbB6TogukatBNMrUwewykzimizPyGbGfBgRyFbG9JYWxFbGfGfGfGfGfGfGfGfGfGfGfGfGfGfJjWxBfGfGfGfBfGfGfGfGfGfGfGfGfGfGfGfGfGfGfGfGfRyJjk/IszabGfGfGfGfGfGfGfGfGbNfGbNfGfGbNfGfGfGbNnRyDfGnRyDfGnRyDfGnRyDfGnRyDfGnRyDfGnRyDfGnRyDfGnRyWn2.这两个词的意思是:一个词的意思是,一个词的意思是,一个词的意思是,一个词的意思是,一个词的意思是,一个词的意思是,一个词的意思是,一个词的意思是,一个词的意思是,一个词的意思是,一个词的意思是,一个词的意思是,一个词的意思是,一个词的意思是,一个词的意思是,一个2。一个词的意思是一个研究一个研究。一个研究一个关于一个关于一个关于一个关于一个关于一个关于一个关于一个关于一个关于一个关于一个关于一个关于一个关于一个关于一个关于一个关于一个关于一个关于一个关于一个关于一个关于一个关于一个关于一个关于一个关于一个关于一个关于一个关于一个关于一个关于一个关于一个关于一个关于一个关于一个关于一B+mMuYT/ms6xpGi1NMYuYMf6XONZ9GKZNGNMWA+9CCQWS1HNZ8WYHTMLSVUEWC/0a1vg4IYX1Ds/ttyhJGTVXOSJSkBz8kRyj1pNBDdc1KeG8M++O8m8VgRTJvYaPc7NMiclISukGpea
0061020051
  • WCF网络服务
我有一个服务器证书(来自可信CA的p7b格式),我使用mmc证书管理单元(目前证书位于可信发布服务器中)将其安装在WCF web服务工作站(dev)所在的位置。我不认为我需要Java服务器上的另一个证书,因为响应应该是清晰的(既没有签名也没有加密)。我对这个证书(以及一般的证书)仍然有点困惑,因为它似乎只持有一个公钥

以下是我的测试项目的app.config:


当我运行一个简单的测试时:
WebAS委托服务=新的WebAS()
ActivationCodes certCodes=TruditService.createUser(“testNomad”)
我有一个错误:
失败:System.Web.Services.Protocols.SoapException: javax.xml.soap.SOAPException:在soap消息中未找到签名元素


我如何强制每个邮件的签名过程?我想我可以通过WCF配置很容易地做到这一点。任何帮助都将不胜感激

您能否捕获WCF服务发送的消息?顺便说一句,WSDL中描述的Java服务使用的是消息安全性吗?这将使事情变得更容易

根据您的描述,我认为您的配置是错误的,因为当使用证书客户端凭据时,您需要两个证书—具有公钥和私钥的客户端证书和具有公钥的服务器证书

您的需求中可能也描述了这一点:

消息是否包含KeyInfo x.509证书

证书是否由受信任的CA颁发?是否基于配置

为什么需要发回已安装在该服务器上的服务证书?为什么服务要检查其证书是否来自受信任的CA?我猜这些要求说明您必须为您的客户创建新的证书

但这些只是假设,因为实际需求通常用共享语言WSDL+WS-Security断言来描述

控制签名和加密在多个服务器上都是可能的
<client>
  <endpoint address="http://entrust-user-certification-uat.fm.rbsgrp.net/rbs/WebAS"
    behaviorConfiguration="endpointCredentialsBehavior" binding="wsHttpBinding"
    bindingConfiguration="WebAsServicePortTypeBinding" contract="IWebAsServicePortType"
    name="WebAsServicePortType">
    <!--<identity>
      <dns value="entrust-user-certification-uat.fm.rbsgrp.net" />
    </identity>-->
  </endpoint>
</client>
<bindings>
  <wsHttpBinding>
    <binding name="WebAsServicePortTypeBinding" closeTimeout="00:01:00"
      openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
      bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
      maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text"
      textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false">
      <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
        maxBytesPerRead="4096" maxNameTableCharCount="16384" />
      <security mode="Message">
        <message clientCredentialType="Certificate" negotiateServiceCredential="false"
          establishSecurityContext="false" />
      </security>
    </binding>
  </wsHttpBinding>
</bindings>

<behaviors>
  <endpointBehaviors>
    <behavior name="endpointCredentialsBehavior">
      <clientCredentials>
        <clientCertificate findValue="entrust-user-certification-uat.fm.rbsgrp.net"
           storeLocation="LocalMachine" storeName="TrustedPublisher"
          x509FindType="FindBySubjectName"></clientCertificate>
        <serviceCertificate>
          <!--   
          Setting the certificateValidationMode to PeerOrChainTrust means that if the certificate 
          is in the user's Trusted People store, then it will be trusted without performing a
          validation of the certificate's issuer chain. This setting is used here for convenience so that the 
          sample can be run without having to have certificates issued by a certificate authority (CA).
          This setting is less secure than the default, ChainTrust. The security implications of this 
          setting should be carefully considered before using PeerOrChainTrust in production code. 
          -->
          <authentication certificateValidationMode="None" revocationMode="NoCheck" trustedStoreLocation="LocalMachine"/>
        </serviceCertificate>
      </clientCredentials>
    </behavior>
  </endpointBehaviors>
</behaviors>
// Sign an XML request and return it
public static string SignRequest(string request, string SubjectName, string Signature, string keyInfoRefId)
{
    if (string.IsNullOrEmpty(request))
        throw new ArgumentNullException("request");
    if (string.IsNullOrEmpty(SubjectName))
        throw new ArgumentNullException("SubjectName");

    // Load the certificate from the certificate store.
    X509Certificate2 cert = GetCertificateBySubject(SubjectName);

    // Create a new XML document.
    XmlDocument doc = new XmlDocument();

    // Format the document to ignore white spaces.
    doc.PreserveWhitespace = false;

    // Load the passed XML 
    doc.LoadXml(request);

    // Add the declaration as per Entrust sample provided -don't think it's necessary though
    if (!(doc.FirstChild is XmlDeclaration))
    {
        XmlDeclaration declaration = doc.CreateXmlDeclaration("1.0", "UTF-8", string.Empty);
        doc.InsertBefore(declaration, doc.FirstChild);
    }

    // Remove the Action (MustUnderstand). 
    // TODO: Need to find a more elegant way to do so
    XmlNode headerNode = null;
    XmlNodeList nodeList = doc.GetElementsByTagName("Action");
    if (nodeList.Count > 0)
    {
        headerNode = nodeList[0].ParentNode;
        headerNode.RemoveChild(nodeList[0]);
    }

    // Set the body id - not in used but could be useful at a later stage of this project
    XmlNamespaceManager ns = new XmlNamespaceManager(doc.NameTable);    
    ns.AddNamespace("s", "http://schemas.xmlsoap.org/soap/envelope/");
    XmlElement body = doc.DocumentElement.SelectSingleNode(@"//s:Body", ns) as XmlElement;    
    if (body == null)    
        throw new ApplicationException("No body tag found");
    body.RemoveAllAttributes();  // no need to have namespace
    body.SetAttribute("Id", "ABC"); // Body Id could be passed as a param

    // Create a custom SignedXml object so that we could sign the keyinfo
    CustomSignedXml signedXml = new CustomSignedXml(doc);

    // Add the key to the SignedXml document. 
    signedXml.SigningKey = cert.PrivateKey;

    // Create a new KeyInfo object.
    KeyInfo keyInfo = new KeyInfo();
    keyInfo.Id = keyInfoRefId;

    // Load the certificate into a KeyInfoX509Data object
    // and add it to the KeyInfo object.
    KeyInfoX509Data keyInfoData = new KeyInfoX509Data();
    keyInfoData.AddCertificate(cert);
    keyInfo.AddClause(keyInfoData);

    // Add the KeyInfo object to the SignedXml object.
    signedXml.KeyInfo = keyInfo;

    // Create a reference to be signed.
    Reference reference = new Reference();
    reference.Uri = "";

    // Add an enveloped transformation to the reference.
    XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform();
    reference.AddTransform(env);

    // Add the reference to the SignedXml object.
    signedXml.AddReference(reference);

    Reference reference2 = new Reference();
    reference2.Uri = "#" + keyInfoRefId;
    signedXml.AddReference(reference2);

    // Add the Signature Id
    signedXml.Signature.Id = Signature;

    // Compute the signature.
    signedXml.ComputeSignature();

    // Get the XML representation of the signature and save
    // it to an XmlElement object.
    XmlElement xmlDigitalSignature = signedXml.GetXml();

    // Append the Signature element to the XML document.
    if (headerNode != null)
    {                
        headerNode.AppendChild(doc.ImportNode(xmlDigitalSignature, true));
    }

    return doc.InnerXml;
}

public static X509Certificate2 GetCertificateBySubject(string CertificateSubject)
{
    // Check the args.
    if (string.IsNullOrEmpty(CertificateSubject))
        throw new ArgumentNullException("CertificateSubject");

    // Load the certificate from the certificate store.
    X509Certificate2 cert = null;

    X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);

    try
    {
        // Open the store.
        store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);

        // Find the certificate with the specified subject.
        cert = store.Certificates.Find(X509FindType.FindBySubjectName, CertificateSubject, false)[0];

        // Throw an exception of the certificate was not found.
        if (cert == null)
        {
            throw new CryptographicException("The certificate could not be found.");
        }
    }
    finally
    {
        // Close the store even if an exception was thrown.
        store.Close();
    }

    return cert;
}
public class CustomSignedXml : SignedXml
{
    public CustomSignedXml(XmlDocument doc) : base(doc)
    {
        return;
    }
    public override XmlElement GetIdElement(XmlDocument doc, string id)
    {
        // see if this is the key info being referenced, otherwise fall back to default behavior
        if (String.Compare(id, this.KeyInfo.Id, StringComparison.OrdinalIgnoreCase) == 0)
            return this.KeyInfo.GetXml();
        else
            return base.GetIdElement(doc, id);
    }
}