Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to manually calculate and verify the signature of a certificate signing request

  • I have created a keypair

openssl genrsa -out test1 2048

  • then created a certificate signing request using that key

openssl req -new -key test1 -subj "/CN=foo" -out foo.csr

  • verifying that certificate using openssl req -in foo.csr -text
    • It Contains the public key of the keypair i have generated

            Public Key Algorithm: rsaEncryption
            Public-Key: (2048 bit)
            Modulus:
                00:ca:c5:29:98:08:05:30:30:03:08:eb:23:c2:af:
                3e:2e:2d:dc:11:96:cb:2f:d1:1f:7f:41:a4:00:13:
                8a:ee:4b:36:5b:f2:c1:d1:0f:8b:27:11:34:08:bd:
                4d:df:7e:6d:7a:d7:f9:dd:ea:62:ad:fa:8f:8c:eb:
                47:5f:55:82:2c:13:c2:11:41:12:b9:87:0b:3d:08:
                86:1b:ad:71:16:89:1c:fa:07:4a:86:8f:80:a9:99:
                37:f7:e2:d4:d3:d8:b2:5f:7f:c9:05:51:73:f0:c8:
                59:ec:c3:09:a2:03:a5:6e:ec:8b:d9:9c:11:de:d3:
                df:55:a5:3f:0c:36:d6:93:8a:70:a0:b9:61:cd:c9:
                4a:09:ad:f7:3e:fd:ce:6f:5c:bb:00:69:e9:3b:3d:
                85:3b:01:1d:8f:6a:a7:d4:61:f9:b5:07:1e:90:ed:
                ab:3b:41:cc:db:e8:a0:e7:88:b7:77:35:66:30:b7:
                a6:cd:ea:d6:12:f5:ef:82:63:e9:46:29:2e:7c:10:
                0e:32:fd:04:2d:cd:62:0e:4b:74:46:f7:fd:f6:4a:
                8d:fb:82:9d:37:11:50:ea:9f:f0:d6:64:2b:50:a4:
                f0:18:6e:81:28:11:04:db:2a:0a:f7:b1:70:c5:78:
                fe:ed:e3:55:2c:64:f4:a5:a0:96:f5:11:3a:27:2c:
                5a:51
            Exponent: 65537 (0x10001)
      
    • Subject attributes i have provided while creating the CSR

              Subject: CN=foo
      
    • Digital Signature Information

      Signature Algorithm: sha256WithRSAEncryption 92:b0:82:a5:aa:98:4a:62:5a:84:8a:15:5c:6f:48:dc:e3:ec: 7f:d5:04:e8:c1:47:55:3c:b3:57:84:16:ff:5a:0d:29:2c:16: f2:cc:0c:18:c3:1f:d5:e1:57:3a:dd:8b:b1:c6:92:c3:fe:cb: 2b:9d:7d:79:d5:64:eb:31:00:8b:5e:77:48:ce:66:6f:dd:7b: 71:41:f9:aa:6e:ea:ea:59:e0:cd:f8:db:a9:13:18:d2:2a:fc: 12:25:b3:01:44:0c:b1:02:f7:1a:0f:d0:07:04:1d:9f:6f:a1: 58:91:87:25:4a:d6:47:a6:b5:4e:3c:a1:fd:b6:6c:c3:96:16: c1:ab:00:d2:4c:95:ee:2c:01:2d:cf:0e:d0:62:1b:4f:0e:34: e3:e8:85:50:63:74:eb:1f:ac:95:30:d4:df:43:7f:58:11:90: 35:29:9d:85:94:dc:c8:c2:29:81:46:71:20:62:9c:9c:f8:ef: ed:bc:8b:e3:d5:41:b3:14:f7:43:c6:b2:74:c2:22:06:a2:af: 88:68:2e:67:c4:de:ed:61:37:41:d6:df:8a:76:7d:42:5d:98: d3:c9:19:8b:1d:26:73:92:95:0f:ba:c1:78:3a:55:87:e0:3e: 16:13:34:6e:21:13:b9:da:b8:66:f3:0a:ec:79:ae:1a:95:6c: 04:cf:b8:b5

Following are my doubts :

  1. what all data fields it has considered to calculate the signature ?
  2. can we manually create the signature and verify it with the one mentioned in CSR ?
like image 600
confused genius Avatar asked Oct 18 '25 22:10

confused genius


1 Answers

The data over which the signature is calculated is pretty much everything in the CSR except for the signature algorithm and signature. This is called the certificationRequestInfo in RFC 2986.

The second question: how do we verify the signature?

The short answer: have openssl do it for you.

The easiest way by far is to ask openssl itself to verify it:

