5

I receive an XML document as a string parameter in my method. The XML document is:   

<Document>
    <ZipContainer> Zip_File_In_Base64 </ZipContainer>
    <X509Certificate> Certificate_In_Base64 </X509Certificate>
</Document>

From this string I extract the ZIP file in base64 format and X509Certificate2 certificate in base64 format. The ZIP file contains:

  • file describing the contents of the ZIP file as XML (file packageDescription.xml);

  • files with the contents of transmitted documents (for example, *.doc files);

  • files with content of detached digital signature (*.p7s files - detached digital signature);

From the archive should be extracted signature that signed documents (detached digital signature may be more than one). Detached digital signature are stored in files with .p7s extension. Each signature must be done to check its agreement with digital signature, with which the user is logged in to the portal.

The must consist of two steps:

  1. See method certificateValidator() (see this method below): This is a of detached signature, contained in the .p7s files with their corresponding files that are signed, these *. P7s-files.
    For example: a pair of related files: ZayavUL_3594c921f545406d9b8734bbe28bf894.doc_1.p7s and
    ZayavUL_3594c921f545406d9b8734bbe28bf894.doc.

  2. See method certificateValidator(): This verifies certificate from a file .p7s with a certificate that is extracted from the XML document input string.

Questions

  1. The method signatureValidator (see this method below) is not currently used detached signature of the files .p7s. I did try, but without success. How do I properly verify the detached signature of .p7s file for its corresponding file?

  2. In the method certificateValidator (see this method below) how do I verify the conformity of the certificate extracted from the .p7s file, with a certificate extracted from input string in Base64 format?

  3. The line of code foreach (X509Certificate2 x509 in signCms.Certificates) { } ---> Certificates Collection always is empty. Why?

Input parameters

  • Dictionary <string, byte[]> dictP7SFiles (key - the name of the file *.p7s, value - array of bytes, representing *.p7s file)

  • Dictionary <string, byte[]> dictNotP7SFiles (key - the name of the file that is signed using detached signature from *.p7s file, value - array of bytes, representing file)

  • X509Certificate2 userCertX509 - certificate object, extracted from the input xml-document (where it has the format Base64)

Code

Here below are testing implementation of verification steps (see above this 2 steps):

private bool certificateValidator(Dictionary<string, byte[]> dictP7SFiles, 
    Dictionary<string, byte[]> dictNotP7SFiles, X509Certificate2 userCertX509)
{
  bool isValid = false;           
  try
  {               
    foreach (KeyValuePair<string, byte[]> pair in dictP7SFiles)
    {
      ContentInfo contentInfo = new ContentInfo(pair.Value);
      SignedCms signCms = new SignedCms(contentInfo, true);                   

      if (signCms.Certificates.Count != 0)
      {
        //Certificates Collection always is empty. Why?
        foreach (X509Certificate2 x509 in signCms.Certificates)
        {
          if ((x509.SerialNumber != userCertX509.SerialNumber) 
              || (x509.Thumbprint != userCertX509.Thumbprint))
          {
            isValid = false;
            return isValid;
          }
        }

        isValid = true;
        return isValid;
      }
    }
  }
  catch (Exception ex)  
  {
    //here process exception code
  }           

  return isValid;
}



private bool signatureValidator(Dictionary<string, byte[]> dictP7SFiles, 
    Dictionary<string, byte[]> dictNotP7SFiles, X509Certificate2 userCertX509)
{
  bool isValid = false;
  try
  {              
    byte[] data = dictP7SFiles["ZayavUL_3594c921f545406d9b8734bbe28bf894.doc"];
    byte[] publicKey;
    byte[] signature;
    object hasher = SHA1.Create(); // Our chosen hashing algorithm.
    // Generate a new key pair, then sign the data with it:
    using (var publicPrivate = new RSACryptoServiceProvider())
    {
      signature = publicPrivate.SignData(data, hasher);
      publicKey = publicPrivate.ExportCspBlob(false); // get public key
    }
    // Create a fresh RSA using just the public key, then test the signature.
    using (var publicOnly = new RSACryptoServiceProvider())
    {
      publicOnly.ImportCspBlob(publicKey);

      isValid = publicOnly.VerifyData(data, hasher, signature); // Return True

      //isValid = ByteArrayCompare(dictP7SStreams["ZayavUL_3594c921f545406d9b8734bbe28bf894.doc_1.p7s"], signature);

      byte[] p7sDetachedSignature = File.ReadAllBytes(@"D:\ZayavUL_3594c921f545406d9b8734bbe28bf894.doc_1.p7s");
      isValid = ByteArrayCompare(p7sDetachedSignature, signature);
    }                
  }
  catch (Exception)
  {
    //here process exception code
  }

  return isValid;    
}
Maarten Bodewes
  • 80,169
  • 13
  • 121
  • 225
Mike Gogi
  • 51
  • 1
  • 3
  • I think that those kind of files doesn't stores certificates. You can use OpenSSL.exe to test it. openssl.exe asn1parse -in file.p7s -inform DER. Also, thank you for the code, it helped me. – Luiz Felipe Aug 28 '14 at 16:11

1 Answers1

0

The main thing you are doing wrong is to regenerate the CMS and signature. You should parse the CMS message, then indicate the external content during verification.

  1. How do I properly verify the detached signature of .p7s file for its corresponding file?

Take a look at the following Java code on SO to see how to verify signatures; C# should use the same architecture and it should therefore work similarly.

  1. In the method certificateValidator (see this method below) how do I verify the conformity of the certificate extracted from the .p7s file, with a certificate extracted from input string in Base64 format?

Decode the base 64 certificate and perform chain verification and validation of the certificate. How much validation you want to perform (e.g. checking the effective date) is up to you.

  1. The line of code foreach (X509Certificate2 x509 in signCms.Certificates) { } ---> Certificates Collection always is empty. Why?

You simply didn't put one in during construction of the new CMS structure.


You certainly should not regenerate the signature either. Normally you would not have the private key during verification, and the algorithm may not match the one used within the CMS document. Furthermore, even if it would, signature generation is not always deterministic (you may get different signature values).

Community
  • 1
  • 1
Maarten Bodewes
  • 80,169
  • 13
  • 121
  • 225