How is the CSR signature constructed?

2019-06-01 12:15发布

I am trying to generate a CSR (Certificate Signing Request) in an embedded device. I have implemented some OpenSSL functions in one embedded device. Unfortunately I only have a few functions available. So far I've been able to generate an RSA private key and now I need to generate the CSR.

My library does NOT have any of the functions such as X509_REQ_new(), X509_REQ_get_subject_name() etc. Therefore I'm building the CSR by means of creating a DER file directly.

The CSR in DER format is decoded as follows:

  1. ASN1 Sequence
  2. Version
  3. Subject(s) name(s)
  4. Type of encryption
  5. Modulus & Exponent (from my RSA Key)
  6. Attributes
  7. Signature (here is where my problem lies)

If I had all the OpenSSL functions available I would use X509_REQ_sign(x509_req, pKey, EVP_sha1()), but as I said before I don't have any of the X509 objects available.

What do I need to sign in order to build that part?

1条回答
做自己的国王
2楼-- · 2019-06-01 12:34

By 'implemented' do you mean you coded them yourself, or you selected chunks out of the OpenSSL source? In the latter case I assume you have the full source available to look at even if it isn't compiled on your limited device.

CSRs, like (X.509) certs and CRLs, use a common top-level structure:

SEQUENCE
  body (itself a SEQUENCE of various things depending on what this thing is)
  AlgorithmIdentifier = SEQUENCE { OID, NULL } identifying the algorithm
  BIT STRING containing signature in algorithm-dependent format

Note that AlgId and signature are separate items and outside the SEQUENCE in the body, though inside the top-level SEQUENCE.

For CSR this is laid out in PKCS#10, more stably available as RFC2986; see section 4.

In particular I hope your "4.- Type of encryption" is really an AlgorithmIdentifier for the algorithm of the publickey which for RSA for historical reasons is the OID rsaEncryption = 1.2.840.113549.1.1.1 even though RSA is used for signature as well as encryption and "5.- Modulus & Exponent" is really a BIT STRING containing the DER encoding of a PKCS#1 public key which is a SEQUENCE containing those two INTEGERs, and that 4 and 5 are grouped into a SEQUENCE to form SubjectPublicKeyInfo.

As described in section 4.2, the 'body' part (certificationRequestInfo) is DER-encoded, and that encoding is signed by your privatekey -- for RSA in practice using PKCS1-v1_5, although there are other RSA signature schemes defined. That signature scheme produces an octet string (in the generic sense, not the ASN.1 type) which is used as the value in the BIT STRING.

