Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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-

  • http://ca.signfiles.com/TSAServer.aspx
  • http://timestamp.globalsign.com/scripts/timstamp.dll
  • https://timestamp.geotrust.com/tsa

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

like image 651
Abrar Jahin Avatar asked Sep 21 '20 05:09

Abrar Jahin


People also ask

How do I verify a signed XML document?

XML data can be represented in different ways, so this step is vital to verification. Add the Reference object to the SignedXml object. Compute the signature by calling the ComputeSignature method. Retrieve the XML representation of the signature (a < Signature > element) and save it to a new XmlElement object.

How do I add a signature to an XML document?

Add the Reference object to the SignedXml object. Compute the signature by calling the ComputeSignature method. Retrieve the XML representation of the signature (a < Signature > element) and save it to a new XmlElement object. Append the element to the XmlDocument object. Save the document.

How to digitally sign an XML document using cspparameters?

To digitally sign an XML document. Create a CspParameters object and specify the name of the key container. C#. CspParameters cspParams = new CspParameters (); cspParams.KeyContainerName = "XML_DSIG_RSA_KEY"; Dim cspParams As New CspParameters () cspParams.KeyContainerName = "XML_DSIG_RSA_KEY". Generate an asymmetric key using ...

How to sign an XML document using rsacryptoserviceprovider?

The key is automatically saved to the key container when you pass the CspParameters object to the constructor of the RSACryptoServiceProvider class. This key will be used to sign the XML document.


1 Answers

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;
    }
}
like image 119
Abrar Jahin Avatar answered Sep 19 '22 00:09

Abrar Jahin