2

How to signing a json document or string with x509 certificate?

public static void fund()
{
    string filePath = @"C:\Users\VIKAS\Desktop\Data.xml";
    //Read the file    

    XmlDocument xmlDoc = new XmlDocument();
    XElement ele = XElement.Load(filePath);
    String Xml = ele.ToString();
    xmlDoc.LoadXml(Xml);
    string signature = SignedXMLCert(xmlDoc);
    bool verified = ValidateSignature(signature);
}

public static string SignedXMLCert(XmlDocument xmlDoc)
{
    string startupPath = AppDomain.CurrentDomain.BaseDirectory + @"Certificates\unidesk.p12";
    //  startupPath = AppDomain.CurrentDomain.BaseDirectory + @"\Certificates\BBPS_enc.cer";

    //X509Certificate2 cert = new X509Certificate2(@"D:\Sonal\AXISOU_TEST.P12", "axisbank", X509KeyStorageFlags.Exportable);
    X509Certificate2 cert = new X509Certificate2(startupPath, "axisbank", X509KeyStorageFlags.Exportable);
    //  string PrivateKey = GetRSAPrivateKeyBase64(cert);

    var privateKey = cert.PrivateKey as RSACryptoServiceProvider;
    SignedXml signedXml = new SignedXml(xmlDoc);
    signedXml.SigningKey = privateKey;

    // Create a reference to be signed.
    Reference reference = new Reference();
    reference.Uri = "";

    KeyInfo keyInfo = new KeyInfo();
    //startupPath = AppDomain.CurrentDomain.BaseDirectory + @"\Certificates\BBPS_enc.cer";
    X509Certificate MSCert = new X509Certificate(startupPath, "axisbank", X509KeyStorageFlags.Exportable);
    // X509Certificate MSCert = X509Certificate.CreateFromCertFile(startupPath);

    keyInfo.AddClause(new KeyInfoX509Data(MSCert));
    signedXml.KeyInfo = keyInfo;


    // Add an enveloped transformation to the reference.
    XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform();
    reference.AddTransform(env);

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

    // Compute the signature.
    signedXml.ComputeSignature();

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

    // Append the element to the XML document.
  xmlDoc.DocumentElement.AppendChild(xmlDoc.ImportNode(xmlDigitalSignature, true));

    return xmlDoc.InnerXml.ToString();
}

public static bool ValidateSignature(String signedServiceMetadataContent)
{
    bool result = false;

    X509Certificate2 cert = GetCertificate();

    //Load the key
    CspParameters csp = new CspParameters();
    csp.KeyContainerName = cert.PublicKey.Key.ToString();

    RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(csp);

    //Load XML document
    XmlDocument xmlDocument = new XmlDocument();
    xmlDocument.PreserveWhitespace = true;
    xmlDocument.LoadXml(signedServiceMetadataContent);

    //create a SignedXml and load the xml document
    SignedXml signedXml = new SignedXml(xmlDocument);

    //find signature and create signature node list
    XmlNodeList xmlNodeList = xmlDocument.GetElementsByTagName("Signature");

    if (xmlNodeList.Count <= 0)
    {
        throw new CryptographicException("Verification failed: No Signature was found in the document.");
    }
    // if more than one signature was found.
    if (xmlNodeList.Count >= 2)
    {
        throw new CryptographicException("Verification failed: More that one signature was found for the document.");
    }

    //Load signature into SignedXml
    signedXml.LoadXml((XmlElement)xmlNodeList[0]);

    //check the signature
    result = signedXml.CheckSignature(cert, true);
    //result = signedXml.CheckSignature(rsa);


    return result;
}
private static X509Certificate2 GetCertificate()
{
    string startupPath = AppDomain.CurrentDomain.BaseDirectory + @"Certificates\unidesk.p12";
    X509Certificate2 cert = new X509Certificate2(startupPath, "axisbank", X509KeyStorageFlags.Exportable);
    return new X509Certificate2(cert);
}
slavoo
  • 5,798
  • 64
  • 37
  • 39
Syan
  • 137
  • 1
  • 5
  • 17
  • This code work for XML properly but how to signing json string please help i'm stuck last few day – Syan Nov 06 '16 at 09:12
  • Xmldsig signature format is only suitable for XML documents. You could apply JSON Web Signature (JWS) which is designed for JSON documents or a binary format like CMS – pedrofb Nov 06 '16 at 14:31
  • Thank you for reply you have sample code in C# using (RSA ,X509Certificate2) for json string – Syan Nov 07 '16 at 16:02
  • I can't provide a full example, but it shouldn't be difficult. A JWS is a Json document Too, which embeds your Json and apply for example a RSA signature like you are using. If you explain the purpose or intended usage of your signature I can help you with the attributes you will nneed – pedrofb Nov 07 '16 at 17:05
  • this my sample json string {"updateSRReq": { "incidentID": "", "createdBy": "037022000042048", "description": "037022000042048", "isVisibleToCustomer": "3", "updateType": "2", "activityType": "2", "createdOn": "2016-09-08 17:57", "lastUpdate": "2016-09-08 17:57", "status": "2", "closedTime": "" }, "subHeader": { "value": { "requestUUID": "123", "ServiceRequestId": "AE.MAPS.UDK.SSTP", "ServiceRequestVersion": "1.0", "ChannelId": "MAPS"}}} how to signature json string and verification json signature. – Syan Nov 08 '16 at 03:39
  • I am not a C# programmer, so I have included the general algorithm to perform the signature and validation of a JWS, and links to C# samples to sign/verify with certificates, and use base64 url encoding – pedrofb Nov 08 '16 at 07:56
  • Note that you sign with a *private key* and (usually) include the certificate within the signature. It's just that .NET binds the private key with the certificate in `X509Certificate2` instances (a specialized class derived from `X509Certificate2` would have been a better idea). – Maarten Bodewes Nov 15 '16 at 09:41
  • Hi @Syan, did you check the answer? Feedback? – pedrofb Nov 15 '16 at 16:33

