Configure TSA in Xml Signature in C#

I am trying to sign an XML file in C# using Signature Class library by Microsoft.

What I have done is like this-

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;
}

And it is working completely fine. But I have an issue with this code. I have to use TSA Server for the stored time in the XML Signature, but the time is set from local PC, to avoid this issue, I have checked time manually from Ntp.CheckIfLocalTimeIsOk() function defined in here. But I like to have the time come from a TSA link like-

Is it possible to configure TSA in XmlSignature in C#?

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

I have solved the problem myself.

What I have done is to create a hash from the XMLDocument like this-

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;
}

Then get the Timestamp Hash like this-
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, 0, 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;
}

Re-

If you like to get signed Timestamp string from the response, you can do like this-

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

If you like to get the time from the hash string, then you have to do something like this-
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;
    }
}


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x