通过 C# 发送带有 PFX 证书的 SOAP 请求

Send SOAP request with PFX Certificate via 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 在评论之一中提到的,问题是该服务期望我的消息被签名。我最终做的是将 Signature 显式添加到消息中并且它起作用了。我的印象是客户端证书本身应该没问题,但事实并非如此。我仍在寻找更清洁的解决方案,但目前以下工作正常。

 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;
        });
    }