13

I posted this question Is a passphrase-protected SSH private key susceptible to a dictionary attack? a few days back.

An excerpt from the final answer:

With PKCS#8 + PBKDF2 and one million rounds (OpenSSL would need some coaxing to produce that), you gain 20 bits (because 220 is approximately equal to one million).

...

The openssl command-line tool does not allow choosing the number of iterations -- but OpenSSL, the library, supports it (and so does OpenSSH, since OpenSSH uses OpenSSL). Producing the encrypted key file would require some programming (calling the library with the appropriate parameters).

Now, does anyone know of any pre-existing utility that can accomplish the above? If not, can somebody write such a program and make it publicly available for the good of all... as I'm sure others would find it useful too?

(I am completely new to security, cryptography, key formats and related topics, and am not at all familiar with the source code of OpenSSL, and so, won't be able to make OpenSSL do things that it's not already doing.)

done
  • 103
  • 4
Harry
  • 861
  • 8
  • 12

1 Answers1

18

Simplest method is still to patch OpenSSL, namely the command-line tool (not the library). Walk-through (assuming a Linux host):

Download the OpenSSL source code. Take the latest, which the page shows in red (right now, version 1.0.1g).

Unpack it in some directory:

cd /tmp
tar xvzf ~/Downloads/openssl-1.0.1g.tar.gz

Edit the source file /tmp/openssl-1.0.1g/apps/pkcs8.c: search the string "iter" until you want to find these lines:

            else if (!strcmp (*args, "-noiter"))
                    iter = 1;
            else if (!strcmp (*args, "-nocrypt"))
                    nocrypt = 1;

That's line 158 in version 1.0.1g. Edit these lines so that they look like this:

            else if (!strcmp (*args, "-noiter"))
                    iter = 1;
            else if (!strcmp (*args, "-iter"))
                    {
                    if (!args[1]) goto bad;
                    iter = atoi(*(++args));
                    if (iter <= 0) goto bad;
                    }
            else if (!strcmp (*args, "-nocrypt"))
                    nocrypt = 1;

In other words, just add the 6 lines which handle a new command-line argument called "-iter". Save the file.

Then compile the code:

cd /tmp/openssl-1.0.1g
./config --prefix=$HOME/local
make
make test
make install

And voilà! You have a brand new openssl command-line tool and library in the local subdirectory of your home directory (you can put it anywhere you wish with the --prefix option, but writing in your home directory does not need root access and won't interfere with your OS-provided tools). The command-line tool command pkcs8 now has a -iter option which can be use to select the number of iterations:

$HOME/local/bin/openssl genrsa -out rsaraw.pem 2048
$HOME/local/bin/openssl pkcs8 -topk8 -v2 aes128 -iter 1000000 -in rsaraw.pem -out rsapk8.pem

The resulting file can be used with a completely standard OpenSSL or OpenSSH, because the library has always supported PBKDF2 with one million iterations. Here we are just patching the command-line tool to be able to set that iteration count. A look at the resulting object will show that the iteration count was indeed taken into account:

openssl asn1parse -i -in rsapk8.pem

will show something which begins with:

  0:d=0  hl=4 l=1312 cons: SEQUENCE          
  4:d=1  hl=2 l=  74 cons:  SEQUENCE          
  6:d=2  hl=2 l=   9 prim:   OBJECT            :PBES2
 17:d=2  hl=2 l=  61 cons:   SEQUENCE          
 19:d=3  hl=2 l=  28 cons:    SEQUENCE          
 21:d=4  hl=2 l=   9 prim:     OBJECT            :PBKDF2
 32:d=4  hl=2 l=  15 cons:     SEQUENCE          
 34:d=5  hl=2 l=   8 prim:      OCTET STRING      [HEX DUMP]:A4E21F4F210DEB6F
 44:d=5  hl=2 l=   3 prim:      INTEGER           :0F4240
 49:d=3  hl=2 l=  29 cons:    SEQUENCE          
 51:d=4  hl=2 l=   9 prim:     OBJECT            :aes-128-cbc
 62:d=4  hl=2 l=  16 prim:     OCTET STRING      [HEX DUMP]:DAA184B3F6CC303B6A40A131E5C8C451
 80:d=1  hl=4 l=1232 prim:  OCTET STRING      [HEX DUMP]:2C15CF37D5ACC537AA92B
 (...)

See the "0F4240" ? That's one million, in hexadecimal.

The patch is trivial; I will try to submit it to the OpenSSL maintainers.

toolbear
  • 103
  • 4
Tom Leek
  • 168,808
  • 28
  • 337
  • 475
  • 1
    Tom, that was unbelievable! I have not 'tried' it yet, but your instructions look like they WILL work when I try them out in the next few days. But, would you be able to (please) confirm in the meantime if this will indeed make my private key so secure that I could choose to make it public? Thomas Pornin seems to suggest that in his answer to my original question but I'm not fully sure. Many thanks in advance. – Harry Aug 01 '13 at 12:43
  • 1
    Thomas Pornin and me have a... mutual understanding. I can confirm that this is what he had in mind. The final decision is yours, though: with 1 million iterations, this will resist brute force attacks 1 million times better than the basic password protection of SSH keys. Whether this is _enough_ depends on how good your password is. If you use your first name as password, no amount of iterations will save you. – Tom Leek Aug 01 '13 at 13:27
  • Thanks, Tom. I cannot tell you how much I appreciate both your help and Thomas's on this. As I said, I will be able to try your solution and instructions only in a few days and not before, but marking your answer as final in the meantime. Will let you know via a comment if something does not work out. – Harry Aug 01 '13 at 14:58
  • I've created a downloadable patch at https://gist.github.com/Lekensteyn/8122263#file-openssl-1-0-1e-pkcs8-iter-patch ([direct link](https://gist.github.com/Lekensteyn/8122263/raw/openssl-1.0.1e-pkcs8-iter.patch)) – Lekensteyn Dec 25 '13 at 11:07
  • This is awesome. Will an unpatched OpenSSL be able to handle the one million (or any other number that is not the default) iters-encrypted private key? – Steven Lu Dec 26 '13 at 05:49
  • 1
    @StevenLu: yes. As I wrote, OpenSSL _the library_ has always supported the general case. Internally, OpenSSL handles the iteration count as an `int` which, on most architectures, will support counts up to a bit more than 2 billions. The limitation is purely a matter of the _command-line tool_, and, as shown, it is easy enough to patch, precisely because the library has full support for all iteration counts. – Tom Leek Dec 26 '13 at 14:07
  • 6
    [Your patch made it in 2014-06-17.](https://github.com/openssl/openssl/commit/8a6c6bbf21cc11ea0fed69a106250af0d734d786) You can compile from git without having to apply an extra patch now. :-) – Matt Nordhoff Jul 05 '14 at 23:44