簡體   English   中英

如何在 C# 中生成數字簽名的 XML 文檔的 XML 的 .sig?

[英]How to generate .sig of XML of digitally signed XML document in C#?

我需要使用數字簽名對 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(512bits) 算法生成消息摘要和 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;
        }
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM