在C#中的Xml签名中配置TSA

问题描述

我正在尝试使用Microsoft的Signature Class库在C#中对XML文件进行签名。

我所做的就是这样-

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography.Xml;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Xml;
using XMLSigner.Model;
using DataObject = System.Security.Cryptography.Xml.DataObject;

internal static XmlDocument GetSignedXMLDocument(XmlDocument xmlDocument,X509Certificate2 certificate,long procedureSerial = -1,string reason = "")
{
    //Check if local time is OK
    if(!Ntp.CheckIfLocalTimeIsOk()) {
        MessageBox.Show("PC Time is need to be updated before sign !");
        return null;    //Last Sign Not Verified
    }
    //Then sign the xml
    try
    {
        //MessageBox.Show(certificate.Subject);
        SignedXml signedXml = new SignedXml(xmlDocument);
        signedXml.SigningKey = certificate.PrivateKey;

        // Create a reference to be signed
        Reference reference = new Reference();
        /////////////////////
        reference.Uri = "";//"#" + procedureSerial;
        //reference.Type = reason;
        //reference.Id = DateTime.UtcNow.Ticks.ToString();
        reference.Id = Base64EncodedCurrentTime();
        //reference.TransformChain = ;
        /////////////////////
        // Add an enveloped transformation to the reference.            
        XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform(true);
        reference.AddTransform(env);

        // Add the reference to the SignedXml object.
        signedXml.AddReference(reference);

        //canonicalize
        XmlDsigC14NTransform c14t = new XmlDsigC14NTransform();
        reference.AddTransform(c14t);

        KeyInfo keyInfo = new KeyInfo();
        KeyInfoX509Data keyInfoData = new KeyInfoX509Data(certificate);
        KeyInfoName kin = new KeyInfoName();
        //kin.Value = "Public key of certificate";
        kin.Value = certificate.FriendlyName;

        RSA rsa = (RSA)certificate.PublicKey.Key;
        RSAkeyvalue rkv = new RSAkeyvalue(rsa);
        keyInfo.AddClause(rkv);

        keyInfo.AddClause(kin);
        keyInfo.AddClause(keyInfoData);
        signedXml.KeyInfo = keyInfo;

        //////////////////////////////////////////Add Other Data as we need////
        // Add the data object to the signature.
        //CreateMetaDataObject("Name",GetNetworkTime());
        signedXml.Addobject(CreateMetaDataObject(procedureSerial,reason));
        ///////////////////////////////////////////////////////////////////////
        // Compute the signature.
        signedXml.ComputeSignature();

        // Get the XML representation of the signature and save 
        // it to an XmlElement object.
        XmlElement xmlDigitalSignature = signedXml.GetXml();

        xmlDocument.DocumentElement.AppendChild(
                xmlDocument.ImportNode(xmlDigitalSignature,true)
            );
        /////////////////////
    } catch (Exception exception) {
        MessageBox.Show("Internal System Error during sign");
        throw exception;
    }
    return xmlDocument;
}

它工作正常。但是我对此代码有疑问。我必须使用TSA Server作为XML Signature中的存储时间,但是该时间是从本地PC设置的,为避免此问题,我已经通过{中定义的Ntp.CheckIfLocalTimeIsOk()函数手动检查了时间{3}}。但是我喜欢让时间来自TSA链接,例如-

是否可以在C#中的TSA中配置XmlSignature

解决方法

我自己解决了这个问题。

我要做的是像这样从XMLDocument创建一个哈希-

private static byte[] GetXmlHashByteStream(XmlDocument xmlDoc)
{
    byte[] hash;
    XmlDsigC14NTransform transformer = new XmlDsigC14NTransform();
    transformer.LoadInput(xmlDoc);
    using (Stream stream = (Stream)transformer.GetOutput(typeof(Stream)))
    {
        SHA1 sha1 = SHA1.Create();
        hash = sha1.ComputeHash(stream);
        stream.Close();
    }
    return hash;
}

然后获得像这样的时间戳哈希-

string stampURI = "http://timestamp.globalsign.com/scripts/timstamp.dll"
private TimeStampResponse GetSignedHashFromTsa(byte[] hash)
{
    TimeStampRequestGenerator reqGen = new TimeStampRequestGenerator();

    TimeStampRequest request = reqGen.Generate(
                TspAlgorithms.Sha1,hash,BigInteger.ValueOf(100)
            );
    byte[] reqData = request.GetEncoded();

    HttpWebRequest httpReq = (HttpWebRequest)WebRequest.Create(stampURI);
    httpReq.Method = "POST";
    httpReq.ContentType = "application/timestamp-query";
    httpReq.ContentLength = reqData.Length;

    //Configure Timeout
    //httpReq.Timeout = 5000;
    //httpReq.ReadWriteTimeout = 32000;

    // Write the request content
    Stream reqStream = httpReq.GetRequestStream();
    reqStream.Write(reqData,reqData.Length);
    reqStream.Close();

    HttpWebResponse httpResp = (HttpWebResponse)httpReq.GetResponse();

    // Read the response
    Stream respStream = new BufferedStream(httpResp.GetResponseStream());
    TimeStampResponse response = new TimeStampResponse(respStream);
    respStream.Close();

    return response;
}

重新-

如果您希望从响应中获得签名的Timestamp字符串,则可以这样做-

internal string GetSignedHashFromTsa(XmlDocument xmlDxocument)
{
    byte[] hash = GetXmlHashByteStream(xmlDxocument);
    TimeStampResponse timeStampResponse = GetSignedHashFromTsa(hash);
    byte[] signedEncodedByteStream = timeStampResponse.GetEncoded();
    return Convert.ToBase64String(signedEncodedByteStream);
}

如果您想从哈希字符串中获取时间,则必须执行以下操作-

internal static DateTime? GetTsaTimeFromSignedHash(string tsaSignedHashString)
{
    try {
        byte[] bytes = Convert.FromBase64String(tsaSignedHashString);
        TimeStampResponse timeStampResponse = new TimeStampResponse(bytes);
        return timeStampResponse.TimeStampToken.TimeStampInfo.GenTime;
    }
    catch(Exception ex)
    {
        Console.WriteLine(ex.Message);
        //throw ex;
        return null;
    }
}

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...