
This article is a sister piece (hereinafter referred to as “sister piece”) to “Understanding Symmetric Encryption, Asymmetric Encryption, Hash Values, Signatures, Certificates, and the Relationship Between HTTPS in One Article”. It is best to read the previous article before reading this one.
This article provides an in-depth explanation of the signatures of PE files (also known as exe/dll/sys files), which are the most widely used and most easily accessible in daily work, and explains step by step how to manually verify the validity of a PE file signature.
Background
In recent work, a self-developed driver needed to prevent unauthorized R3 program calls, so a plan needed to be designed. The first thing the author thought of was to use Windows’ signature mechanism, solidify the signature’s CommonName and the trusted parent certificate’s public key into the driver, and then separately verify the driver, the module where the caller thread is located, and the signature of the binary program of the caller to prevent illegal calls.
After checking a lot of signature-related materials, it was found that they were all implemented by the WinTrust API function under R3. However, the problem is that there are no such APIs under R0, so it seems that I have to write code to implement the signature verification logic myself.
After a period of exploration, it was found that the existing code is heavily bound to a certain platform or library - for example, the signature verification code under Linux is bound to the data structure of Linux, and other open source codes are bound to the openssl library, but the openssl library cannot be called under R0 because it depends on many functions that are not available under R0 (such as _open and other POSIX C functions).
It seems that I can only do it myself, so I continue to study the underlying principles of signature verification. However, most of the information about signatures on the Internet is heavily theoretical, and at least the author has not found an example of practical verification of signatures using only hash and RSA algorithms - so there is no way, I can only completely verify it myself from scratch…
Several concepts
ASN.1
ASN.1 itself only defines the abstract syntax for representing information, but does not limit its encoding method. Excerpt from Wikipedia
DER encoding
Since ASN.1 does not specify the encoding method, some encoding methods are still needed when organizing data in practice. DER is the most commonly used one, and this encoding method is big-endian.
PKCS#7/PKCS#1/PKCS#9
These are predefined standards for various encryption formats.
AuthentiCode signature
A type of embedded signature format defined by Microsoft (using PKCS#7 and other standards), another common one is CAT signature (.cat file).
To understand the above concepts in a more popular way: ASN.1 can be understood as the alphabets of A-Z, DER encoding is to specify which alphabet to form a word, PKCS#7 and other standards further specify which words can form some fixed sentences, and finally AuthentiCode uses these fixed sentences to write an article.
Talk about certificates again
Through the sister piece, we have roughly understood the concepts and functions of certificates and signatures. But in the sister piece, only a rough structure of a certificate was given (below), and it was not explained in detail.
Since this article needs to be manually verified, it is necessary to give a detailed structure of the certificate:
- Certificate ::= SEQUENCE {
- tbsCertificate TBSCertificate,
- signatureAlgorithm AlgorithmIdentifier,
- signatureValue BIT STRING
- }
The above definition uses ASN1 syntax, here we need to understand the following information first:
- tbsCertificate is the blue part in the above figure
- signatureValue is the red part in the above figure
- signatureAlogrithm is not reflected in the above figure, this field indicates which hash function is used to hash the data of tbsCertificate
Knowing the above structure, we can figure out how to verify the certificate:
- Extract the public key of the parent certificate (issuer or called Issuer) of the certificate
- Use the public key to decrypt the signatureValue part of the sub-certificate, and then extract the hash value 1 according to the structure of PKCS#9, here it is recorded as Hash1
- Use the same hash algorithm in signatureAlgorithm to hash the data of tbsCertificate, get Hash2
- If Hash1 is equal to Hash2, the certificate is valid
Manual verification of certificate validity
Well, the boring theory is temporarily over, let’s look at a practical example.
We take the Windows version of Python.exe as an example, and check its digital signature and certificate information in turn:
It can be found that the digital signature of Python.exe uses the sha256 algorithm, and the certificate chain is:
- DigiCert->DigiCert SHA2 Assured ID Code Singing CA->Python Software Foundation
Through the sister piece, we know that the entire PKI system is built on the trust of some certificates distributed with the operating system or browser. For the convenience of describing this article, suppose we trust the DigiCert SHA2 Assured ID Code Singing CA certificate in the above system chain, we can select “Copy to File” under “Details” to export this certificate for research (remember to select DER format when exporting):
First, we need to obtain the public key of the parent certificate, which can be obtained using the openssl command. The following command outputs the public key of the parent certificate of Python.exe (PythonParent.cer) to the PythonParentPublicKey.pem file:
- >> openssl x509 -inform DER -in PythonParent.cer -pubkey -noout > PythonParentPublicKey.pem
- >> cat PythonParentPublicKey.pem
- -----BEGIN PUBLIC KEY-----
- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+NOzHH8OEa9ndwfTCzFJ
- Gc/Q+0WZsTrbRPV/5aid2zLXcep2nQUut4/6kkPApfmJ1DcZ17aq8JyGpdglrA55
- KDp+6dFn08b7KSfH03sjlOSRI5aQd4L5oYQjZhJUM1B0sSgmuyRpwsJS8hRniolF
- 1C2ho+mILCCVrhxKhwjfDPXiTWAYvqrEsq5wMWYzcT6scKKrzn/pfMuSoeU7MRzP
- 6vIK5Fe7SrXpdOYr/mzLfnQ5Ng2Q7+S1TqSp6moKq4TzrGdOtcT3jNEgJSPrCGQ+
- UpbB8g8S9MWOD8Gi6CxR93O8vYWxYoNzQYIH5DiLanMg0A9kczyen6Yzqf0Z3yWT
- 0QIDAQAB
- -----END PUBLIC KEY-----
Now that we have the public key of the parent certificate, we need to separate the tbsCertificate and signatureValue parts of the Python.exe certificate. So how do we know the offset and length of these two parts in the certificate? The answer is: continue to use the versatile openssl command:
First, let’s get an intuitive feel for the certificate information of Python.exe:
- >> openssl x509 -inform DER -in ./Python.exe.cer -noout -text
- Certificate:
- Data:
- Version: 3 (0x2)
- Serial Number:
- 03:3e:d5:ed:a0:65:d1:b8:c9:1d:fc:f9:2a:6c:9b:d8
- Signature Algorithm: sha256WithRSAEncryption
- Issuer: C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert SHA2 Assured ID Code Signing CA
- Validity
- Not Before: Dec 18 00:00:00 2018 GMT
- Not After : Dec 22 12:00:00 2021 GMT
- Subject: C = US, ST = New Hampshire, L = Wolfeboro, O = Python Software Foundation, CN = Python Software Foundation
- Subject Public Key Info:
- Public Key Algorithm: rsaEncryption
- RSA Public-Key: (4096 bit)
- Modulus:
- 00:aa:bd:a4:4b:b2:75:b9:6e:e8:25:1c:65:b1:da:
- c4:4c:08:ca:6a:b7:e8:1f:94:c8:f0:f5:92:4f:a6:
- 1b:db:13:e8:a0:fe:ef:1d:3e:22:69:55:f2:2f:92:
- f3:7b:57:f9:dc:9c:3a:ed:2a:7e:bb:9d:b8:7c:95:
- df:df:4a:87:56:21:16:c6:e9:b2:d9:15:86:d2:77:
- 22:53:67:7e:98:ca:b3:8e:56:80:59:26:4d:17:4b:
- b8:45:cb:f2:0c:9a:24:11:5d:11:50:ea:88:e4:21:
- b9:cc:f2:37:5b:db:90:e8:b8:94:93:71:c2:61:6e:
- a4:a4:7d:7b:ec:0e:53:de:9c:3f:3e:8f:0e:f0:a1:
- 2b:24:69:f5:6a:76:aa:b4:82:02:ab:df:72:4b:1a:
- cc:69:df:f6:84:f3:01:45:fe:8d:75:a8:7b:7f:b1:
- cf:9f:58:24:49:24:c0:a1:e8:f2:ba:a1:79:87:e0:
- 74:a8:8e:3e:24:ae:7e:54:bb:f3:eb:9f:55:4d:b0:
- 16:26:c6:1a:92:4c:59:c5:55:98:a4:5b:f8:29:e4:
- 12:4b:0a:28:d0:3c:cc:be:61:11:b1:3c:cd:bd:50:
- 4c:5a:1b:bd:3a:b8:89:36:0f:90:7c:59:9f:f7:ac:
- d5:4e:ef:77:71:9f:ab:ef:13:29:6d:7c:9f:20:e1:
- 8a:84:73:1a:46:e6:7c:8a:1b:96:23:1d:e0:23:d5:
- 87:0c:55:fa:7c:12:91:f3:e1:e5:85:d9:1a:88:11:
- 16:22:c5:d1:a3:2f:84:41:4c:8a:ef:35:2c:f8:5a:
- 8e:a3:6b:11:62:db:5b:b3:c3:13:17:d6:03:28:56:
- 70:c8:f8:e7:f5:69:fe:80:b1:9d:e4:d5:04:57:23:
- 6f:0f:d4:15:18:11:2d:37:bb:f1:f3:b6:dd:b8:95:
- 01:f0:5e:03:ca:51:2c:32:d6:53:7e:3c:3f:6a:ee:
- 80:98:e9:e6:9d:e2:b9:51:ca:92:26:ec:11:c9:96:
- 86:36:4e:f2:de:a8:f4:ea:eb:71:f8:74:d3:a8:78:
- 22:f7:be:54:a7:17:f2:af:00:2a:92:8b:e8:64:45:
- 81:55:2a:6f:92:ef:0f:56:19:01:5d:c2:e6:35:ee:
- 8c:10:79:45:89:a3:28:88:00:c0:78:a9:97:e5:11:
- 51:90:df:95:ae:66:06:4e:0e:33:6a:3c:5f:74:77:
- 88:63:c4:ef:2d:fe:3c:b5:37:e6:9d:02:5d:f7:c8:
- 1e:25:0b:ff:d3:53:54:cb:f1:71:bb:0a:80:b9:39:
- 1a:7b:3e:4d:97:52:f1:3f:40:a9:4c:78:60:87:33:
- b8:15:10:f8:8a:d4:f6:c2:a4:e1:e2:3a:68:8f:0f:
- 50:66:7b
- Exponent: 65537 (0x10001)
- X509v3 extensions:
- X509v3 Authority Key Identifier:
- keyid:5A:C4:B9:7B:2A:0A:A3:A5:EA:71:03:C0:60:F9:2D:F6:65:75:0E:58
- X509v3 Subject Key Identifier:
- FC:2A:BF:7E:D4:BE:AC:F3:82:9C:A4:CF:7B:22:01:3B:B8:8F:07:F2
- X509v3 Key Usage: critical
- Digital Signature
- X509v3 Extended Key Usage:
- Code Signing
- X509v3 CRL Distribution Points:
- Full Name:
- URI:http://crl3.digicert.com/sha2-assured-cs-g1.crl
- Full Name:
- URI:http://crl4.digicert.com/sha2-assured-cs-g1.crl
- X509v3 Certificate Policies:
- Policy: 2.16.840.1.114412.3.1
- CPS: https://www.digicert.com/CPS
- Policy: 2.23.140.1.4.1
- Authority Information Access:
- OCSP - URI:http://ocsp.digicert.com
- CA Issuers - URI:http://cacerts.digicert.com/DigiCertSHA2AssuredIDCodeSigningCA.crt
- X509v3 Basic Constraints: critical
- CA:FALSE
- Signature Algorithm: sha256WithRSAEncryption
- 4b:75:a1:2d:b5:5f:46:b1:89:a7:cf:8f:26:3e:be:56:2a:8d:
- 62:ae:52:ef:d8:16:e6:16:20:4a:ba:89:14:5a:15:a6:cd:0e:
- 18:fd:44:11:50:17:f6:89:88:4e:66:b2:b4:04:39:f0:03:eb:
- fe:a0:55:21:fd:d6:56:56:06:a8:3a:34:47:86:f5:3f:52:5d:
- b8:80:3e:f2:7d:08:45:85:b1:d9:17:52:b8:db:5a:1b:c2:9e:
- e6:7b:92:a5:2e:53:90:40:ba:62:35:41:16:e9:62:06:4b:dd:
- 40:3e:89:95:e4:9a:36:c6:87:59:67:f2:b5:21:58:8c:5b:05:
- 82:f8:4a:0d:a7:aa:90:78:e2:9e:c2:50:56:24:3e:3f:cc:6f:
- 05:36:82:f0:55:da:95:e3:8f:95:9a:b2:4a:19:b6:c0:02:32:
- fe:60:e3:60:4d:a1:52:e8:44:08:be:7a:fe:ac:d3:b3:ce:b7:
- e2:6d:1d:12:26:f3:b9:53:ca:e7:3c:c2:1d:2c:c1:33:d4:c2:
- 4b:0a:6c:b5:35:65:d8:fd:0a:9a:ad:cd:79:ee:54:4d:30:e7:
- 47:b1:26:f4:52:2b:75:6d:e2:0f:b4:be:28:29:23:7a:8f:98:
- 37:69:91:ff:7e:e0:cb:f2:d1:73:0d:72:aa:ed:87:0d:b4:47:
- e3:e4:22:53
The part under Signature Algorithm: sha256WithRSAEncryption is the signatureValue part, copy it to any binary editor, and then save it as PythonExeSignatureValue.bin, finally use openssl to decrypt this part of the data:
- >> hexdump -C PythonExeSignatureValue.bin
- 00000000 4b 75 a1 2d b5 5f 46 b1 89 a7 cf 8f 26 3e be 56 |Ku.-._F.....&>.V|
- 00000010 2a 8d 62 ae 52 ef d8 16 e6 16 20 4a ba 89 14 5a |*.b.R..... J...Z|
- 00000020 15 a6 cd 0e 18 fd 44 11 50 17 f6 89 88 4e 66 b2 |......D.P....Nf.|
- 00000030 b4 04 39 f0 03 eb fe a0 55 21 fd d6 56 56 06 a8 |..9.....U!..VV..|
- 00000040 3a 34 47 86 f5 3f 52 5d b8 80 3e f2 7d 08 45 85 |:4G..?R]..>.}.E.|
- 00000050 b1 d9 17 52 b8 db 5a 1b c2 9e e6 7b 92 a5 2e 53 |...R..Z....{...S|
- 00000060 90 40 ba 62 35 41 16 e9 62 06 4b dd 40 3e 89 95 |[email protected].@>..|
- 00000070 e4 9a 36 c6 87 59 67 f2 b5 21 58 8c 5b 05 82 f8 |..6..Yg..!X.[...|
- 00000080 4a 0d a7 aa 90 78 e2 9e c2 50 56 24 3e 3f cc 6f |J....x...PV$>?.o|
- 00000090 05 36 82 f0 55 da 95 e3 8f 95 9a b2 4a 19 b6 c0 |.6..U.......J...|
- 000000a0 02 32 fe 60 e3 60 4d a1 52 e8 44 08 be 7a fe ac |.2.`.`M.R.D..z..|
- 000000b0 d3 b3 ce b7 e2 6d 1d 12 26 f3 b9 53 ca e7 3c c2 |.....m..&..S..<.|
- 000000c0 1d 2c c1 33 d4 c2 4b 0a 6c b5 35 65 d8 fd 0a 9a |.,.3..K.l.5e....|
- 000000d0 ad cd 79 ee 54 4d 30 e7 47 b1 26 f4 52 2b 75 6d |..y.TM0.G.&.R+um|
- 000000e0 e2 0f b4 be 28 29 23 7a 8f 98 37 69 91 ff 7e e0 |....()#z..7i..~.|
- 000000f0 cb f2 d1 73 0d 72 aa ed 87 0d b4 47 e3 e4 22 53 |...s.r.....G.."S|
- 00000100
The above is the extracted signature data, use the following command to decrypt, the decrypted file is saved to PythonExeSignatureValueDecrypted.bin
- >> openssl rsautl -inkey PythonParentPublicKey.pem -pubin -in PythonExeSignatureValue.bin >PythonExeSignatureValueDecrypted.bin
- >> hexdump -C PythonExeSignatureValueDecrypted.bin
- 00000000 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 |010...`.H.e.....|
- 00000010 00 04 20 ca 1c 82 f3 fd 76 74 30 54 39 09 8d 87 |.. .....vt0T9...|
- 00000020 95 4f e2 af b4 a0 64 24 bf 49 2f 27 34 61 46 2e |.O....d$.I/'4aF.|
- 00000030 ea d7 33 |..3|
- 00000033
The “mysterious data” decrypted above is actually a DER-encoded PKCS#9 message, which still uses ASN1 syntax, so you can continue to use openssl to parse:
- >> openssl asn1parse -i -inform DER -in PythonExeSignatureDecrypted.bin
- 0:d=0 hl=2 l= 49 cons: SEQUENCE
- 2:d=1 hl=2 l= 13 cons: SEQUENCE
- 4:d=2 hl=2 l= 9 prim: OBJECT :sha256
- 15:d=2 hl=2 l= 0 prim: NULL
- 17:d=1 hl=2 l= 32 prim: OCTET STRING [HEX DUMP]:CA1C82F3FD7674305439098D87954FE2AFB4A06424BF492F273461462EEAD733
So far, we have obtained the first hash value, which is a SHA256 value:
CA1C82F3FD7674305439098D87954FE2AFB4A06424BF492F273461462EEAD733
Next, we need to extract the tbsCertificate part of the Python.exe certificate, you can use the following openssl command to first view the structure of the Python.exe certificate (for intuitive description, the output content has been reduced):
- >> openssl asn1parse -i -inform DER -in Python.exe.cer
- 0:d=0 hl=4 l=1607 cons: SEQUENCE
- 4:d=1 hl=4 l=1327 cons: SEQUENCE
- .......
- 1335:d=1 hl=2 l= 13 cons: SEQUENCE
- 1337:d=2 hl=2 l= 9 prim: OBJECT :sha256WithRSAEncryption
- 1348:d=2 hl=2 l= 0 prim: NULL
- 1350:d=1 hl=4 l= 257 prim: BIT STRING
In the above results, the number before the colon at the beginning of each line represents the offset in the data, hl represents the header length, and l represents the actual data length. According to the ASN1 structure definition of the certificate in the above text, we need to extract 4+1327=1331 length of data from offset 4, this part of the data is the tbsCertificate part of the Python.exe certificate, use the following command to extract:
- >> dd if=./Python.exe.cer of=./PythonExetbsCertificate.bin skip=4 bs=1 count=1331
- 输入了 1331+0 块记录
- 输出了 1331+0 块记录
- 1331 字节 (1.3 kB, 1.3 KiB) 已复制,0.018058 s,73.7 kB/s
Then, we do sha256 on the extracted part of the tbsCertificate data:
- >> sha256sum PythonExetbsCertificate.bin
- ca1c82f3fd7674305439098d87954fe2afb4a06424bf492f273461462eead733 PythonExetbsCertificate.bin
As you can see, the second sha2256 value obtained is “just” equal to the first sha256 value above.
Certificate verification summary
Let’s review what we did above.
- Extracted the public key of the trusted certificate DigiCert SHA2 Assured ID Code Singing CA
- Used this public key to decrypt the signatureValue part of the Python.exe certificate, and got the first sha256 value
- Extracted the tbsCertificate part of the Python.exe certificate data, and performed sha256 operation on the extracted data, and got the second sha256 value
- The first sha256 value is equal to the second sha256
Therefore, it is concluded that the certificate used by Python.exe is legally valid and has not been modified, so the data recorded in this certificate, to be precise, the data recorded in the tbsCertificate part of this certificate, can be fully trusted. So what important data does tbsCertificate record that needs such a “great effort” to protect? The answer is: the public key of the Python.exe developer!
As we all know, RSA, as asymmetric encryption, data encrypted with the public key can only be decrypted by the private key (if you don’t know, please read the sister piece), and vice versa. That is to say, now we use the public key of the Python.exe developer to decrypt a piece of data, as long as the decryption is successful, this piece of data must be “sent” by the developer, because theoretically, the private key of Python.exe developer is only possible to be held by the developer.
So, what data should we decrypt with the public key of the Python.exe developer?
Let’s continue to explore~
PE file AuthentiCode
Before continuing, we need to supplement some theoretical knowledge.
A little knowledge of PE file structure
I guess you are no stranger to the structure of PE files, but for the completeness of this article, I still have to say it briefly, if you are familiar with it, you can skip it directly.
Here is a diagram of the PE file structure [reference 5]:

We can start with the DOS Header, find the PE Header, and then the IMAGE_DATA_DIRECTORY structure in the Optional Header. The data involved in the PE signature is in the 5th item (index 4) of IMAGE_DATA_DIRECTORY, which records the offset and length of SecurityDirectory. Using the PE file structure analysis tool, you can easily locate this piece of data:
As can be seen from the above figure, this segment of data is located at RawOffset 0x16E00, the size is 0x1A10, this segment of data is defined as follows:
- typedef struct _WIN_CERTIFICATE
- {
- DWORD dwLength;
- WORD wRevision;
- WORD wCertificateType;
- BYTE bCertificate[ANYSIZE_ARRAY];
- } WIN_CERTIFICATE, *LPWIN_CERTIFICATE;
It can be seen that it is only 2 DWORDs offset from 0x16E00 to the real PKCS#7 format data. You can use the following command to extract the PKCS#7 data stored in SecurityDirectory:
- >> dd if=./python.exe of=./PythonPkcs7Data.bin skip=93704 bs=1 count=6664
- 输入了 6664+0 块记录
- 输出了 6664+0 块记录
- 6664 字节 (6.7 kB, 6.5 KiB) 已复制,0.0351038 s,190 kB/s
The data analyzed in the following text is all extracted from here.
With the basic knowledge of the above PE structure, let’s take a look at more in-depth content, first borrow a picture from the Microsoft document:

The left half of the above figure is basically the PE structure mentioned in the above text. The right half of the figure, Microsoft gives a rough structure introduction to the digital signature data part of the PKCS#7 format. In fact, the data structure of this part is far more complicated than the above figure, but so far, you only need to know a basic structure.
The right half of the above figure, the actual structure corresponds to SignedData, expand to see this structure.
Microsoft’s definition of SignedData
Definition of SignedData
The data mentioned above that is finally extracted is in the SignedData format, which is also data that conforms to the PKCS#7 standard and satisfies the following ASN.1 description format:
- SignedData ::= SEQUENCE {
- version Version,
- digestAlgorithms DigestAlgorithmIdentifiers,
- contentInfo ContentInfo,
- certificates
- [0] IMPLICIT ExtendedCertificatesAndCertificates
- OPTIONAL,
- Crls
- [1] IMPLICIT CertificateRevocationLists OPTIONAL,
- signerInfos SignerInfos
- }
- DigestAlgorithmIdentifiers ::=
- SET OF DigestAlgorithmIdentifier
- ContentInfo ::= SEQUENCE {
- contentType ContentType,
- content
- [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL
- }
- ContentType ::= OBJECT IDENTIFIER
- SignerInfos ::= SET OF SignerInfo
The contentInfo will store the AuthentiCode information of the PE file. In this structure, we need to pay attention to the data stored in the content field, please see below for details.
The certificates will store information such as the certificate of the PE file developer, and we can extract the developer’s public key from here. The certificate part has been detailed in the above text, and it will not be repeated here.
The signerInfos stores data encrypted with the private key of the PE file developer and some data used for verifying other information (optional, not necessarily).
Definition of SignerInfo
SignerInfo's ASN.1 syntax definition is as follows:
- SignerInfo ::= SEQUENCE {
- version Version,
- issuerAndSerialNumber IssuerAndSerialNumber,
- digestAlgorithm DigestAlgorithmIdentifier,
- authenticatedAttributes
- [0] IMPLICIT Attributes OPTIONAL,
- digestEncryptionAlgorithm
- DigestEncryptionAlgorithmIdentifier,
- encryptedDigest EncryptedDigest,
- unauthenticatedAttributes
- [1] IMPLICIT Attributes OPTIONAL
- }
- IssuerAndSerialNumber ::= SEQUENCE {
- issuer Name,
- serialNumber CertificateSerialNumber
- }
- EncryptedDigest ::= OCTET STRING
In this definition, what we are most concerned about is the encryptedDigest field. According to the definition of the Microsoft document, this field records a piece of data encrypted by the software issuer’s private key:
By analyzing the exported PKCS#7 binary data, the offset of this data is finally located at: 0x15f6 (you need to skip the 4-byte header), the size is 0x0200:

After copying this part of the binary data, use the public key extracted from the Python.exe certificate to decrypt:
- // Extract public key of Python.exe into PythonExePublicKey.pem
- >> openssl x509 -in Python.exe.cer -inform DER -noout -pubkey > PythonExePublicKey.pem
- // Dump data of SignedData.singerInfo.encryptDigest
- >> hexdump -C PythonPkcs7Encrypted.bin
- 00000000 12 37 05 82 c9 7f 73 e9 fc ff d0 71 78 f8 50 04 |.7....s....qx.P.|
- 00000010 18 c4 2d ae 0b 4f da 5f 6a db 88 79 44 bc 80 cd |..-..O._j..yD...|
- 00000020 9d 8d 86 51 48 5d 83 38 d2 1f 5d 60 ed a6 09 6e |...QH].8..]`...n|
- 00000030 61 1d 55 e6 0b df ab 34 01 21 e1 c3 17 5d 2f d7 |a.U....4.!...]/.|
- 00000040 f6 e0 cd 18 1b 88 bb c9 f9 0f 2e b9 3b ea 34 28 |............;.4(|
- 00000050 0e 4d 99 01 1b 33 66 59 d4 dd fa e0 4a 81 36 35 |.M...3fY....J.65|
- 00000060 8f 6d b1 9e 76 f7 eb c9 5a 31 a4 0f 4f 32 28 be |.m..v...Z1..O2(.|
- 00000070 a7 d9 7a 01 c9 d7 fa 22 06 10 76 06 55 57 42 ce |..z...."..v.UWB.|
- 00000080 f5 9c 7e 36 ff 24 cc 0f 5d d1 b2 00 d2 e6 da 47 |..~6.$..]......G|
- 00000090 03 c0 06 f1 41 cb 2a f3 7f cc 69 1c f1 ea 53 de |....A.*...i...S.|
- 000000a0 ab ca 25 a3 db 53 6e 0e 06 ff 37 b6 71 a9 5b 6b |..%..Sn...7.q.[k|
- 000000b0 85 d8 d7 b0 19 4e 1f 56 a2 5f b9 4c c4 4a 1b 18 |.....N.V._.L.J..|
- 000000c0 4f 30 86 95 90 e6 b9 23 6d 74 9e 76 d3 7e 5f ad |O0.....#mt.v.~_.|
- 000000d0 20 8e ae 02 d6 32 f3 a7 be ba 00 6a 30 90 8d e4 | ....2.....j0...|
- 000000e0 83 d5 02 bb 19 e6 eb 62 a0 55 c9 4b 93 59 3b 12 |.......b.U.K.Y;.|
- 000000f0 51 ef 7e 50 b3 f3 0b 50 3c e5 01 9a ef 6a 9a 9b |Q.~P...P<....j..|
- 00000100 1a 2b a4 ef 79 47 09 1a d8 48 28 e8 2c 52 3e 29 |.+..yG...H(.,R>)|
- 00000110 ad 31 05 2b 24 e6 11 e1 c0 bb 11 1d d0 0f e0 78 |.1.+$..........x|
- 00000120 89 cf dc 85 e5 52 21 1c 43 69 b5 40 17 dc 08 98 |.....R!.Ci.@....|
- 00000130 8e fe d6 36 d4 6b ce ef 24 06 34 09 eb 7f 1b 9d |...6.k..$.4.....|
- 00000140 24 04 e2 e6 cb de b9 6d 14 70 3a b2 50 82 f9 83 |$......m.p:.P...|
- 00000150 37 b8 b8 ee 70 08 ce 6e 94 53 0e c4 0a 8b 5b d9 |7...p..n.S....[.|
- 00000160 98 b5 54 2f 94 bd 46 20 9f a7 38 02 86 ef d8 b5 |..T/..F ..8.....|
- 00000170 64 a2 ea 2f 0b 79 fb d3 e0 5c a3 83 8f 94 54 56 |d../.y...\....TV|
- 00000180 51 16 06 f7 fe ba 93 e2 b2 7d 08 74 08 a8 55 84 |Q........}.t..U.|
- 00000190 11 09 7d 74 4b 6b 48 2f 4f 98 4b dd 19 5d f2 db |..}tKkH/O.K..]..|
- 000001a0 18 40 d9 d1 a7 52 c3 35 7e 9a 5e d5 72 62 f2 64 |[email protected]~.^.rb.d|
- 000001b0 f4 cd 3f 70 d6 de e8 27 a8 cd 06 30 40 58 80 31 |..?p...'[email protected]|
- 000001c0 6a 44 7b 22 bd 4f 1b d0 1d 9e a5 b1 26 60 d5 e4 |jD{".O......&`..|
- 000001d0 10 8e c5 67 4d 1d d2 51 b0 29 bb f5 f2 0f 24 e6 |...gM..Q.)....$.|
- 000001e0 96 49 36 99 01 e2 56 aa 32 5f 13 c9 bd a0 2c dd |.I6...V.2_....,.|
- 000001f0 06 db dc ae ee ca 28 9e 0c e8 98 68 2d e8 d0 a8 |......(....h-...|
- 00000200
- // Decrypt PythonPkcs7Encrypted.bin with public key, which is stored in PythonExePulbicKey.pem, into PythonPkcs7Decrypted.bin
- >> openssl rsautl -inkey PythonExePublicKey.pem -pubin -in PythonPkcs7Encrypted.bin > PythonPkcs7Decrypted.bin
- // Parse decryptedPKCS#9 data (described by ASN1 syntax)
- >> openssl asn1parse -i -inform DER -in PythonPkcs7Decrypted.bin
- 0:d=0 hl=2 l= 49 cons: SEQUENCE
- 2:d=1 hl=2 l= 13 cons: SEQUENCE
- 4:d=2 hl=2 l= 9 prim: OBJECT :sha256
- 15:d=2 hl=2 l= 0 prim: NULL
- 17:d=1 hl=2 l= 32 prim: OCTET STRING [HEX DUMP]:B300E437486CB53B647A9C1A84B2986502254681C688020158504646776C31E1
At this point, we have once again obtained a mysterious sha256 hash value:
B300E437486CB53B647A9C1A84B2986502254681C688020158504646776C31E1
Question is, what data is this hash value from?
There are two situations here: the SingedData.signerInfos.authenticatedAttributes field does not exist and the aforementioned field exists. Among them, the second situation has a huge pit, as for what pit, it will be mentioned later.
Non-existent authenticatedAttributes field
In this case, the hash value decrypted with the public key above should be equal to the value calculated by the same hash algorithm in the ContentInfo field.
Let’s do some practical operations.
First, determine the binary data to be extracted, offset: 0x3d (note to skip the 2-byte header) length: 0x4c:
Verify the hash value of the extracted data:
- >> hexdump -C PythonExeContentInfo.bin
- 00000000 30 17 06 0a 2b 06 01 04 01 82 37 02 01 0f 30 09 |0...+.....7...0.|
- 00000010 03 01 00 a0 04 a2 02 80 00 30 31 30 0d 06 09 60 |.........010...`|
- 00000020 86 48 01 65 03 04 02 01 05 00 04 20 5a 3a d4 3d |.H.e....... Z:.=|
- 00000030 81 d5 6a 86 d9 7b 24 b3 74 da 49 44 aa ee 94 c7 |..j..{$.t.ID....|
- 00000040 10 e3 77 25 4c fe 8e cc 72 04 06 6a |..w%L...r..j|
- 0000004c
- >> sha256sum PythonExeContentInfo.bin
- b5e4b32d6ebae47869646ccf93416b320a113b2d35d948e7e2a9122146cb9634 PythonContentInfo.bin
It seems…the hash value is not the same as the one decrypted above…did I make a mistake? Actually not, the hash value here is incorrect because this example is the second situation - it has an authenticatedAttributes field, it is written here just as an example, so that you know how to operate when you encounter a non-authenticatedAttributes field.
But this sha256 value is still useful, you need to pay attention to it, it will be used in the following text.
Existence of authenticatedAttributes field
Looking at the picture below, the authenticatedAttributes field is located at the offset: 0x1548 (this time there is no need to skip the two-byte header, pit 1), length: 0x9a, dump this part of the data and save it as PythonExeAttribute.bin.