SHA 384算法签名的XML标头

时间:2018-07-12 12:20:01

标签: c#

我正在尝试将签名的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;
    }
 }

0 个答案:

没有答案