# OpenSSL - extract hash algorithm from signature data (PKCS#1 v 1.5)

PKCS#1 v 1.5 stores hash algorithm identifier that was used to digest the original message. I would like to know if and how to extract this information.

I am working on a buffer containing the signature bytes and a buffer with public key.

I know it can be done (e.g. openssl rsautl does it with -verify and -asn1parse), but there is no sensible documentation for asn1 functions in openssl.

The hash identifier of a given RSASSA-PKCS1-v1_5 signature can only be obtained using the signature's public key¹. It's revealed by performing the signature verification steps 1 and 2/a/b/c (which basically gets the signature as an integer, raises it to the power $$e$$ modulo $$N$$ extracted from the public key, and converts back to bytes), then parsing the Encoded Message, which should be as per section 9.2 step 5, that is $$\text{EM}=\mathtt{00}\mathbin\|\mathtt{01}\mathbin\|\mathtt{FF}\mathbin\|\ldots\|\mathtt{FF}\mathbin\|\mathtt{00}\mathbin\|T$$ where

• the left two bytes $$\mathtt{00}\mathbin\|\mathtt{01}$$ are fixed, and followed by $$f\ge8$$ bytes at $$\mathtt{FF}$$ then a $$\mathtt{00}$$ ($$f$$ is maximal, that is for a $$b$$-bit public modulus $$N$$ the length of $$T$$ is $$\left\lfloor(b-17)/8\right\rfloor-f$$ bytes).
• $$T$$ is ASN.1 DER encoded, containing the hash identifier and hash value.

The pragmatic approach to decode $$T$$ is to look at note 1 (or the table below, which comes from that).

MD2:         30 20 30 0C 06 08 2A 86 48 86 F7 0D 02 02 05 00 04 10 ∥ H
MD5:         30 20 30 0C 06 08 2A 86 48 86 F7 0D 02 05 05 00 04 10 ∥ H
SHA-1:       30 21 30 09 06 05 2B 0E 03 02 1A 05 00 04 14 ∥ H
SHA-224:     30 2D 30 0D 06 09 60 86 48 01 65 03 04 02 04 05 00 04 1C ∥ H
SHA-256:     30 31 30 0D 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 ∥ H
SHA-384:     30 41 30 0D 06 09 60 86 48 01 65 03 04 02 02 05 00 04 30 ∥ H
SHA-512:     30 51 30 0D 06 09 60 86 48 01 65 03 04 02 03 05 00 04 40 ∥ H
SHA-512/224: 30 2D 30 0D 06 09 60 86 48 01 65 03 04 02 05 05 00 04 1C ∥ H
SHA-512/256: 30 31 30 0D 06 09 60 86 48 01 65 03 04 02 06 05 00 04 20 ∥ H


If we dive into the fine print, this is a DigestInfoValue. For example, the last entry decodes as

• 30 31   Sequence of 49 bytes
• 30 0D   Sequence of 13 bytes
• 06 09   OID of 9 bytes
• 60 86 48 01 65 03 04 02 06
OID 2.16.840.1.101.3.4.2.6 meaning "SHA512-256 algorithm"
• 05 00   NULL for the Parameters field²
• 04 20   Octet string of 32 bytes
• (the hash H follows as 32 bytes).

See this for how OIDs are encoded, and links to the ASN.1 DER specification. Examples can be thrown at Lapo Luchini's useful ASN.1 decoder.

¹ Given a signature, it's possible to make a new public/private key pair that makes this signature valid with any desired hash identifier and matching any desired known message (not only the the original) when checked with this new public key.

² This field must be present and NULL when encoding and checking $$T$$ in the context of RSASSA-PKCS1-v1_5. In other contexts a parser must accept that it is missing for the SHA algorithms, see the Exception in appendix B.1 and the two paragraphs above.

FWIW if using OpenSSL library (which OP implied but didn't exactly state), you can

1. call RSA_public_decrypt with RSA_PKCS1_PADDING to get the T specified in PKCS1, and shown in fgrieu's answer. (Calling this 'decrypt' is semantically wrong but OpenSSL derives from SSLeay which was initially created in the 1990s before the mistake in PKCS1v1 of calling signing and verifying 'encrypting with private key' and 'decrypting with public key' was corrected in PKCS1v2. The newer, generic and more flexible EVP_PKEY interface added by OpenSSL 1.0.0 in 2010 correctly calls this verifyrecover instead -- but it strips the hash OID, which doesn't work for this Q.)

2. call d2i_X509_SIG and look at resulting sig->algor->algorithm. For some reason, Eric used the name X509_SIG for what is actually PKCS1 DigestInfo. (And he also reused it for PKCS8 EncryptedPrivateKeyInfo -- which has the same structure algid + octetstring, but is not a signature or digest in any way! The algor part is type X509_ALGOR instead of AlgorithmIdentifier, but at least that is in X.509, so it's closer.)