Click here to Skip to main content
15,892,161 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I am trying to post a signed xml request to some third party java service. The request has been signed by a signing certificate shared by third party.
The third party java service expects the incoming request to be signed by SHA384 algorithms.
Our request is failing at the java service with the error digest1 value does not match (i.e. signature verification is getting failed)

Below is the XML signed code:

What I have tried:

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

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900