Depending on what primitives you are using, remember that PKCS1-v1_5 requires you to hash the message (here certificationRequestInfo), encode that hash in an ASN.1 SEQUENCE with an AlgId for the hash, pad that ASN.1 with 01 FF... 00, and modexp (OS2I of) the result with d. In OpenSSL EVP_DigestSign* does all 4 steps, EVP_PKEY_sign* does the last 3, and RSA_private_encrypt [sic] does the last 2 i.e. does NOT do the ASN.1 (and optionally doesn't do the padding either).


Your comments are too vague for me to be clear what you are doing, so here is a VALID EXAMPLE:

$ openssl req -newkey rsa:2048 -nodes -keyout SO39842014.key \
  -subj '/CN=example.com/C=AU/O=Widgits Pty' -outform d -out SO39842014.req
Generating a 2048 bit RSA private key
.............+++
..+++
writing new private key to 'SO39842014.key'
-----
$ # -nodes to simplify example; real keys should be secured!
NOTE: this example done in 1.0.1 which defaults signing hash SHA1
  unless set in the config file which my system's file didn't;
  1.0.2 up defaults to SHA256 unless config, 
  or you can override it with e.g. -sha256 on req,
  in which case must change the dgst's below to match
  (and the expected recovery value ditto) -- thanks @foxiris
$ openssl rsa -in SO39842014.key -pubout -out SO39842014.pub
writing RSA key
$ 
$ xxd SO39842014.req
0000000: 3082 027e 3082 0166 0201 0030 3931 1430  0..~0..f...091.0
0000010: 1206 0355 0403 0c0b 6578 616d 706c 652e  ...U....example.
0000020: 636f 6d31 0b30 0906 0355 0406 1302 4155  com1.0...U....AU
0000030: 3114 3012 0603 5504 0a0c 0b57 6964 6769  1.0...U....Widgi
0000040: 7473 2050 7479 3082 0122 300d 0609 2a86  ts Pty0.."0...*.
0000050: 4886 f70d 0101 0105 0003 8201 0f00 3082  H.............0.
0000060: 010a 0282 0101 00fa 06d2 819f be88 270e  ..............'.
0000070: 5cc8 8938 aa2d c87c 01e5 bbbd d4c7 e9b6  \..8.-.|........
0000080: 1401 65b3 adf1 52fd 671b 7fa4 6a26 7f36  ..e...R.g...j&.6
0000090: ca1c aec5 877e 2954 1541 0a88 366a 6b52  .....~)T.A..6jkR
00000a0: b3f1 4303 3848 5b05 2b58 e299 c0fd 5a65  ..C.8H[.+X....Ze
00000b0: 4748 4d43 9e5a 26b3 382e 9600 3ce3 4eb6  GHMC.Z&.8...<.N.
00000c0: 09a8 8554 25b4 3ad3 abee 49c8 d15d 27c5  ...T%.:...I..]'.
00000d0: aa9e ff56 f511 a1b6 daaa d484 86ce c0fd  ...V............
00000e0: e4bf 7435 69ed 2806 2adb 9674 a06c 21d9  ..t5i.(.*..t.l!.
00000f0: c64c 0741 88d1 2f4a d2ec b7ec e24c 2cad  .L.A../J.....L,.
0000100: 0dc4 8deb 7fc6 3c9d 3eee a5f6 4aeb 9bd0  ......<.>...J...
0000110: b2b8 0e48 5baf d272 8628 1d79 a05d e632  ...H[..r.(.y.].2
0000120: befa 9f3e eac5 fafb eae1 a0c9 c9a7 3a69  ...>..........:i
0000130: 4ec5 733e 63fc 47d8 bbcc 1c07 1f22 e190  N.s>c.G......"..
0000140: 1ac7 e6d4 f71f 9c4b 1d1b 6cb9 303a b05a  .......K..l.0:.Z
0000150: 4d4f c1e1 8c0e 2b09 5f39 2058 89f7 b2ee  MO....+._9 X....
0000160: 9e2c 6b5d 6b2c 2d02 0301 0001 a000 300d  .,k]k,-.......0.
0000170: 0609 2a86 4886 f70d 0101 0505 0003 8201  ..*.H...........
0000180: 0100 6a7e 8afc d5b1 f55c a824 3516 44f2  ..j~.....\.$5.D.
0000190: ec60 d081 2334 eaae 6d8d 8f79 7e18 bbea  .`..#4..m..y~...
00001a0: c932 bd07 1dc0 67ad 636b b552 619e 9d7f  .2....g.ck.Ra...
00001b0: 5291 3829 0649 e64a 07b6 d659 e181 127e  R.8).I.J...Y...~
00001c0: a56f 3ab9 7dad 9f53 adf9 7c86 3035 ae1c  .o:.}..S..|.05..
00001d0: 9b20 7509 3618 c71e 8f47 98da 4f03 c377  . u.6....G..O..w
00001e0: 52a7 c56e 0a58 265b be81 d263 a9f3 e2c8  R..n.X&[...c....
00001f0: d465 fab6 46a2 269a d649 be08 5857 4a40  .e..F.&..I..XWJ@
0000200: 4cd0 0ddf 17a3 a605 c2a3 a2ae 1c1f c22a  L..............*
0000210: 4aa5 1e1b 6b8a dfd4 708c 55f1 05c5 8309  J...k...p.U.....
0000220: fd62 78d4 e650 367c bf91 2c18 3ccb f6f4  .bx..P6|..,.<...
0000230: c52f 0770 3a44 9558 709f ebba 8878 3fce  ./.p:D.Xp....x?.
0000240: 4588 a7bb d605 8c46 c80c 3b11 8420 c8bd  E......F..;.. ..
0000250: 623c 8205 7d25 1588 017f 2e1b bf09 881f  b<..}%..........
0000260: a56b 5d0f 617a 9914 611d 2336 8335 2f74  .k].az..a.#6.5/t
0000270: 42f9 7188 4b67 7c65 5d83 bc51 52af 124a  B.q.Kg|e]..QR..J
0000280: d426                                     .&

This consists of the outer SEQUENCE tag & length, then the TBS or body (certificationRequestInfo) at 4 up to 0x16E which is this DER encoding:

$ dd if=SO39842014.req bs=1 skip=4 count=$((0x16E-4)) 2>/dev/null \
  | openssl asn1parse -inform d -i
    0:d=0  hl=4 l= 358 cons: SEQUENCE
    4:d=1  hl=2 l=   1 prim:  INTEGER           :00
    7:d=1  hl=2 l=  57 cons:  SEQUENCE
    9:d=2  hl=2 l=  20 cons:   SET
   11:d=3  hl=2 l=  18 cons:    SEQUENCE
   13:d=4  hl=2 l=   3 prim:     OBJECT            :commonName
   18:d=4  hl=2 l=  11 prim:     UTF8STRING        :example.com
   31:d=2  hl=2 l=  11 cons:   SET
   33:d=3  hl=2 l=   9 cons:    SEQUENCE
   35:d=4  hl=2 l=   3 prim:     OBJECT            :countryName
   40:d=4  hl=2 l=   2 prim:     PRINTABLESTRING   :AU
   44:d=2  hl=2 l=  20 cons:   SET
   46:d=3  hl=2 l=  18 cons:    SEQUENCE
   48:d=4  hl=2 l=   3 prim:     OBJECT            :organizationName
   53:d=4  hl=2 l=  11 prim:     UTF8STRING        :Widgits Pty
   66:d=1  hl=4 l= 290 cons:  SEQUENCE
   70:d=2  hl=2 l=  13 cons:   SEQUENCE
   72:d=3  hl=2 l=   9 prim:    OBJECT            :rsaEncryption
   83:d=3  hl=2 l=   0 prim:    NULL
   85:d=2  hl=4 l= 271 prim:   BIT STRING
  360:d=1  hl=2 l=   0 cons:  cont [ 0 ]

followed by the AlgId for the signature (aka the 'outer' AlgId) at 0x16E up to 0x17D:

$ dd if=SO39842014.req bs=1 skip=$((0x16E)) count=$((0xF)) 2>/dev/null \
  | openssl asn1parse -inform d -i
    0:d=0  hl=2 l=  13 cons: SEQUENCE
    2:d=1  hl=2 l=   9 prim:  OBJECT            :sha1WithRSAEncryption
   13:d=1  hl=2 l=   0 prim:  NULL

followed by a BIT STRING containing the signature value from 0x182 up to 0x282. This can be verified as the signature of the TBS:

$ dd if=SO39842014.req bs=1 skip=$((0x282)) of=SO39842014.sig 2>/dev/null 
$ dd if=SO39842014.req bs=1 skip=4 count=$((0x16E-4)) 2>/dev/null \
  | openssl dgst -sha1 -verify SO39842014.pub -signature SO39842014.sig
Verified OK

or since PKCS1-v1_5 signature is deterministic we can recreate the same signature:

$ dd if=SO39842014.req bs=1 skip=4 count=$((0x16E-4)) 2>/dev/null \
 | openssl dgst -sha1 -sign SO39842014.key | xxd
0000000: 6a7e 8afc d5b1 f55c a824 3516 44f2 ec60  j~.....\.$5.D..`
0000010: d081 2334 eaae 6d8d 8f79 7e18 bbea c932  ..#4..m..y~....2
0000020: bd07 1dc0 67ad 636b b552 619e 9d7f 5291  ....g.ck.Ra...R.
0000030: 3829 0649 e64a 07b6 d659 e181 127e a56f  8).I.J...Y...~.o
0000040: 3ab9 7dad 9f53 adf9 7c86 3035 ae1c 9b20  :.}..S..|.05...
0000050: 7509 3618 c71e 8f47 98da 4f03 c377 52a7  u.6....G..O..wR.
0000060: c56e 0a58 265b be81 d263 a9f3 e2c8 d465  .n.X&[...c.....e
0000070: fab6 46a2 269a d649 be08 5857 4a40 4cd0  ..F.&..I..XWJ@L.
0000080: 0ddf 17a3 a605 c2a3 a2ae 1c1f c22a 4aa5  .............*J.
0000090: 1e1b 6b8a dfd4 708c 55f1 05c5 8309 fd62  ..k...p.U......b
00000a0: 78d4 e650 367c bf91 2c18 3ccb f6f4 c52f  x..P6|..,.<..../
00000b0: 0770 3a44 9558 709f ebba 8878 3fce 4588  .p:D.Xp....x?.E.
00000c0: a7bb d605 8c46 c80c 3b11 8420 c8bd 623c  .....F..;.. ..b<
00000d0: 8205 7d25 1588 017f 2e1b bf09 881f a56b  ..}%...........k
00000e0: 5d0f 617a 9914 611d 2336 8335 2f74 42f9  ].az..a.#6.5/tB.
00000f0: 7188 4b67 7c65 5d83 bc51 52af 124a d426  q.Kg|e]..QR..J.&

