当前位置: 首页 > 知识库问答 >
问题:

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

马弘和
2023-03-14

我需要用数字签名对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 文件是否可能?

正如我的要求是:

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

共有1个答案

邵飞鸿
2023-03-14

尝试以下代码。我将您的代码与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;
        }
    }
}
 类似资料:
  • 我希望我们拥有iPhone的员工能够让公众对一份文件进行数字签名,该文件最初是基于Spring的网络表单。 此表格仅在我们的内部网上,仅由我们的员工填写。 到目前为止,我的研究考虑了一些可能的解决方案: 手机屏幕上的电子湿墨水签名 目的是证明某个人签署了一份文件。 1从技术上讲是可能的,但并不重要,因为它很容易被复制。 是否可以在iOS设备上通过浏览器和javascript捕获指纹? 3能适应这种

  • 我一直在网络和java教程中寻找这一点。但我没有澄清如何为文档生成数字签名。java教程对此进行了解释,但我真正想要的是 用户附带一个文件和一个字符串密钥 使用该密钥,文件被数字签名 相应的公钥,该标志与该文档一起发布 那么,如何转换给定的String私钥来做到这一点。在尝试java教程和web中给出的示例(带有将字节从字符串而不是文件中放置的一些变体)时,我遇到了如下异常

  • 我需要一些使用OpenSSL的帮助,以便使用C(Windows和Linux)生成数据块的签名。该应用程序与谷歌认证有关。谷歌文档中的说明如下: “使用从Google开发者控制台获得的私钥,使用SHA256withRSA(也称为RSASSA-PKCS1-V1_5-Sign,使用SHA-256哈希函数)对输入的UTF-8表示形式进行签名。输出将是字节数组。” 私钥是一个密码。p12包含1660字节二进

  • signature1值总是39字节[]数组,当我转换为十六进制时,长度大于64。我想在C#中从P-128 ECC算法生成精确的64长度十六进制签名值。 让我解释下面的代码: > 对必须签名的纯文本消息进行哈希

  • 当我从REST客户端调用APIendpoint时,由于涉及Signature而出错。 请求: 主持人:https://xxx.execute-api.ap-southeast-1.amazonaws.com/latest/api/name 授权: AWS4-HMAC-SHA256凭据=/20160314/ap-东南-1/执行-api/aws4_request, SignedHeaders=host

  • 我正在尝试向现有的数字签名pdf(认证签名)中添加一个空签名字段。 我有一个工作流,其中许多用户将签署该文档(批准签名),该文档创建时带有“n”个空签名字段,每个用户一个,我们的应用程序首先应用一个不可见的认证签名,然后每个用户可以在各自的字段中签署该文档,但由于工作流中意外的更改,其他用户可能希望签名,因此,我们希望添加相应的空签名字段,然后应用签名。 我试图将空字段(带有单元格事件的表)添加到