4

Is there a quick way to take a set of numbers and encode it as a private key with openssl command line? Inventing the CLI interface

openssl genpkey -fromhex 'FFFFFFFFF' -algorithm RSA

edit: of course any random numbers wouldn't always fit (depends on the cipher, with EC it might well be the case that most numbers fit), but that's part of the deal. If the number is not a valid private key, I want openssl yell at me.

The main use case is, verifying calculations with openssl. It's really convenient to, say, write a program that converts private key to public key, and use openssl to make sure it's actually working.

4
  • What's your goal? Jan 28, 2013 at 7:47
  • @F.Hauri verifying calculations done by toy programs I've written. I'd rather use the hex encoded numbers from my program, than teach my program ASN.1. Jan 28, 2013 at 8:07
  • @ElazarLeibovich Just use the OpenSSL API. You dump the data into a key structure and then you can do whatever you want with it. OpenSSL will do the ASN1 for you if you need it. The API is significantly more capable than the CLI.
    – tylerl
    Jan 28, 2013 at 18:04
  • @tylerl correct, but significant more cumbersome, and more error prone. Jan 29, 2013 at 10:17

2 Answers 2

4

I'm afraid what you're asking doesn't make a lot of sense.

If indeed you already have the set of numbers that compose your private key, then all you need to do is express that as a string. And if you can already do that, then what do you need openssl for? And if you can't do that, then how are you going to communicate them to openssl? Typically if you actually have the data at all, then you also have the means to output it into a standard file format.

The standard key format used in x509 is simply ASN.1 data formatted using DER (binary) or PEM (base64) formatting rules. Here's a fun activity to see what it looks like:

$ openssl genrsa > key.pem

Now we have a 512-bit RSA keypair. Not particularly useful, but small.

$ openssl rsa -text < key.pem
Private-Key: (512 bit)
modulus:
    00:cc:95:9a:a2:ac:03:6b:77:11:52:be:06:86:8e:
    ....
publicExponent: 65537 (0x10001)
privateExponent:
    00:91:d1:b0:3e:72:4c:9d:b4:68:6c:89:75:2d:43:
    ....

So that's your private key. The first two fields; the modulus and publicExponent are also your public key.

How is this stored? We can ASN.1 decode it without additional processing:

$ openssl asn1parse < key.pem
    0:d=0  hl=4 l= 316 cons: SEQUENCE          
    4:d=1  hl=2 l=   1 prim: INTEGER           :00
    7:d=1  hl=2 l=  65 prim: INTEGER           :CC959AA2AC036B77...
   74:d=1  hl=2 l=   3 prim: INTEGER           :010001
   79:d=1  hl=2 l=  65 prim: INTEGER           :91D1B03E724C9DB4...
  146:d=1  hl=2 l=  33 prim: INTEGER           :E831F72E74263DDC...
   ...

As you can see, the file simply contains the expected data fields dropped in there one after the other, with no fluff. As long as you have the right ASN.1 identifiers for each field, you're pretty much done.

Of course, if you don't want to bother with the binary encoding rules, you can read the ASN.1 fields in as text using the "conf file" format described in the documentation for ASN1_generate_nconf. It looks more like this:

asn1=SEQUENCE:private_key
[private_key]
version=INTEGER:0
n=INTEGER:0xCC959AA2AC036B77...
e=INTEGER:0x010001
d=INTEGER:0x91D1B03E724C9DB4...

You can load this format in to the asn1parse command using the -genconf <file> option, but I'm not sure the command line program allows you to then output a PEM or DER file from the contents. Though, realistically, a C program that uses the openssl API to do just that would only be about 20 lines long, so it's something you could easily do if necessary.

Still, this all sounds pretty contrived; why you would possibly need to do this is beyond me. But I would imagine that if you were well-versed in this technology enough to know that you did need to do it, then the writing the appropriate utility to do so would be trivial.

1
  • I'm well aware that writing it by hand is a trivial task (I'd use python's Crypto for that, or golang). I'd still rather use builtin command line utility and not rely on hand written programs if there's a reasonable way to do that. Thanks for the elaborated answer, and see edit for the purpose. the ASN1 conf is a possibility I wasn't aware of. Jan 28, 2013 at 8:13
1

I don't think it is possible: private and public keys are not any random numbers, but derive from specially crafted two-factor large primes.

The probability of your hex string being a suitable private key would be negligible.

Either you supplied a valid private key generated some other way, say 97a40cf79c13f403f1ecb560fbfd3408ec4bc87b045f9b84eab04a930ac207dd (which would leave you with the problem of how to generate the key in the first place, but then you would only need to ASN-encode it), or you would have to encode a private key by supplying a range where to seek a workable pair of ~512 bit primes, from which to build a ~1024 bit composite, to another prime-seeking algorithm. That, though, smacks of "building your own cryptography system", which is widely regarded as an unwise move.

UPDATE

OK, say you have all the information making up a private key - modulus, composite prime, factors, exponents, the lot. OpenSSL does not provide (AFAIK) for building a PrivateKey block from command-line arguments. So you would need to do that yourself, from RFC 3447 Appendix A.1.2:

A.1.2 RSA private key syntax

   An RSA private key should be represented with the ASN.1 type
   RSAPrivateKey:

      RSAPrivateKey ::= SEQUENCE {
          version           Version,
          modulus           INTEGER,  -- n
          publicExponent    INTEGER,  -- e
          privateExponent   INTEGER,  -- d
          prime1            INTEGER,  -- p
          prime2            INTEGER,  -- q
          exponent1         INTEGER,  -- d mod (p-1)
          exponent2         INTEGER,  -- d mod (q-1)
          coefficient       INTEGER,  -- (inverse of q) mod p
          otherPrimeInfos   OtherPrimeInfos OPTIONAL
      }

Your package would then begin with a "0" character (0x30 hex), followed by a header of 82 HH LL (where 0xHHLL is the length of the package - I get two less than binary key file length, even if I was expecting four less; you may want to check with a known good PEM base64-decoded sequence).

Then each INTEGER will be encoded sequentially by a struct like this

unsigned char type;    // 0x02
unsigned char length;  // 
unsigned char ose[];   // "length" bytes of data (see RFC 3447, #4.1)

You would then need to:

  • implement the I2OSP from RFC 3447 (but if you have the hex representation, that is straightforward)
  • package the numbers as above
  • Encode them in Base64 (NOTE: there are actually two base64 encodings, you may want to check the alphabet with the appropriate reference),
  • "sandwich" them between -----BEGIN RSA PRIVATE KEY-----/-----END RSA PRIVATE KEY-----

and you should be done.

1
  • I didn't imply that it should magically make any number a valid key. I just want it to encode it as proper DER if it's a valid key, and otherwise yell at me. See edit. And ASN encoding is EXACTLY what I need. Jan 28, 2013 at 8:10

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .