【问题标题】:How to generate .sig of XML of digitally signed XML document in C#?如何在 C# 中生成数字签名 XML 文档的 XML 的 .sig?
【发布时间】:2017-07-01 04:57:51
【问题描述】:

我需要使用数字签名对 xml 文档进行签名,并且我需要使用该文档生成数字签名 xml 的 .sig 文件。我正在使用 PKCS7 算法。我能够成功地将签名放入 xml。但无法生成 .sig 文件。我的代码如下:

public static void SignXmlDocumentWithCertificate(XmlDocument doc, X509Certificate2 cert)
{
    SignedXml signedxml = new SignedXml(doc);
    signedxml.SigningKey = cert.PrivateKey;
    Reference reference = new Reference();
    reference.Uri = "";
    reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
    signedxml.AddReference(reference);

    KeyInfo keyinfo = new KeyInfo();
    keyinfo.AddClause(new KeyInfoX509Data(cert));

    signedxml.KeyInfo = keyinfo;
    signedxml.ComputeSignature();

    XmlElement xmlsig = signedxml.GetXml();
    doc.DocumentElement.AppendChild(doc.ImportNode(xmlsig, true));
    //Console.WriteLine(doc.ImportNode(xmlsig,true));
}

现在我正在生成这样的 .sig 文件:

AsymmetricKeyParameter asymmetricKeyParameter = PublicKeyFactory.CreateKey(keyBytes);
RsaKeyParameters rsaKeyParameters = (RsaKeyParameters)asymmetricKeyParameter;
RSAParameters rsaParameters = new RSAParameters();
rsaParameters.Modulus = rsaKeyParameters.Modulus.ToByteArrayUnsigned();
rsaParameters.Exponent = rsaKeyParameters.Exponent.ToByteArrayUnsigned();

RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); rsa.ImportParameters(rsaParameters);

byte[] ciphertext = rsa.Encrypt(keyBytes, false);
string cipherresult = Convert.ToBase64String(ciphertext);
Console.WriteLine(cipherresult);

这会引发错误长度的错误\r\n。 数字签名后我的 xml 是:

<?xml version="1.0" encoding="UTF-8"?>
<xml>
<CATALOG>
 <PLANT>
    <COMMON>Grecian Windflower</COMMON>
    <BOTANICAL>Anemone blanda</BOTANICAL>
    <ZONE>6</ZONE>
    <LIGHT>Mostly Shady</LIGHT>
    <PRICE>$9.16</PRICE>
    <AVAILABILITY>071099</AVAILABILITY>
</PLANT>
</CATALOG>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
    <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
    <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
    <Reference URI="">
      <Transforms>
        <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
     </Transforms>
     <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>

     <DigestValue>/VUzr4wRNv2e6SzE6TdHLM8c+/A=</DigestValue>

    </Reference>

    </SignedInfo>
    <SignatureValue>i3gGf2Q......8Q==</SignatureValue>
    <KeyInfo>
    <X509Data>
      <X509Certificate>MIID6D.......fFo=</X509Certificate>
    </X509Data>
    </KeyInfo>

</Signature>

</xml>

现在我知道我要么做错了,要么我错过了一些东西。我的问题是

  1. 有没有办法用签名的 xml 生成 .sig 文件?
  2. PKCS7 中是否可以使用大型 xml 文件?

因为我的要求是:

  1. 数字签名将作为 PKCS7 信封的一部分生成为纯字节。 PKCS7 信封将包含用于签名的证书以及数字签名本身。
  2. PKCS7 信封将不是 base-64 编码。它不包含任何开始和结束标识符。普通的 PKCS7 信封是一个字节序列,将被写入 .sig 文件。
  3. 数字签名将使用 SHA-2(512 位)算法生成消息摘要和 RSA-2048 算法进行加密