1 Answers1

8

XMLDsig signature format is only suitable for XML documents. You could apply JSON Web Signature (JWS) which is designed for JSON documents.

JWS Signature

A JWS with compact serialization is represented by (see RFC7515)

BASE64URL(UTF8(JWS Protected Header)) || '.' ||
BASE64URL(JWS Payload) || '.' ||
BASE64URL(JWS Signature)

JWS protected header

The simplest header is composed by alg. RS256 means algorithm RSA with SHA-256

{"alg":"RS256"}

You can add other parameters such as x5c (X.509 Certificate Chain) or cty (Content Type)

JWS Payload

The payload is your JSON object encoded as base64url

eyJ1cGRhdGVTUlJlcSI6IHsgImluY2lkZW50SUQiOiAiIiwgImNyZWF0ZWRCeSI6ICIwMzcwMjIwMDAwNDIwNDgiLCAiZGVzY3JpcHRpb24iOiAiMDM3MDIyMDAwMDQyMDQ4IiwgImlzVmlzaWJsZVRvQ3VzdG9tZXIiOiAiMyIsICJ1cGRhdGVUeXBlIjogIjIiLCAiYWN0aXZpdHlUeXBlIjogIjIiLCAiY3JlYXRlZE9uIjogIjIwMTYtMDktMDggMTc6NTciLCAibGFzdFVwZGF0ZSI6ICIyMDE2LTA5LTA4IDE3OjU3IiwgInN0YXR1cyI6ICIyIiwgImNsb3NlZFRpbWUiOiAiIiB9LCAic3ViSGVhZGVyIjogeyAidmFsdWUiOiB7ICJyZXF1ZXN0VVVJRCI6ICIxMjMiLCAiU2VydmljZVJlcXVlc3RJZCI6ICJBRS5NQVBTLlVESy5TU1RQIiwgIlNlcnZpY2VSZXF1ZXN0VmVyc2lvbiI6ICIxLjAiLCAiQ2hhbm5lbElkIjogIk1BUFMifX19

JWS Signature

The JWS signature is computed on

BASE64URL(UTF8(JWS Protected Header)) || '.' || BASE64URL(JWS Payload))

Build the following string and apply the RSA digital signature algorithm with the private key of your certificate

eyJhbGciOiJSUzI1NiJ9.eyJ1cGRhdGVTUlJlcSI6IHsgImluY2lkZW50SUQiOiAiIiwgImNyZWF0ZWRCeSI6ICIwMzcwMjIwMDAwNDIwNDgiLCAiZGVzY3JpcHRpb24iOiAiMDM3MDIyMDAwMDQyMDQ4IiwgImlzVmlzaWJsZVRvQ3VzdG9tZXIiOiAiMyIsICJ1cGRhdGVUeXBlIjogIjIiLCAiYWN0aXZpdHlUeXBlIjogIjIiLCAiY3JlYXRlZE9uIjogIjIwMTYtMDktMDggMTc6NTciLCAibGFzdFVwZGF0ZSI6ICIyMDE2LTA5LTA4IDE3OjU3IiwgInN0YXR1cyI6ICIyIiwgImNsb3NlZFRpbWUiOiAiIiB9LCAic3ViSGVhZGVyIjogeyAidmFsdWUiOiB7ICJyZXF1ZXN0VVVJRCI6ICIxMjMiLCAiU2VydmljZVJlcXVlc3RJZCI6ICJBRS5NQVBTLlVESy5TU1RQIiwgIlNlcnZpY2VSZXF1ZXN0VmVyc2lvbiI6ICIxLjAiLCAiQ2hhbm5lbElkIjogIk1BUFMifX19 

Finally encode the signature as base64url and append the result to the previous data to sign. You will get a JWS like this hhhhh.ppppp.sssss where hhhhh is the header ppppp the payload and sssss the signature

Use the following links to

JWS verification

To verify a signature from the compact format hhhhh.ppppp.sssss, base64url decode the signature sssss, and verify the signature with the signed data hhhhh.ppppp and the used certificate

Community
  • 1
  • 1
pedrofb
  • 37,271
  • 5
  • 94
  • 142
  • 1
    Alternative: a (detached) XML or CMS signature over the JSON *file*. Note that the JSON representation should not change between signing and verifying in that case. JSON signatures are of course a more elegant solution. – Maarten Bodewes Nov 15 '16 at 09:38
  • is signature depends on order of fields in the JSON? – Andrew Sneck Jul 15 '20 at 08:01
  • 1
    @AndrewSneck, JWS signature is performed on the payload in raw, so the order of the fields, spaces, characters or any variation of the JSON would invalidate the signature. This is not exactly the case in XMLDsig because there is a prior canonicalization process, which allows some type of variability but at the same time makes the XML signature much more difficult to use in practice. – pedrofb Jul 15 '20 at 08:39
  • Worth mentioning that except if you intend to send the JWS within an URL, base64url is not necessary: you can use classic Base64 encoding instead. That would not be a JWS any more, but that would work for data persistence within a file, database, when sending data within the body of a message, etc. – Ama Apr 08 '22 at 08:25