$ openssl genrsa -out test.key 2048
$ openssl req -new -key test.key -subj "/CN=foo" -out foo.csr
$ openssl req -in foo.csr -verify -noout
verify OK

Done!


The long (and tedious) answer: do it manually

Ok, so you really want to do it manually. Let's give it a shot.

Given the above, we first need to extract the desired information from the CSR. Dumping the ASN.1 structure, we get:

$ openssl asn1parse -i -in foo.csr 
    0:d=0  hl=4 l= 595 cons: SEQUENCE          
    4:d=1  hl=4 l= 315 cons:  SEQUENCE          
    8:d=2  hl=2 l=   1 prim:   INTEGER           :00
   11:d=2  hl=2 l=  14 cons:   SEQUENCE          
   13:d=3  hl=2 l=  12 cons:    SET               
   15:d=4  hl=2 l=  10 cons:     SEQUENCE          
   17:d=5  hl=2 l=   3 prim:      OBJECT            :commonName
   22:d=5  hl=2 l=   3 prim:      UTF8STRING        :foo
   27:d=2  hl=4 l= 290 cons:   SEQUENCE          
   31:d=3  hl=2 l=  13 cons:    SEQUENCE          
   33:d=4  hl=2 l=   9 prim:     OBJECT            :rsaEncryption
   44:d=4  hl=2 l=   0 prim:     NULL              
   46:d=3  hl=4 l= 271 prim:    BIT STRING        
  321:d=2  hl=2 l=   0 cons:   cont [ 0 ]        
  323:d=1  hl=2 l=  13 cons:  SEQUENCE          
  325:d=2  hl=2 l=   9 prim:   OBJECT            :sha256WithRSAEncryption
  336:d=2  hl=2 l=   0 prim:   NULL              
  338:d=1  hl=4 l= 257 prim:  BIT STRING        

This is a structured CertificationRequest defined by RFC 2986 as:

CertificationRequest ::= SEQUENCE {
    certificationRequestInfo CertificationRequestInfo,
    signatureAlgorithm       AlgorithmIdentifier{{ SignatureAlgorithms }},
    signature                BIT STRING
}

The certificationRequestInfo (see RFC for structure details) in DER-encoded ASN.1 format is signed using the algorithm described in signatureAlgorithm and the private key to yield signature.


Let's extract all the parts we need from the CSR. The strparse value is the offset you want to export, this is the first number on each line in the output above.

# Extract the certificationRequestInfo (data to be signed)
$ openssl asn1parse -in foo.csr -strparse 4 -out info.der

# Extract the public key.
$ openssl req -pubkey -in foo.csr -noout -out pub.pem

# Alternatively, you can use:
$ openssl asn1parse -in foo.csr -strparse 27 -out tmp.der
$ openssl rsa -pubin -inform DER -in tmp.der -out pub.pem

# Extract the raw signature bytes:
$ openssl asn1parse -in foo.csr -strparse 338 -out sig.raw
    0:d=0  hl=2 l=  70 cons: cont [ 3 ]        
Error in encoding
139935063934272:error:0D07209B:asn1 encoding routines:ASN1_get_object:too long:../crypto/asn1/asn1_lib.c:91:

Ignore the error on the last one, this is because the extracted data is the raw signature bytes, it's not ASN.1 encoded. openssl still happily wrote it out to the file.

We now have the following files:

  • info.der: the DER-encoded data that was signed
  • pub.pem: the CSR requester's public key
  • sig.raw: the signature included in the CSR

Let's verify the RSA signature (because that's what the signature algorithm says) using the public key and extract the original hash:

$ openssl rsautl -verify -pubin -inkey pub.pem -in sig.raw -out hash.der
$ openssl asn1parse -i -in hash.der -inform DER
    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]:192E0909DABC7454006628AA3F7FB009AFA62A17A44908CAE5E166E528DCDD11

It didn't fail, so we already know the public key is the one that matches the private key used to sign the data.

The last section, the long OCTET STRING is the raw hash of the message as computed by the CSR requester:

192e0909dabc7454006628aa3f7fb009afa62a17a44908cae5e166e528dcdd11

Let's compute the sha256 hash (once again: because the signature algorithm tells us to) of the certificationRequestInfo:

$ sha256sum info.der 
192e0909dabc7454006628aa3f7fb009afa62a17a44908cae5e166e528dcdd11  info.der

Yay! The hash is equal to the one extracted from the signature.


The hashes match and the signature was signed by the private key corresponding to the public key listed in the CSR. This is a valid CSR.

Done! See, I said it would be tedious.

like image 58
Marc Avatar answered Oct 22 '25 04:10

Marc



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!