如何在C#中生成数字签名XML文档的.sig XML?

如何在C#中生成数字签名XML文档的.sig XML?,c#,xml,digital-signature,pkcs#7,xml-signature,C#,Xml,Digital Signature,Pkcs#7,Xml Signature,我需要使用数字签名对xml文档进行签名,并使用该文档生成数字签名xml的.sig文件。我正在使用PKCS7算法进行同样的操作。我能够成功地将签名放入xml中。但无法生成.sig文件。我的代码如下: public static void SignXmlDocumentWithCertificate(XmlDocument doc, X509Certificate2 cert) { SignedXml signedxml = new SignedXml(doc); signedxml

我需要使用数字签名对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>

希腊风花
希腊银莲花
6.
大部分是阴凉的
$9.16
071099
/VUzr4wRNv2e6SzE6TdHLM8c+/A=
i3gGf2Q……8Q==
MIID6D………fFo=
现在我知道我不是做错了,就是错过了什么。我的问题是

  • 有没有办法用签名的xml生成.sig文件
  • PKCS7中是否可以使用大型xml文件
  • 我的要求是:

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

  • 试试下面的代码。我将您的代码与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;
            }
        }
    }
    
    使用系统;
    使用System.Collections.Generic;
    使用System.Linq;
    使用系统文本;
    使用System.Xml;
    使用系统安全;
    使用System.Security.Cryptography;
    使用System.Security.Cryptography.X509证书;
    使用System.Security.Cryptography.Xml;
    使用System.IO;
    命名空间控制台应用程序1
    {
    班级计划
    {
    常量字符串文件名=@“c:\temp\test.xml”;
    静态void Main(字符串[]参数)
    {
    XmlDocument doc=新的XmlDocument();
    doc.Load(文件名);
    字符串computerName=Environment.GetEnvironmentVariable(“computerName”);
    字符串userName=Environment.GetEnvironmentVariable(“用户名”);
    X509Certificate2 cert=GetCertificateFromStore(“CN=”+computerName+“\\”+用户名);
    签署XmlDocumentWithCertificate(文件、证书);
    rsacyptoserviceprovider publicKey=(rsacyptoserviceprovider)cert.publicKey.Key;
    byte[]unencryptedData=Encoding.UTF8.GetBytes(doc.OuterXml);
    流=加密文件(未加密数据,公钥);
    Console.ReadLine();
    }
    公共静态无效签名XmlDocumentWithCertificate(XmlDocument文档,X509Certificate2证书)
    {
    SignedXml SignedXml=新的SignedXml(doc);
    signedxml.SigningKey=cert.PrivateKey;
    引用=新引用();
    reference.Uri=“”;
    AddTransform(新的XMLDSIGenveledSignatureTransform());
    signedxml.AddReference(参考);
    KeyInfo KeyInfo=新的KeyInfo();
    keyinfo.AddClause(新的KeyInfoX509Data(cert));
    signedxml.KeyInfo=KeyInfo;
    signedxml.ComputeSignature();
    xmlement xmlsig=signedxml.GetXml();
    doc.DocumentElement.AppendChild(doc.ImportNode(xmlsig,true));
    //Console.WriteLine(doc.ImportNode(xmlsig,true));
    }
    私有静态X509Certificate2 GetCertificateFromStore(字符串certName)
    {
    //获取当前用户的证书存储。
    X509Store=新的X509Store(StoreLocation.CurrentUser);
    尝试
    {
    打开(OpenFlags.ReadOnly);
    //将所有证书放置在X509Certificate2Collection对象中。
    X509Certificate2Collection certCollection=store.Certificates;
    //如果使用具有受信任根的证书,则不需要FindByTimeValid,而是:
    //currentCerts.Find(X509FindType.FindBySubjectDifferentizedName,certName,true);
    X509Certificate2Collection currentCerts=certCollection.Find(X509FindType.FindByTimeValid,DateTime.Now,false);
    X509Certificate2Collection signingCert=currentCerts.Find(X509FindType.FindBySubjectDifferentizedName,certName,false);
    如果(signingCert.Count==0)
    返回null;
    //返回集合中的第一个证书,该证书具有正确的名称且为当前证书。
    返回签名证书[0];
    }
    最后
    {
    store.Close();
    }
    }
    //使用公钥加密文件。
    私有静态内存流加密文件(字节[]未加密数据,RSACryptServiceProvider RSAPPublicKey)
    {
    MemoryStream stream=null;
    使用(AesManaged AesManaged=新AesManaged())
    {
    //为创建托管的AES实例
    //数据的对称加密。
    aesManaged.KeySize=256;
    AES.BlockSize=128;
    aes.Mode=CipherMode.CBC;
    使用(ICryptoTransform transform=aesManaged.CreateEncryptor())
    {
    RSAPKCS1KeyExchangeFormatter keyFormatter=新的RSAPKCS1KeyExchangeFormatter(rsaPublicKey);
    byte[]keyEncrypted=keyFormatter.CreateKeyExchange(aesManaged.Key,aesManaged.GetType());
    //创建要包含的字节数组
    //键和IV的长度值。
    字节[]LenK=新字节[4];
    字节[]LenIV=新字节[4];
    int lKey=密钥加密的.Length;
    LenK=比特变换器