我正在尝试将签名的xml请求发布到某些第三方Java服务。 该请求已由第三方共享的签名证书签名。
第三方Java服务希望传入请求由SHA384算法签名。
我们的请求在Java服务上失败,错误为 digest1值不匹配(即签名验证失败)
下面是XML签名的代码:
public class RsaPkCs1Sha384SignatureDescription : SignatureDescription
{
public RsaPkCs1Sha384SignatureDescription()
{
KeyAlgorithm = typeof(RSACryptoServiceProvider).FullName;
DigestAlgorithm = typeof(SHA384CryptoServiceProvider).FullName;
FormatterAlgorithm = typeof(RSAPKCS1SignatureFormatter).FullName;
DeformatterAlgorithm = typeof(RSAPKCS1SignatureDeformatter).FullName;
}
public override AsymmetricSignatureDeformatter CreateDeformatter(AsymmetricAlgorithm key)
{
var sigProcessor = (AsymmetricSignatureDeformatter)CryptoConfig.CreateFromName(DeformatterAlgorithm);
sigProcessor.SetKey(key);
sigProcessor.SetHashAlgorithm("SHA384");
return sigProcessor;
}
public override AsymmetricSignatureFormatter CreateFormatter(AsymmetricAlgorithm key)
{
var sigProcessor = (AsymmetricSignatureFormatter)CryptoConfig.CreateFromName(FormatterAlgorithm);
sigProcessor.SetKey(key);
sigProcessor.SetHashAlgorithm("SHA384");
return sigProcessor;
}
}
internal class CustomSignedXml
{
// Summary:
// Represents the Uniform Resource Identifier (URI) for the soap envelope description
// transformation. This field is constant.
public const string xmlSoapEnvelopeUrl = "http://schemas.xmlsoap.org/soap/envelope/";
// Summary:
// Represents the Uniform Resource Identifier (URI) for the soap security description
// transformation. This field is constant.
public const string xmlSoapSecurityUrl = "http://schemas.xmlsoap.org/soap/security/2000-12";
// Summary:
// Represents the Uniform Resource Identifier (URI) for the wss utility namespace
// transformation. This field is constant.
public const string xmlOasisWSSSecurityUtilUrl = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd";
// Summary:
// Represents the Uniform Resource Identifier (URI) for the wss extension namespace
// transformation. This field is constant.
public const string xmlOasisWSSSecurityExtUrl = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
// This function expands the overriden GetIdElement method of the
// base SignedXML class to search for attribute "id" in addition to
// the default "Id" attribute.
public const string rsaSHA256Url = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";
public const string rsaSHA384Url = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha384";
public const string SHA256Url = "http://www.w3.org/2001/04/xmlenc#sha256";
public const string SHA384Url = "http://www.w3.org/2001/04/xmldsig-more#sha384";
public const string Base64Binary = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary";
public const string ValueType = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3";
public const string EnvelopeSoap = "http://www.w3.org/2003/05/soap-envelope";
}
internal class SignedXmlWithId : SignedXml
{
public SignedXmlWithId(XmlDocument xml) : base(xml) { }
public SignedXmlWithId(XmlElement xmlElement) : base(xmlElement) { }
public override XmlElement GetIdElement(XmlDocument doc, string id)
{
// check to see if it's a standard ID reference
XmlElement idElem = base.GetIdElement(doc, id);
if (idElem == null)
{
XmlNamespaceManager nsManager = new XmlNamespaceManager(doc.NameTable);
nsManager.AddNamespace("wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");
idElem = doc.SelectSingleNode("//*[@wsu:Id=\"" + id + "\"]", nsManager) as XmlElement;
}
return idElem;
}
}
[Serializable]
internal class SecurityTokenReference : KeyInfoClause
{
private string m_id, referenceAttributeValue;
private XmlDocument m_doc;
private KeyInfoX509Data m_keyX509Data = new KeyInfoX509Data();
public SecurityTokenReference(string id, string referenceAttribute, XmlDocument doc)
{
m_id = id;
m_doc = doc;
referenceAttributeValue = referenceAttribute;
}
public override XmlElement GetXml()
{
XNamespace ca = CustomSignedXml.xmlOasisWSSSecurityExtUrl;
XmlElement element = m_doc.CreateElement("wsse", "SecurityTokenReference", CustomSignedXml.xmlSoapEnvelopeUrl);
XmlAttribute idAttrib = m_doc.CreateAttribute("wsu", "Id", CustomSignedXml.xmlOasisWSSSecurityUtilUrl);
idAttrib.Value = m_id;
element.Attributes.Append(idAttrib);
XmlElement xElement = m_doc.CreateElement("wsse", "Reference", CustomSignedXml.xmlSoapEnvelopeUrl);
element.Prefix = "wsse";
XmlAttribute referenceAttribute = m_doc.CreateAttribute("URI", "");
referenceAttribute.Value = referenceAttributeValue;
XmlAttribute referenceValueType = m_doc.CreateAttribute("ValueType", "");
referenceValueType.Value = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3";
xElement.Attributes.Append(referenceAttribute);
xElement.Attributes.Append(referenceValueType);
element.AppendChild(xElement);
return element;
}
public KeyInfoX509Data KeyX509Data { get { return m_keyX509Data; } }
public override void LoadXml(XmlElement element)
{
m_id = element.GetAttribute("Id", CustomSignedXml.xmlOasisWSSSecurityUtilUrl);
}
}
public class SignedXML
{
private readonly string clientCertificatePassword;
private readonly string certificatePath;
private X509Certificate2 certificate;
private string BodyId = "Id-" + Guid.NewGuid().ToString().Replace("-", "");
private string TimeStampAttribute = "TS-" + Guid.NewGuid().ToString().Replace("-", "");
private string BinarySecurityAttribute = "X509-" + Guid.NewGuid().ToString().Replace("-", "");
private string SignatureId = "SIG-" + Guid.NewGuid().ToString().Replace("-", "");
private string keyInfoId = "KI-" + Guid.NewGuid().ToString().Replace("-", "");
private string SecurityToken = "STR-" + Guid.NewGuid().ToString().Replace("-", "");
public SignedXML()
{
ReadCertificate();
}
private RSA GenerateSigningKey(XmlDocument doc)
{
X509Certificate2 certificate = ReadCertificate();
var key = (RSACryptoServiceProvider)certificate.PrivateKey;
var enhCsp = new RSACryptoServiceProvider().CspKeyContainerInfo;
var cspparams = new CspParameters(enhCsp.ProviderType, enhCsp.ProviderName, key.CspKeyContainerInfo.KeyContainerName);
RSACryptoServiceProvider rsaKey = new RSACryptoServiceProvider(4096, cspparams);
rsaKey.PersistKeyInCsp = false;
return rsaKey;
}
private X509Certificate2 ReadCertificate()
{
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
X509Certificate2Collection collection = (X509Certificate2Collection)store.Certificates;
X509Certificate2Collection fcollection = (X509Certificate2Collection)collection.Find(X509FindType.FindByTimeValid, DateTime.Now, false);
X509Certificate2Collection scollection = X509Certificate2UI.SelectFromCollection(fcollection, "Sign", "Select certificate", X509SelectionFlag.SingleSelection);
if (scollection != null && scollection.Count == 1)
{
certificate = scollection[0];
if (certificate.HasPrivateKey == false)
{
throw new Exception("Certificate has no private key.");
}
}
store.Close();
return certificate;
}
private void MakeSecurityElement(ref XmlDocument xmlDocument)
{
var export = this.certificate.Export(X509ContentType.Cert);
var exportCertificate = Convert.ToBase64String(export);
XmlElement xmlElementSecurity = xmlDocument.CreateElement("wsse", "Security", CustomSignedXml.xmlOasisWSSSecurityExtUrl);
xmlElementSecurity.Attributes.SetNamedItem(CreateAttribute(xmlDocument, "soap", "mustUnderstand", "true", CustomSignedXml.EnvelopeSoap));
xmlElementSecurity.SetAttribute("xmlns:wsse", CustomSignedXml.xmlOasisWSSSecurityExtUrl);
xmlElementSecurity.SetAttribute("xmlns:wsu", CustomSignedXml.xmlOasisWSSSecurityUtilUrl);
XmlElement xmlElementBinarySecurityTokeny = xmlDocument.CreateElement("wsse", "BinarySecurityToken", CustomSignedXml.xmlSoapEnvelopeUrl);
xmlElementBinarySecurityTokeny.InnerText = exportCertificate;
xmlElementBinarySecurityTokeny.Attributes.SetNamedItem(CreateAttribute(xmlDocument, "wsu", "Id", BinarySecurityAttribute, CustomSignedXml.xmlOasisWSSSecurityUtilUrl));
xmlElementBinarySecurityTokeny.Attributes.SetNamedItem(CreateAttribute(xmlDocument, "", "EncodingType", CustomSignedXml.Base64Binary, ""));
xmlElementBinarySecurityTokeny.Attributes.SetNamedItem(CreateAttribute(xmlDocument, "", "ValueType", CustomSignedXml.ValueType, ""));
XmlElement xmlElementTimestamp = xmlDocument.CreateElement("wsu", "Timestamp", CustomSignedXml.xmlOasisWSSSecurityUtilUrl);
xmlElementTimestamp.Attributes.SetNamedItem(CreateAttribute(xmlDocument, "wsu", "Id", TimeStampAttribute, CustomSignedXml.xmlOasisWSSSecurityUtilUrl));
XmlElement xmlElementCreated = xmlDocument.CreateElement("wsu", "Created", CustomSignedXml.xmlOasisWSSSecurityUtilUrl);
xmlElementCreated.InnerText = DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ssZ");
xmlElementTimestamp.AppendChild(xmlElementCreated);
xmlElementSecurity.AppendChild(xmlElementBinarySecurityTokeny);
xmlElementSecurity.AppendChild(xmlElementTimestamp);
xmlDocument.GetElementsByTagName("soap:Header")[0].InsertBefore(xmlElementSecurity, xmlDocument.GetElementsByTagName("soap:Header")[0].FirstChild);
xmlDocument.GetElementsByTagName("soap:Body").Item(0).Attributes.SetNamedItem(CreateAttribute(xmlDocument, "wsu", "Id", BodyId, CustomSignedXml.xmlOasisWSSSecurityUtilUrl));
xmlDocument.PreserveWhitespace = true;
}
public XmlDocument SignXMLHeader(XmlDocument xmlDocument)
{
CryptoConfig.AddAlgorithm(typeof(RsaPkCs1Sha384SignatureDescription), "http://www.w3.org/2001/04/xmldsig-more#rsa-sha384");
MakeSecurityElement(ref xmlDocument);
BinarySecurityAttribute = "#" + BinarySecurityAttribute;
TimeStampAttribute = "#" + TimeStampAttribute;
BodyId = "#" + BodyId;
var signingKey = GenerateSigningKey(xmlDocument);
SignedXmlWithId signedXml = new SignedXmlWithId(xmlDocument);
signedXml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl;
XmlDsigExcC14NTransform canMethod = (XmlDsigExcC14NTransform)signedXml.SignedInfo.CanonicalizationMethodObject;
canMethod.InclusiveNamespacesPrefixList = "soap";
signedXml.SigningKey = signingKey;
signedXml.Signature.Id = SignatureId;
signedXml.SignedInfo.SignatureMethod = CustomSignedXml.rsaSHA384Url;
SecurityTokenReference tokenRef = new SecurityTokenReference(SecurityToken, BinarySecurityAttribute, xmlDocument);
tokenRef.KeyX509Data.AddSubjectKeyId(Guid.NewGuid().ToString().Replace("-", ""));
tokenRef.KeyX509Data.AddIssuerSerial(certificate.Issuer, certificate.GetSerialNumberString());
Reference referenceBody = new Reference(BodyId);
Reference referenceTimeStamp = new Reference(TimeStampAttribute);
Reference referenceBinarySecurity = new Reference(BinarySecurityAttribute);
XmlDsigExcC14NTransform reference1TransForm = new XmlDsigExcC14NTransform();
reference1TransForm.InclusiveNamespacesPrefixList = "";
XmlDsigExcC14NTransform reference2TransForm = new XmlDsigExcC14NTransform();
reference2TransForm.InclusiveNamespacesPrefixList = "wsse soap";
XmlDsigExcC14NTransform reference3TransForm = new XmlDsigExcC14NTransform();
reference3TransForm.InclusiveNamespacesPrefixList = "soap";
referenceBody.AddTransform(reference1TransForm);
referenceTimeStamp.AddTransform(reference2TransForm);
referenceBinarySecurity.AddTransform(reference3TransForm);
referenceBody.DigestMethod = CustomSignedXml.SHA384Url;
referenceTimeStamp.DigestMethod = CustomSignedXml.SHA384Url;
referenceBinarySecurity.DigestMethod = CustomSignedXml.SHA384Url;
signedXml.AddReference(referenceBody);
signedXml.AddReference(referenceTimeStamp);
signedXml.AddReference(referenceBinarySecurity);
KeyInfo keyInfo = new KeyInfo();
keyInfo.Id = keyInfoId;
keyInfo.AddClause(tokenRef);
signedXml.KeyInfo = keyInfo;
signedXml.ComputeSignature();
XmlElement xmlDigitalSignature = signedXml.GetXml();
foreach (XmlNode node in xmlDigitalSignature.SelectNodes("descendant-or-self::*[namespace-uri()='http://www.w3.org/2000/09/xmldsig#']"))
node.Prefix = "ds";
foreach (XmlNode node in xmlDigitalSignature.SelectNodes("descendant-or-self::*[namespace-uri()='http://www.w3.org/2001/10/xml-exc-c14n#']"))
node.Prefix = "ec";
signedXml.LoadXml(xmlDigitalSignature);
signedXml.SignedInfo.References.Clear();
signedXml.ComputeSignature();
XmlNodeList xmlNodeList = xmlDocument.GetElementsByTagName("wsse:Security");
XmlNode xmlNode = xmlNodeList[0];
xmlNode.InsertBefore(xmlDigitalSignature, xmlNode.FirstChild);
VerifyDetachedSignature(xmlDocument, signingKey);
return xmlDocument;
}
private XmlAttribute CreateAttribute(XmlDocument xmlDocument, string prefix, string attributeName, string attributeValue, string namespaceURI)
{
XmlAttribute xmlAttribute = xmlDocument.CreateAttribute(prefix, attributeName, namespaceURI);
xmlAttribute.Value = attributeValue;
return xmlAttribute;
}
// Verify the signature of an XML file and return the result.
public static Boolean VerifyDetachedSignature(XmlDocument xmlDocument, RSA Key)
{
// Create a new SignedXml object and pass it
// the XML document class.
SignedXml signedXml = new SignedXml();
// Find the "Signature" node and create a new
// XmlNodeList object.
XmlNodeList nodeList = xmlDocument.GetElementsByTagName("ds:Signature");
// Load the signature node.
signedXml.LoadXml((XmlElement)nodeList[0]);
// Check the signature and return the result.
var result = signedXml.CheckSignature(Key);
return result;
}
}
public class ConsumeProxy
{
readonly string ServicePath;
readonly X509Certificate2 x509Certificate;
public ConsumeProxy(X509Certificate2 certificate)
{
ServicePath = ManageConfigurationItem.ServicePath;
x509Certificate = certificate;
}
public string ConsumeProxy(string Xml)
{
XmlDocument soapEnvelopeXml = new XmlDocument();
soapEnvelopeXml.LoadXml(Xml);
HttpWebRequest webRequest = CreateWebRequest();
webRequest.UserAgent = "Client Cert Sample";
//ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return true; });
using (Stream stream = webRequest.GetRequestStream())
{
soapEnvelopeXml.Save(stream);
using (WebResponse response = webRequest.GetResponse())
{
using (StreamReader rd = new StreamReader(response.GetResponseStream()))
{
return rd.ReadToEnd();
}
}
}
}
private HttpWebRequest CreateWebRequest()
{
Uri myUri = new Uri(ServicePath, UriKind.Absolute);
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(myUri);
webRequest.ContentType = "application/soap+xml; charset=\"utf-8\"";
webRequest.Method = "POST";
webRequest.Proxy.Credentials = System.Net.CredentialCache.DefaultCredentials;
webRequest.ClientCertificates.Add(x509Certificate);
return webRequest;
}
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void SHA384_Click(object sender, EventArgs e)
{
SignedXML();
}
public void SignedXML()
{
string path = Environment.CurrentDirectory + "/test1.xml";
string xml = File.ReadAllText(path);
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.LoadXml(xml);
SignedXML signedXml = new SignedXML();
XmlDocument signedXmlDocument = signedXml.SignXMLHeader(xmlDocument);
string testXML = signedXmlDocument.OuterXml;
}
}