Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Enveloping, signing and creating PKCS#7 DER message with .pfx certificate

My task is to create data that are digitally signed in format of PKCS#7 version 1.5 (RFC 2315) DER (ITU-T Recommendation X.690) - basically ANSI.1 with X.509 signature?

the message must satisfy following:

  • must be type signedData
  • must contain signed data
  • must contain signer's certificate
  • must contain one digital signature

My code is following

static void Main(string[] args)
{

    string pfx = @"C:\Users\marek\Downloads\mfcr\marek-pfx.pfx";
    string xml = @"C:\Users\marek\Downloads\mfcr\souhr20141.xml";
    X509Certificate2 cert = new X509Certificate2(pfx, "thepass");

    byte[] publicBytes = cert.RawData;

    //var f = new FileStream(xml, System.IO.FileMode.Open);
    var fileContent = System.IO.File.ReadAllBytes(xml);

    char[] cArray = System.Text.Encoding.ASCII.GetString(fileContent).ToCharArray();
    RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)cert.PrivateKey;

    byte[] signedData = rsa.SignData(new System.Text.UTF8Encoding().GetBytes(cArray), new SHA1CryptoServiceProvider());

    RSACryptoServiceProvider rsa2 = (RSACryptoServiceProvider)new X509Certificate2(publicBytes).PublicKey.Key;

    var dataGenerator = new CmsEnvelopedDataStreamGenerator();
    bool verified = rsa2.VerifyData(new System.Text.UTF8Encoding().GetBytes(cArray), new SHA1CryptoServiceProvider(), signedData);

    File.WriteAllBytes(@"C:\Users\marek\Downloads\mfcr\Foo.p7b", signedData);
 }

The WebService that Iam sending the Foo.p7b responds with: File is not in expected format of PKCS7(DER).

This code for sending the HttpWebRequest :

static void Main(string[] args)
    {
        try
        {

            string fileName = (@"C:\Users\marek\Downloads\mfcr\Foo.p7b");
            WebResponse rsp = null;
            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create("https://adisepo.mfcr.cz/adistc/epo_podani");
            request.ClientCertificates.Add(new X509Certificate(pfx,"thepass"));
            request.Method = "POST";
            request.ContentType = "application/pkcs7-signature";
            request.Credentials = CredentialCache.DefaultNetworkCredentials;
            var encoder = new UTF8Encoding();
            var reqStream = request.GetRequestStream();
            StreamWriter writer = new StreamWriter(request.GetRequestStream());
            // Write the XML text into the stream
            writer.WriteLine(GetTextFromXMLFile(fileName));
            writer.Close();
            reqStream.Close();
            rsp = request.GetResponse();
            StreamReader sr = new StreamReader(rsp.GetResponseStream());
            string result = sr.ReadToEnd();
            sr.Close();
            Console.Write("\n příkaz odeslán  \n");
            Console.Write(result);
            Console.ReadLine();
            Console.Read();
        }
        catch (Exception ex)
        { Console.WriteLine(ex.ToString());
        Console.ReadLine();
        }

    }
    private static string GetTextFromXMLFile(string file)
    {
        StreamReader reader = new StreamReader(file);
        string ret = reader.ReadToEnd();
        reader.Close();
        return ret;
    }
}

I'm struggling with this issue for almost 5 days - I'm surely not expert on digital signature or certificates.

From what I learned so far - to create message like that I should do:

  1. Sign the xml with my private key
  2. Envelope that blob with my public key

But how could the recipient check whether I am the real sender? Should I add to HttpWebRequest parameter with my certificate? Or that step 2 - Enveloping the message is enough for him to check that?

Thank you everyone for your time and replies.

like image 642
Marek Avatar asked Jan 11 '23 12:01

Marek


1 Answers

Your code tries digitally sign byte representation of XML, but signing XML requires more processing before signing. For example XML requires to be canonicalized (or signed message can be injected with unsigned data), and there is a special format for enveloped signatures. I don't know what is actual method Danovy Portal uses, but if it uses standard way, you can follow the links below and sign your data.

MSDN: How to: Sign XML Documents with Digital Signatures

How enveloped signatures look

And just for information (don't think you really need to read this) W3C Xml Signature specification

EDIT: to send pkcs#7 message change the code. When generating

        ContentInfo contentInfo = new ContentInfo(new System.Text.UTF8Encoding().GetBytes(cArray));
        SignedCms cms = new SignedCms (contentInfo);
        CmsSigner signer = new CmsSigner (cert);
        cms.ComputeSignature (signer);
        byte[] pkcs7=cms.Encode ();
        File.WriteAllBytes(@"../../Foo.p7b", pkcs7);

When send:

            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create("https://adisepo.mfcr.cz/adistc/epo_podani");
            //we don't need to add certificate to POST
    //      request.ClientCertificates.Add(new X509Certificate(pfx,"test"));
            request.Method = "POST";
            request.ContentType = "application/pkcs7-signature";
            request.Credentials = CredentialCache.DefaultNetworkCredentials;
            var encoder = new UTF8Encoding();
            using (var reqStream = request.GetRequestStream())
            {
                // Write pkcs#7 into the stream
                byte[] pkcs = File.ReadAllBytes(@"../../Foo.p7b");
                reqStream.Write(pkcs, 0, pkcs.Length);
            }
            rsp = request.GetResponse();
like image 200
Sergey Zhukov Avatar answered Feb 03 '23 04:02

Sergey Zhukov