Finally, since this (like most) RSA signature allows 'recovery' (sometimes misleadingly called 'decrypt with public key') we can look at the value actually input to the modexp-d:

$ openssl rsautl -in SO39842014.sig -verify -inkey SO39842014.pub -pubin -raw |xxd
0000000: 0001 ffff ffff ffff ffff ffff ffff ffff  ................
0000010: ffff ffff ffff ffff ffff ffff ffff ffff  ................
0000020: ffff ffff ffff ffff ffff ffff ffff ffff  ................
0000030: ffff ffff ffff ffff ffff ffff ffff ffff  ................
0000040: ffff ffff ffff ffff ffff ffff ffff ffff  ................
0000050: ffff ffff ffff ffff ffff ffff ffff ffff  ................
0000060: ffff ffff ffff ffff ffff ffff ffff ffff  ................
0000070: ffff ffff ffff ffff ffff ffff ffff ffff  ................
0000080: ffff ffff ffff ffff ffff ffff ffff ffff  ................
0000090: ffff ffff ffff ffff ffff ffff ffff ffff  ................
00000a0: ffff ffff ffff ffff ffff ffff ffff ffff  ................
00000b0: ffff ffff ffff ffff ffff ffff ffff ffff  ................
00000c0: ffff ffff ffff ffff ffff ffff ffff ffff  ................
00000d0: ffff ffff ffff ffff ffff ffff 0030 2130  .............0!0
00000e0: 0906 052b 0e03 021a 0500 0414 e577 fca2  ...+.........w..
00000f0: 2372 a03a a4fe a0d1 485d 7f71 1321 37c4  #r.:....H].q.!7.

and see this consists of type-01 padding of an ASN.1 SEQUENCE containing an AlgId for SHA1 plus an OCTET STRING containing the actual hash:

$ if=SO39842014.req bs=1 skip=4 count=$((0x16E-4)) 2>/dev/null \
  | openssl dgst -sha1
(stdin)= e577fca22372a03aa4fea0d1485d7f71132137c4
查看更多
登录 后发表回答