通过 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;
});
}
我需要调用带有 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;
});
}