【问题讨论】:

    标签: c# xml digital-signature pkcs#7 xml-signature


    【解决方案1】:

    试试下面的代码。我将您的代码与来自 msdn 的示例合并。我还在 PC 上使用了默认用户证书:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Xml;
    using System.Security;
    using System.Security.Cryptography;
    using System.Security.Cryptography.X509Certificates;
    using System.Security.Cryptography.Xml;
    using System.IO;
    
    namespace ConsoleApplication1
    {
        class Program
        {
            const string FILENAME = @"c:\temp\test.xml";
            static void Main(string[] args)
            {
                XmlDocument doc = new XmlDocument();
                doc.Load(FILENAME);
                string computerName = Environment.GetEnvironmentVariable("COMPUTERNAME");
                string userName = Environment.GetEnvironmentVariable("USERNAME");
                X509Certificate2 cert = GetCertificateFromStore("CN=" + computerName + "\\" + userName);
    
                SignXmlDocumentWithCertificate(doc, cert);
                RSACryptoServiceProvider publicKey = (RSACryptoServiceProvider)cert.PublicKey.Key;
    
                byte[] unencryptedData = Encoding.UTF8.GetBytes(doc.OuterXml); 
                Stream stream = EncryptFile(unencryptedData,publicKey);
    
                Console.ReadLine();
    
            }
            public static void SignXmlDocumentWithCertificate(XmlDocument doc, X509Certificate2 cert)
            {
                SignedXml signedxml = new SignedXml(doc);
                signedxml.SigningKey = cert.PrivateKey;
                Reference reference = new Reference();
                reference.Uri = "";
                reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
                signedxml.AddReference(reference);
    
                KeyInfo keyinfo = new KeyInfo();
                keyinfo.AddClause(new KeyInfoX509Data(cert));
    
                signedxml.KeyInfo = keyinfo;
                signedxml.ComputeSignature();
    
                XmlElement xmlsig = signedxml.GetXml();
                doc.DocumentElement.AppendChild(doc.ImportNode(xmlsig, true));
                //Console.WriteLine(doc.ImportNode(xmlsig,true));
    
            }
    
            private static X509Certificate2 GetCertificateFromStore(string certName)
            {
    
                // Get the certificate store for the current user.
                X509Store store = new X509Store(StoreLocation.CurrentUser);
                try
                {
                    store.Open(OpenFlags.ReadOnly);
    
                    // Place all certificates in an X509Certificate2Collection object.
                    X509Certificate2Collection certCollection = store.Certificates;
                    // If using a certificate with a trusted root you do not need to FindByTimeValid, instead:
                    // currentCerts.Find(X509FindType.FindBySubjectDistinguishedName, certName, true);
                    X509Certificate2Collection currentCerts = certCollection.Find(X509FindType.FindByTimeValid, DateTime.Now, false);
                    X509Certificate2Collection signingCert = currentCerts.Find(X509FindType.FindBySubjectDistinguishedName, certName, false);
                    if (signingCert.Count == 0)
                        return null;
                    // Return the first certificate in the collection, has the right name and is current.
                    return signingCert[0];
                }
                finally
                {
                    store.Close();
                }
    
            }
            // Encrypt a file using a public key.
            private static MemoryStream  EncryptFile(byte[] unencryptedData, RSACryptoServiceProvider rsaPublicKey)
            {
                MemoryStream stream = null;
    
                using (AesManaged aesManaged = new AesManaged())
                {
                    // Create instance of AesManaged for
                    // symetric encryption of the data.
                    aesManaged.KeySize = 256;
                    aesManaged.BlockSize = 128;
                    aesManaged.Mode = CipherMode.CBC;
                    using (ICryptoTransform transform = aesManaged.CreateEncryptor())
                    {
                        RSAPKCS1KeyExchangeFormatter keyFormatter = new RSAPKCS1KeyExchangeFormatter(rsaPublicKey);
                        byte[] keyEncrypted = keyFormatter.CreateKeyExchange(aesManaged.Key, aesManaged.GetType());
    
                        // Create byte arrays to contain
                        // the length values of the key and IV.
                        byte[] LenK = new byte[4];
                        byte[] LenIV = new byte[4];
    
                        int lKey = keyEncrypted.Length;
                        LenK = BitConverter.GetBytes(lKey);
                        int lIV = aesManaged.IV.Length;
                        LenIV = BitConverter.GetBytes(lIV);
    
                        // Write the following to the FileStream
                        // for the encrypted file (outFs):
                        // - length of the key
                        // - length of the IV
                        // - ecrypted key
                        // - the IV
                        // - the encrypted cipher content
    
    
                        stream = new MemoryStream();
                        try
                        {
    
                            stream.Write(LenK, 0, 4);
                            stream.Write(LenIV, 0, 4);
                            stream.Write(keyEncrypted, 0, lKey);
                            stream.Write(aesManaged.IV, 0, lIV);
    
                            // Now write the cipher text using
                            // a CryptoStream for encrypting.
                            CryptoStream outStreamEncrypted = new CryptoStream(stream, transform, CryptoStreamMode.Write);
                            try
                            {
    
                                // By encrypting a chunk at
                                // a time, you can save memory
                                // and accommodate large files.
                                int count = 0;
                                int offset = 0;
    
                                // blockSizeBytes can be any arbitrary size.
                                int blockSizeBytes = aesManaged.BlockSize / 8;
    
                                do
                                {
                                    if (offset + blockSizeBytes <= unencryptedData.Length)
                                    {
                                        count = blockSizeBytes;
                                    }
                                    else
                                    {
                                        count = unencryptedData.Length - offset;
                                    }
                                    outStreamEncrypted.Write(unencryptedData, offset, count);
                                    offset += count;
                                }
                                while (offset < unencryptedData.Length);
    
                                outStreamEncrypted.FlushFinalBlock();
                            }
                            catch(Exception ex)
                            {
                                Console.WriteLine("Error : {0}", ex.Message);
                            }
                        }
                        catch(Exception ex)
                        {
                            Console.WriteLine("Error : {0}", ex.Message);
                        }
                        stream.Position = 0;
                    }
                }
                return stream;
            }
        }
    }
    

    【讨论】:

    • 如何将这个加密的 xml 转为 .sig 文件?或者用 .sig 扩展名编写流就可以了?
    • 代码加密所有东西并放入内存流所以你可以使用 FileStream fStream = new FileStream("filename", FileMode.Create); memoryStream.WriteTo(fStream);
    猜你喜欢
    • 2020-04-26
    • 1970-01-01
    • 1970-01-01
    • 2018-11-30
    • 2020-05-25
    • 2017-10-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多