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.