通过C#

问题描述

我需要调用具有PFX证书的SOAP Web服务。 我正在尝试在.NET 4.7.2控制台应用程序中编写它。 这是我写的。

public async Task<string> CreateSoapEnvelope()
{
    string soapString = @"<?xml version=""1.0"" encoding=""utf-8""?>
  <soapenv:Envelope xmlns:soapenv=""http://schemas.xmlsoap.org/soap/envelope/"" xmlns:b2b=""Some_URL"">
       <soapenv:Header/>
       <soapenv:Body>
          SOAP_BODY
       </soapenv:Body>
    </soapenv:Envelope>";       

    

    HttpResponseMessage response = await PostXmlRequest("CLIENT_URL",soapString);
    string content = await response.Content.ReadAsstringAsync();

    return content;
}

public static async Task<HttpResponseMessage> PostXmlRequest(string baseUrl,string xmlString)
{
    try
    {
        // Create httpclienthandler instance
        var handler = new httpclienthandler();

        // Add the certificate
        var certPath = @"PathToCertificate"; // Path to PFX fle
        var cert = new X509Certificate2(certPath,"Password");
        handler.ClientCertificates.Add(cert);
        using (var httpClient = new HttpClient(handler))
        {
            var httpContent = new StringContent(xmlString,Encoding.UTF8,"text/xml");
            httpContent.Headers.Add("SOAPAction","");

            return await httpClient.PostAsync(baseUrl,httpContent);
        }
    }catch(Exception ex)
    {
    }
    return null;
}

现在,当我调用CreateSoapEnvelope()时,它始终会因InternalServerError出错。

下面是我收到的故障字符串。

<faultstring>No signature in message! (from client). Rejected by filter; SOAP fault sent.Rejected by filter; SOAP fault sent. </faultstring><detail><service_error_message>No signature in message!</service_error_message>

我做错什么了吗?我是SOAP和PFX的新手。任何帮助表示赞赏。

解决方法

正如@bartonjs在评论中提到的那样,问题是该服务希望我的消息被签名。我最终要做的是将签名显式添加到消息中,并且它起作用了。我印象深刻的是,客户证书本身应该很好,但是事实并非如此。我仍在寻找更清洁的解决方案,但目前无法正常工作。

 public static async Task<string> SignXml(XmlDocument document,X509Certificate2 cert)
    {
        return await Task.Run(() =>
        {
            CryptoConfig.AddAlgorithm(typeof(RSAPKCS1SHA256SignatureDescription),"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");

            // Export private key from cert.PrivateKey and import into a PROV_RSA_AES provider:
            var exportedKeyMaterial = cert.PrivateKey.ToXmlString( /* includePrivateParameters = */ true);
            var key = new RSACryptoServiceProvider(new CspParameters(24 /* PROV_RSA_AES */));
            key.PersistKeyInCsp = false;
            key.FromXmlString(exportedKeyMaterial);


            SignedXml signedXml = new SignedXml(document);
            signedXml.SigningKey = key;
            signedXml.SignedInfo.SignatureMethod = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";

            // 
            // Add a signing reference,the uri is empty and so the whole document 
            // is signed. 
            Reference reference = new Reference();
            reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
            reference.AddTransform(new XmlDsigExcC14NTransform());
            reference.Uri = "";
            signedXml.AddReference(reference);

            
            // Add the certificate as key info,because of this the certificate 
            // with the public key will be added in the signature part. 
            KeyInfo keyInfo = new KeyInfo();
            keyInfo.AddClause(new KeyInfoX509Data(cert));
            signedXml.KeyInfo = keyInfo;



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

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

            //get the SOAP-SEC header and add our signature to it
            var soapSecurityList = document.GetElementsByTagName("Header","http://schemas.xmlsoap.org/soap/envelope/");
            if (soapSecurityList.Count == 0)
            {
                throw new Exception("Could not find SOAP-SEC header!");
            }
            var soapSecurity = soapSecurityList.Item(0);

            soapSecurity.AppendChild(xmlDigitalSignature);

            return document.OuterXml;
        });
    }