5

I'd like to change the CSR before it is transferred to the CA without going through the CSR creation process again. To do that I need to know how to calculate the signature of the CSR after I modified it. Unfortunately I couldn't find out exactly how many bytes of the CSR are hashed.

I created the CSR like this:

openssl req -out certrequest.csr -new -newkey rsa:2048 -nodes -keyout private.key

After

sed -e '1d' -e '$d' certrequest.csr | openssl enc -base64 -d > certrequest-stripped

the last 256 bytes (sha256WithRSAEncryption) of certrequest-stripped are signature1. Now I'd like to know how many of the first bytes of certrequest-stripped I have the hash in order to sign the hash using the private key.

In other words, I'm looking for the value of the "?" in the following command so that both signatures (signature1 and signature2) match:

head -c <?> certrequest-stripped | openssl dgst -sha256 > hash
openssl rsautl -sign -inkey private.key -keyform PEM -in hash > signature2

I know that the cut has to be somewhere behind the ASN1 field for the exponent, but since my attempt to increase the head of the CSR byte by byte failed, I'm worried I'm missing something fundamental here.

jhscheer
  • 201
  • 2
  • 6
  • See [RFC2986](http://tools.ietf.org/html/rfc2986) (§4.1). Signature is done over the DER binary data (not PEM), and the field sizes vary by content due to 0-padding of some integer values. The digest is not used as-is though. If your real intention is to skip key generation, you can just omit `-new` and `-newkey` and use `-key` to create any number of CSRs from an existing key. – mr.spuratic May 26 '14 at 09:42
  • No, I wouldn't go through all this if my real intention would've been to just skip generating new keys. – jhscheer May 27 '14 at 18:47

3 Answers3

5

I figured out the answer to my question and post it here for reference.

Generate fresh key pair and CSR

openssl req -out csr.der -new -newkey rsa:2048 -nodes -keyout privateKey.der -outform DER

Modify csr.der to modified_csr.der

Extract the sequence (which starts at offset 4) of the CSR that's hashed for the signature

openssl asn1parse -in modified_csr.der -inform der -strparse 4 -out part_of_CSR_thats_hashed

Create new signature

openssl dgst -sha256 -sign privateKey.der -out new_signature part_of_CSR_thats_hashed

Merge modified_csr.der and new_signature to new_csr.der

head -c $(( $(stat -c '%s' modified_csr.der) - 256 )) modified_csr.der > new_csr.der
cat new_signature >> new_csr.der

Verify new CSR

openssl req -in new_csr.der -inform DER -noout -text -verify

Convert CSR to PEM

openssl req -outform PEM -inform DER -in new_csr.der -out new_csr.pem
jhscheer
  • 201
  • 2
  • 6
1

Aside: you don't need to manually convert to binary (DER), openssl req -in req.pem -out req.der -outform der will do it for you. Or you could do -outform der on the initial creation. Also your wording suggests that the 256-byte signature size has some connection to your use of SHA256 in the signature algorithm, which is untrue. And I assume you understand that if you change the length of anything in the body you'll have to change all the affected ASN.1 length fields, since the body is required to be DER.

A CSR, like the closely-related X.509 SIGNED-macro objects (cert and CRL), is an ASN.1 SEQUENCE of:

  • the body (for CSR a SEQUENCE of several things)
  • an AlgorithmIdentifier that identifies the signing algorithm (and theoretically can include parameters but never does; the same structure in other places sometimes does); this is a SEQUENCE of an OID (here the OID for sha256WithRSA) and NULL
  • a BIT STRING containing the signature

openssl asn1parse -in req.der -inform der shows you the decoding, with the offset, header length and body length of each part; adding -i makes the structure clearer (indent data based on level). Note that the public key isn't quite the last thing in the CSR body. And the CSR format can handle other key types than RSA, in which case there is no 'exponent'.

But just hash and then rsautl -sign won't give you a correct PKCS#1 signature; it doesn't encode the digest AlgId as required. See rsa encrypted hash == rsa signature . You can do that by hand (as the OpenSSL FIPS module does to save on code subject to costly verification) or you can use

openssl dgst -sha256 -sign rsaprivatekey.pem -in actual_data_not_hash -out sigvalue

EDIT: or another option I remembered; since 1.0.0:

openssl pkeyutl -sign -inkey rsapriv -pkeyopt digest:sha256 -in hashval -out sigval

lets you do the hash, but does the ASN.1 encode (be sure you specify the same hash to be encoded as you used for the actual hash), padding, and modexp.

dave_thompson_085
  • 9,759
  • 1
  • 24
  • 28
  • Thank you very much! Your answer helped me alot to understand the format of the CSR and to figure out the exact steps I had to do. – jhscheer May 27 '14 at 18:47
0

The total part of the CSR is considered while hashing , so if u change the anypart of it will make the pop verification fail.its better to re-create the csr.

user45475
  • 1,030
  • 2
  • 9
  • 14
  • While it's true that I need to create a new signature if I edit something in the CSR (which is also why I opened this question in the first place), it's not true that the whole CSR is used for the hash. – jhscheer May 27 '14 at 18:48