0

I need to use Azure Key Vault HSM to sign arbitrary data in a PKCS#7 CMS. There are plenty of libraries and packages that can do this if you supply them with the private key, but obviously I don't want to export the private key from the HSM, so I've built my own PKCS#7 object in C# using Asnwriter.

I'm using the ITU-T spec as a reference: https://www.itu.int/ITU-T/formal-language/itu-t/x/x420/1999/PKCS7.html#PKCS7.signed-data

Everything was going well until I got to the "encryptedDigest" section of the SignerInfo. I need to know how this is constructed so I can inject it into my PKCS#7.

I've tried so many different ways of building this based on my understanding of the reference above, the relevent RFC's and tons of similar suggestions found on forums like this, but I can never create a "valid" signature.

From my latest understanding the Digest Info is constructed something like this:

DigestInfo Sequence {
    DigestAlgorithm Sequence{
        algorithm OID;
    }
    Digest Sequence {
        AlgorithmIdentifier Sequence {
            algorithm OID;
        }
        hashValue BitString;
    }
}

Where hashValue is a hash of the authenticatedAttributes (which includes its self a hash of data to be signed(both hashed in SHA256))

This whole DigestInfo object is BER encoded and then encrypted using the private key according to RSAES‐PKCS‐v1_5 and put in an OctetString at the end of the PKCS#7.

When I import my PKCS#7 into a verification tool, it all looks correct and the whole structure of the PKCS#7 matches that of an example PKCS#7 I have that has a valid signature. However when I try and validate the signature of my PKCS#7, it comes back as invalid.

I'm assuming the data in my hashValue is wrong (I have seen posts about not including the ASN1Tag [0] and length of the authenticatedAttributes in this, but that still didn't work) or my structure of the DigestInfo above is wrong, perhaps I need to write null bytes for the optional parameters in the algorithms?

Any insight would be greatly appreciated! EDIT================================================

That is a lot of useful information dave, much appreciated. However I'm still generating invalid signatures. I've amended my DigestInfo object based on the RFC's you referenced including the Null bytes for the parameters value and made it so everything is DER:

Base64 of actual object with data: MDEwDQYJYIZIAWUDBAIBBQAEIFFbecM/Y/Kw1qRLyqJn4VfXLoHebSpj9bVSy2V77pdc

DigestInfo Sequence { DigestAlgorithm Sequence{ algorithm OID (SHA256: 2.16.840.1.101.3.4.2.1); parameters NULL; } digest OctetString (bytes in base64 below hashed); }

And I swapped the A0 byte for the 31 of the SET tag, here is the base 64 of the ASN1 I am hashing(prehash):

MWkwGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMjIwOTEwMTExNjE4WjAvBgkqhkiG9w0BCQQxIgQg0dbdNJbW+Q5yalayQ0T4oBRhfvsTBtGEBLtSBf7C7PM=

So providing all of that is correct this issue lies with how I'm actually signing, and as this is occuring on Azure, I don't have a lot of control over it. I'm currently asking Azure to sign the bytes of the ASN1 digestInfo (top base64) using RS256 - RSASSA-PKCS-v1_5 using SHA-256. It seems Azure hashes the digestInfo object to make it 32bytes then signs it using the private key stored on the HSM.

2 Answers2

2

ITU may provide information about PKCS (and CMS) but they are not the author. PKCS7 (like all PKCS) was originally from RSALabs and now remains available as rfc2315; several versions of CMS -- which starts out almost but not exactly the same, and then progressively adds new features -- are only RFCs: rfc2630 rfc3369 rfc3852 rfc5652.

First of all, 'encrypted digest' and 'digest encryption' is a misnomer: signature is NOT encryption; see my list of links about this next door at https://security.stackexchange.com/questions/159282/ . This terminology was an attempt to be clever in PKCS1 v1 that backfired; it was corrected in PKCS1 v2 (2.0, 2.1, and 2.2) but PKCS7 had already been superseded by CMS which also corrected it.

In particular RSAES-PKCS1-v1_5 is not used here; rather PKCS7 signature uses what was called "block type 1" in PKCS1 v1 -- also republished as rfc2313 though not referenced in rfc2315 -- and retronymed RSASSA-PKCS1-v1_5 in PKCS1 v2 (RFCs 2437, 3447, 8017) which are used in most implementations now. (CMS continues to use RSASSA-PKCS1-v1_5 for RSA, but adds other non-RSA algorithms, which RSALabs didn't have motivation to do when it had responsibility.)

The DigestInfo structure is specified in detail in section 9.4 in rfc2315; this is (and says it is) a copy of PKCS1, specifically section 10.1.2 in rfc2313, and is not at all what you have. PKCS1 v2 changed the presentation of this material (more than once!) and for example rfc3447 section 9.2 shows how the DER (not just BER) encoding of DigestInfo is actually just a prefix which depends only on the hash algorithm concatenated with the hash value. (Since signature depends on having exactly the same bits, and the whole point of DER is to produce the same bits suitable for signature, it's not at all clear why v1 only specified BER for data so small no sane person would ever want indefinite encoding anyway. PKCS1 v2.0 only a few months after PKCS7 changed to DER, without even flagging it as a change.)

Over the lifetime of X.509 it has not been consistently clear whether the AlgorithmIdentifier for an algorithm with no parameters should omit them or explicitly encode NULL, and this has caused quite a few problems. For the DigestInfo in PKCS1 RSA signature rfc3447 et seq shows them as explicitly NULL and that's what everybody does.

I have seen posts about not including the ASN1Tag [0] and length of the authenticatedAttributes in [the hash input]

The correct formulation is that you should hash the encoding of authenticatedAttributes (renamed signedAttributes in CMS) without the 'implicit context-0' tag but WITH its 'natural' SET tag -- i.e. 31 instead of A0. Perhaps you or someone got this confused with the hashing of eContent which DOES exclude the tag and length. Compare (disclosure: my) PKCS#7 CMS - Message digest calculation process .

dave_thompson_085
  • 6,523
  • 1
  • 22
  • 25
0

Have you had a look at https://github.com/clairernovotny/SignedCmsKeyVault/ which does cms with azure keyvault keys?