openssl icon indicating copy to clipboard operation
openssl copied to clipboard

Determining the digest algorithm used by a PKCS#7 object

Open gettalong opened this issue 2 years ago • 5 comments

I'm working on adding digital signature support to HexaPDF, using OpenSSL for the cryptographic needs.

One type of signature is a DER encoded PKCS#7 object. I can successfully extract the signing time, validity periods and other stuff. However, I didn't find any method that returns the message digest algorithm used during signing.

For example, when extracting the PKCS#7 object and looking at it with OpenSSL command line tools, I find that the algorithm is SHA1:

$ openssl pkcs7 -in /tmp/sig.pkcs7 -inform DER -print | head -n 7
PKCS7:
  type: pkcs7-signedData (1.2.840.113549.1.7.2)
  d.sign:
    version: 1
    md_algs:
        algorithm: sha1 (1.3.14.3.2.26)
        parameter: NULL

How can I retrieve this information using Ruby?

gettalong avatar Nov 10 '21 10:11 gettalong

Getting digestAlgorithms (of SignedData = d.sign structure; the one appearing in the excerpt of openssl's output) is currently only possible by manually parsing DER encoded data:

der = File.binread("/tmp/sig.pkcs7")

#    ContentInfo ::= SEQUENCE {
#      contentType ContentType,
#      content
#        [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL }
p7 = OpenSSL::ASN1.decode(der)
raise "not a signed-data" if p7.value[0].value != "pkcs7-signedData"
signed_data = p7.value.find { |x| x.tag_class == :CONTEXT_SPECIFIC && x.tag == 0 }.value[0]

#    SignedData ::= SEQUENCE {
#      version Version,
#      digestAlgorithms DigestAlgorithmIdentifiers,
#      contentInfo ContentInfo,
#      certificates
#         [0] IMPLICIT ExtendedCertificatesAndCertificates
#           OPTIONAL,
#      crls
#        [1] IMPLICIT CertificateRevocationLists OPTIONAL,
#      signerInfos SignerInfos }
digest_algorithms = signed_data.value[1]

#    DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier
#    DigestAlgorithmIdentifier ::= AlgorithmIdentifier
#    AlgorithmIdentifier  ::=  SEQUENCE  {
#         algorithm               OBJECT IDENTIFIER,
#         parameters              ANY DEFINED BY algorithm OPTIONAL  }
digest_algorithms.value.each do |algo_id|
  p algo_id.value[0].value
end

There is also another field indicating the digest algorithm inside SignerInfo. Currently neither has a corresponding method. This would be a nice addition to these classess.

rhenium avatar Nov 15 '21 09:11 rhenium

Thanks @rhenium! If I implement this, would this be needed to be implemented in C or in Ruby?

gettalong avatar Nov 15 '21 11:11 gettalong

@rhenium I was just revisiting this and wondering if you mean by "Currently neither has a corresponding method." that OpenSSL itself has no method for that or that the Ruby interface has no method for that?

I'm asking because I was looking through the available OpenSSL functions at https://www.openssl.org/docs/man3.0/man3/ (to see how a PKCS7/CMS structure could be created manually to include additional signed attributes) and seeing no method for getting the digest algorithm.

gettalong avatar Dec 29 '21 01:12 gettalong

I asked a similar question on stackoverflow. I suspected that getting this info would require digging into the raw ASN1, but wasn't sure. Reading this thread seems to confirm my guess.

There is also another field indicating the digest algorithm inside SignerInfo. Currently neither has a corresponding method. This would be a nice addition to these classess.

@rhenium would you be able to suggest an outline of how you'd like to see this implemented? i'd be available to work on this contribution, but would appreciate some guidance on how to get started.

alexdean avatar Apr 13 '23 21:04 alexdean

@alexdean I did not add this to the Ruby OpenSSL library but in Ruby it would not be that hard, see https://github.com/gettalong/hexapdf/blob/master/lib/hexapdf/digital_signature/cms_handler.rb#L110-L116 where I manually decode the ASN.1 structure.

The drawback is that the structure gets parsed twice, once by Ruby OpenSSL and then manually. So having this integrated in Ruby OpenSSL would certainly be better.

As for your StackOverflow question, maybe this helps: https://github.com/gettalong/hexapdf/blob/master/lib/hexapdf/digital_signature/signing/signed_data_creator.rb (I had a similar problem because I needed to add some custom things to a PKCS#7/CMS signed data structure).

gettalong avatar Apr 15 '23 09:04 gettalong