问题描述
我需要调用具有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;
});
}