Bitcoin Forum

Bitcoin => Wallet software => Topic started by: pc on December 25, 2012, 03:01:55 AM



Title: Zsh/OpenSSL Shell Script Key Generator
Post by: pc on December 25, 2012, 03:01:55 AM
I've been intrigued for a while by the use of bitaddress.org and Bitcoin off-the-grid (BOTG) (https://bitcointalk.org/index.php?topic=23081.0) to generate keys for secure storage (and gift-certificate-like paper keys). I particularly liked how BOTG just used basic things that were part of most linux-like systems and straight OpenSSL to generate the keys. (While I think bitaddress.org's code is probably fine, I'm a little paranoid about using Javascript to generate the random numbers, and OpenSSL is basically the standard.) Also, I wanted to play around with learning zsh scripting. I used a lot of the code from BOTG, but adapted it in several ways: (1) Zsh is a little different than most shells, and so I had to make a couple tweaks (like 1-indexing arrays), (2) I'm on a Mac, and a couple of the utilities it used weren't available. (Though for all I know, this won't work on a linux system now), (3) I wanted to support the full range of keys that OpenSSL generated, rather than just most of them (I think that BOTG eliminated about a bit of private key availability, not that it [em]really[/em] matters), (4) I'm not convinced that the BOTG address-generating code worked for addresses that were less than 34 characters, and though I might have I redid it anyway.

I very much appreciate the BOTG base, and it handles a lot more things than this code does. This is just a way to generate a key with OpenSSL and find its address, so that one can send coins there and then later import the key into a wallet. Any contributions here which are mine I put into the public domain. (And as usual, no warranty, while I've tested it for my purposes and it seems good to me and I plan to use it, there's of course the chance of a subtle bug that will mean you can't get your coins or something.)

To run, just put the code in a file and mark it executable. Pass an argument of "1" if you'd like to see all the details of the OpenSSL key to check everything.

Code:
#!/bin/zsh
debug=$1
base58=(1 2 3 4 5 6 7 8 9 A B C D E F G H J K L M N P Q R S T U V W X Y Z a b c d e f g h i j k m n o p q r s t u v w x y z)

capitalizeHex() {
    tr "[a-f]" "[A-F]"
}

encodeBase58() {                                 
    initialones=$(sed -e 's/\(\(00\)*\).*/\1/' -e 's/00/1/g' <<<$1)
    echo -n $initialones
    bc <<<"ibase=16; n=$1; while(n>0) { n%3A ; n/=3A }" |
    tail -r |
    while read n
        do echo -n ${base58[n+1]}
    done
}

checksum() {
    xxd -p -r <<<"$1" |
    openssl dgst -sha256 -binary |
    openssl dgst -sha256 -binary |
    xxd -p -c 80 |
    head -c 8 |
    capitalizeHex
}

hexToAddress() {
    echo "$(encodeBase58 "$2$1$(checksum "$2$1")")"
}

privatekey=$(openssl ecparam -genkey -name secp256k1 -noout)
openssldescription=$(openssl ec -text <<<$privatekey 2>/dev/null)
privatekeyhex=$(head -5 <<<$openssldescription | tail -3 | fmt -120 | sed -e 's/[: ]//g' -e 's/^00//' | awk '{printf "%064s\n", $0}' | capitalizeHex)
publickeyhashhex=$(openssl ec -pubout -outform DER <<<$privatekey 2>/dev/null | tail -c 65 | openssl dgst -sha256 -binary | openssl dgst -rmd160 -binary | xxd -p -c 80 | capitalizeHex)
address=$(hexToAddress $publickeyhashhex "00")
privatekeywif=$(hexToAddress $privatekeyhex "80")

(( $debug )) && echo $openssldescription
echo "Private Key Hex:" $privatekeyhex
echo "Private Key WIF:" $privatekeywif
(( $debug )) && echo "Public Key Hash:" $publickeyhashhex
echo "Address:" $address


Title: Re: Zsh/OpenSSL Shell Script Key Generator
Post by: K1773R on December 25, 2012, 03:12:36 AM
2. how about making a POSIX version? (runs on every *nix, atleast it should)


Title: Re: Zsh/OpenSSL Shell Script Key Generator
Post by: pc on December 25, 2012, 03:19:38 AM
I don't have any particular objection to a POSIX version. I was mainly working on adapting it for my own use, and posted it in case anybody else found it useful. But if somebody wants to tell me what to change to make it POSIX-compliant, I'd be happy to.


Title: Re: Zsh/OpenSSL Shell Script Key Generator
Post by: DannyHamilton on December 26, 2012, 03:10:58 PM
I'm also on a Mac and have been thinking about putting something like this together.  Thanks for saving me the work, this is great!


Title: Re: Zsh/OpenSSL Shell Script Key Generator
Post by: DannyHamilton on December 27, 2012, 01:39:35 AM
I was trying to modify the script to generate a private key and bitcoin address from a passphrase, essentially creating a brainwallet version of your script.

If I've generated a 256 bit private key, for example with the following:

Quote
echo -n "correct horse battery staple" |openssl dgst -sha256

How would I go about determining what the associated public key is?

Can I use the openssl command? Something like:

Quote
echo -n C4BBCB1FBEC99D65BF59D85C8CB62EE2DB963F0FE106F483D9AFA73BD4E39A8A |openssl {some parameters here}


Title: Re: Zsh/OpenSSL Shell Script Key Generator
Post by: pc on December 27, 2012, 02:09:12 AM
I'm sure there's a way to do it, but getting as far as I did was stretching my knowledge of openssl. (Which is largely why I did it.) When OpenSSL generates a key, it creates a PEM file with all sorts of information beyond the private key, at least the curve being used, but I suspect much more (See the output of "openssl ecparam -name secp256k1 -genkey"). OpenSSL seems to expect all keys to be in either that format or DER format, and I don't know how to construct such a file given just the private key, though I assume that it couldn't be that hard for somebody who actually knew the nuts and bolts of all this, either using some obscure (at least to me) OpenSSL option, or via just smashing some sort of header and footer bytes onto the key. I'll look into it a little, but I suspect you'll need an actual expert here.


Title: Re: Zsh/OpenSSL Shell Script Key Generator
Post by: DannyHamilton on December 27, 2012, 02:14:12 AM
. . . I suspect you'll need an actual expert here.
And here I was hoping you were that "actual expert"  ;D

I'll keep playing with it a bit too.  If neither of us comes up with a solution in the next day or two (and no actual experts happen across this discussion), I'll try posting the question in the "Technical Support" sub-forum.

I'm not necessarily looking for a solution using "openssl", I'm just looking for something I can run from a command line terminal on a Mac that will give me the appropriate public key if I have the private key.


Title: Re: Zsh/OpenSSL Shell Script Key Generator
Post by: K1773R on December 27, 2012, 03:35:59 AM
How would I go about determining what the associated public key is?

Can I use the openssl command? Something like:

Quote
echo -n C4BBCB1FBEC99D65BF59D85C8CB62EE2DB963F0FE106F483D9AFA73BD4E39A8A |openssl {some parameters here}

Damn, this is exactly what I've been trying to figure out all day as well :) No luck here either.
openssl dosnt support this.


Title: Re: Zsh/OpenSSL Shell Script Key Generator
Post by: DannyHamilton on December 27, 2012, 03:56:38 AM
How would I go about determining what the associated public key is?

Can I use the openssl command? Something like:

Quote
echo -n C4BBCB1FBEC99D65BF59D85C8CB62EE2DB963F0FE106F483D9AFA73BD4E39A8A |openssl {some parameters here}

Damn, this is exactly what I've been trying to figure out all day as well :) No luck here either.
openssl dosnt support this.
Yep, all three of us were noticing that.  Any useful suggestion on how to get a public key from a command line terminal on a Mac given a 256 bit private key?


Title: Re: Zsh/OpenSSL Shell Script Key Generator
Post by: K1773R on December 27, 2012, 05:37:37 AM
How would I go about determining what the associated public key is?

Can I use the openssl command? Something like:

Quote
echo -n C4BBCB1FBEC99D65BF59D85C8CB62EE2DB963F0FE106F483D9AFA73BD4E39A8A |openssl {some parameters here}

Damn, this is exactly what I've been trying to figure out all day as well :) No luck here either.
openssl dosnt support this.
Yep, all three of us were noticing that.  Any useful suggestion on how to get a public key from a command line terminal on a Mac given a 256 bit private key?
armoryengine.py would be the solution!


Title: Re: Zsh/OpenSSL Shell Script Key Generator
Post by: DannyHamilton on December 27, 2012, 05:50:03 AM
How would I go about determining what the associated public key is?

Can I use the openssl command? Something like:

Quote
echo -n C4BBCB1FBEC99D65BF59D85C8CB62EE2DB963F0FE106F483D9AFA73BD4E39A8A |openssl {some parameters here}

Damn, this is exactly what I've been trying to figure out all day as well :) No luck here either.
openssl dosnt support this.
Yep, all three of us were noticing that.  Any useful suggestion on how to get a public key from a command line terminal on a Mac given a 256 bit private key?
armoryengine.py would be the solution!
Where do I find this program, how do I trust it (is the source available?) and what is the usage? Something like this?

echo -n C4BBCB1FBEC99D65BF59D85C8CB62EE2DB963F0FE106F483D9AFA73BD4E39A8A | armoryengine.pl -genPublicKey


Title: Re: Zsh/OpenSSL Shell Script Key Generator
Post by: K1773R on December 27, 2012, 07:48:45 AM
How would I go about determining what the associated public key is?

Can I use the openssl command? Something like:

Quote
echo -n C4BBCB1FBEC99D65BF59D85C8CB62EE2DB963F0FE106F483D9AFA73BD4E39A8A |openssl {some parameters here}

Damn, this is exactly what I've been trying to figure out all day as well :) No luck here either.
openssl dosnt support this.
Yep, all three of us were noticing that.  Any useful suggestion on how to get a public key from a command line terminal on a Mac given a 256 bit private key?
armoryengine.py would be the solution!
Where do I find this program, how do I trust it (is the source available?) and what is the usage? Something like this?

echo -n C4BBCB1FBEC99D65BF59D85C8CB62EE2DB963F0FE106F483D9AFA73BD4E39A8A | armoryengine.pl -genPublicKey
https://github.com/etotheipi/BitcoinArmory


Title: Re: Zsh/OpenSSL Shell Script Key Generator
Post by: jim618 on December 27, 2012, 08:27:12 AM
Another thing you can do with private keys and OpenSSL is create MultiBit encrypted private key export files. You can then import these later into MultiBit, specifying the same encryption password in the UI.

The file format is described here:
https://github.com/jim618/multibit/wiki/Export%20and%20limited%20import%20of%20private%20keys (https://github.com/jim618/multibit/wiki/Export%20and%20limited%20import%20of%20private%20keys)

The OpenSSL commands to encrypt and decrypt the private key export file are near the bottom of the wiki.


Title: Re: Zsh/OpenSSL Shell Script Key Generator
Post by: BkkCoins on December 30, 2012, 07:20:49 AM
I've created a simple utility that can take hex private keys and output various related data using a format string. So you can give it random data and it outputs WIF keys with address, etc.

It currently only takes HEX keys in but if people would find it useful I could have it take WIF keys as well. That would be a bit more of a swiss army bitcoin knife.
Now supports either Hex or WIF input keys with autodetection.

See my post here, with link to my github misc repo.
https://bitcointalk.org/index.php?topic=133350.msg1425723#msg1425723

Generate keys from the cmd line, from random data,

hexdump -v -e '/1 "%02X"' -n 32 /dev/urandom | keyfmt '%a:%w'


Title: Re: Zsh/OpenSSL Shell Script Key Generator
Post by: mintymark on December 30, 2012, 02:36:36 PM
To get this to work with Ubuntu, I had to change tail -r to tac.

Both reverse the order of lines in a file.


Title: Re: Zsh/OpenSSL Shell Script Key Generator
Post by: etotheipi on December 30, 2012, 07:22:26 PM
The predecessor to Armory (PyBtcEngine (https://github.com/etotheipi/PyBtcEngine/blob/master/pybtcengine.py#L598)) used pure-python implementation of ECDSA.  This implementation was created by forum user "Lis" here:

http://bitcointalk.org/index.php?topic=23241.0

If you plug that into google translate, you'll see that he declared that code to be public domain.  It's not exactly fast, but it most definitely works.  I was able to create, sign, and verify blockchain transactions with it.  It is based on python's native handling of arbitrary-sized integers -- go ahead, open a python shell and type "2**10000" ... you'll see what I mean :))

Not only can you verify that the code is just a basic EC library with hardcoded secp256k1 constants, it's stupid easy to wrap in a shell script to get whatever you need out of it.  I would recommend using entropy generated from a reliable source (OpenSSL?), and pass that into Lis' script.  His script uses the python "random" solely for testing that signing and verification work.  But as is mentioned in python's own docs "python's PRNG is wholly unsuitable for cryptographic purposes".

EDIT:  also, you can use armoryengine.py, but it doesn't use Lis' code anymore... it uses Crypto++ libraries made available to python via SWIG.  It has turned out to be pretty simple to compile and run on any Linux (even OSX, because you can use armoryengine.py without any Qt dependencies).   But that could be a lot of code just to get to its ECDSA implementation.   (you can probably strip out 90% of armoryengine.py, though, and keep just the ECDSA parts)

Alternatively, you could extract my wrapper around Crypto++ (https://github.com/etotheipi/BitcoinArmory/blob/master/cppForSwig/EncryptionUtils.h) and use that somehow.  It does exactly what you want, and you can use publicly-verifiable download the crypto++ libraries, which have no external dependencies.   (use only SecureBinaryData objects and all methods below this line (https://github.com/etotheipi/BitcoinArmory/blob/master/cppForSwig/EncryptionUtils.h#L361)).


Title: Re: Zsh/OpenSSL Shell Script Key Generator
Post by: pc on January 08, 2013, 03:01:42 AM
I did play around quite a bit with trying to create a DER file, but without the public key (it looks optional in the format spec), in the hopes that OpenSSL would figure it out and calculate it itself, but OpenSSL would just crash when given it.

But, if anyone was using what I originally posted, I've updated it to support compressed public keys as well as generating testnet addresses. It also prints the unhashed public key, which is useful if you're using the key for a multisig address. As before, any aspects original to me I put in public domain, and I sure hope there's no bug that causes you to lose your coins since I'd hate for that to happen, but I'm not going to be held liable if it does.
Code:
#!/bin/zsh
zparseopts h=help v=verbose c=compressed t=testnet
if [[ -n $help ]]; then
<<EOF
generatebitcoinkey Generates a bitcoin key using zsh scripting and OpenSSL
-h Print help text
-v Verbose output (mainly for debugging)
-c Use compressed keys
-t Output testnet addresses
EOF
exit
fi

[[ -n $verbose ]] && openssl version

base58=(1 2 3 4 5 6 7 8 9 A B C D E F G H J K L M N P Q R S T U V W X Y Z a b c d e f g h i j k m n o p q r s t u v w x y z)

capitalizeHex() {
    tr "[a-f]" "[A-F]"
}

encodeBase58() {                                 
    initialones=$(sed -e 's/\(\(00\)*\).*/\1/' -e 's/00/1/g' <<<$1)
    echo -n $initialones
    bc <<<"ibase=16; n=$1; while(n>0) { n%3A ; n/=3A }" |
    tail -r |
    while read n
        do echo -n ${base58[n+1]}
    done
}

checksum() {
    xxd -p -r <<<"$1" |
    openssl dgst -sha256 -binary |
    openssl dgst -sha256 -binary |
    xxd -p -c 80 |
    head -c 8 |
    capitalizeHex
}

hexToAddress() {
    echo "$(encodeBase58 "$2$1$(checksum "$2$1")")"
}

if [[ -n $compressed ]]; then
convform=compressed
pubkeybytes=33
wifTag=01
else
convform=uncompressed
pubkeybytes=65
wifTag=
fi

if [[ -n $testnet ]]; then
addressPrefix="6F"
wifPrefix="EF"
else
addressPrefix="00"
wifPrefix="80"
fi

privatekey=$(openssl ecparam -genkey -name secp256k1 -conv_form $convform -noout)
openssldescription=$(openssl ec -text -conv_form $convform <<<$privatekey 2>/dev/null)
privatekeyhex=$(head -5 <<<$openssldescription | tail -3 | fmt -120 | sed -e 's/[: ]//g' -e 's/^00//' | awk '{printf "%064s\n", $0}' | capitalizeHex)
publickeyhex=$(openssl ec -pubout -outform DER -conv_form $convform <<<$privatekey 2>/dev/null | tail -c $pubkeybytes | xxd -p -c 80 | capitalizeHex)
publickeyhashhex=$(xxd -r -p <<<$publickeyhex | openssl dgst -sha256 -binary | openssl dgst -rmd160 -binary | xxd -p -c 80 | capitalizeHex)
address=$(hexToAddress $publickeyhashhex $addressPrefix)
privatekeywif=$(hexToAddress $privatekeyhex$wifTag $wifPrefix)

[[ -n $verbose ]] && echo $openssldescription
echo "Private Key Hex:" $privatekeyhex
echo "Private Key WIF:" $privatekeywif
echo "Public Key:" $publickeyhex
[[ -n $verbose ]] && echo "Public Key Hash:" $publickeyhashhex
echo "Address:" $address


Title: Re: Zsh/OpenSSL Shell Script Key Generator
Post by: grondilu on January 08, 2013, 03:09:25 AM

Notice that xxd might not be present on some Unix system.

An alternative is to use the 'pack' perl function.  Perl is always present on any decent GNU/linux/Unix system:

Code:
checksum() {
    perl -we "print pack 'H*', '$1'" |
    openssl dgst -sha256 -binary |
    openssl dgst -sha256 -binary |
    perl -we "print unpack 'H8', join '', <>"
}


Title: Re: Zsh/OpenSSL Shell Script Key Generator
Post by: Scrat Acorns on January 31, 2013, 10:25:48 AM
If you intend to use this on a live CD make sure you aren't generating keys on a freshly booted machine.

See http://eprint.iacr.org/2006/086.pdf (http://eprint.iacr.org/2006/086.pdf) and http://eprint.iacr.org/2012/251.pdf (http://eprint.iacr.org/2012/251.pdf)

Live CD distributions boot with a preset entropy pool. In theory, generating keys right after you boot will greatly reduce the 256bit keyspace since you have only added a few bits of mouse and IO entropy. You probably need to leave the system on a for a while - maybe even seed /dev/random with microphone data. OpenSSL seeds its PRNG with data from /dev/urandom plus the process ID of the current process.

On a normal system this isn't an issue since all distros will save some bytes from /dev/*random on shutdown and use them on startup to seed the PRNG.


Title: Re: Zsh/OpenSSL Shell Script Key Generator
Post by: samadamsbeer on April 24, 2013, 12:52:18 AM
The predecessor to Armory (PyBtcEngine (https://github.com/etotheipi/PyBtcEngine/blob/master/pybtcengine.py#L598)) used pure-python implementation of ECDSA.  This implementation was created by forum user "Lis" here:

http://bitcointalk.org/index.php?topic=23241.0

If you plug that into google translate, you'll see that he declared that code to be public domain.  It's not exactly fast, but it most definitely works.  I was able to create, sign, and verify blockchain transactions with it.  It is based on python's native handling of arbitrary-sized integers -- go ahead, open a python shell and type "2**10000" ... you'll see what I mean :))

Not only can you verify that the code is just a basic EC library with hardcoded secp256k1 constants, it's stupid easy to wrap in a shell script to get whatever you need out of it.  I would recommend using entropy generated from a reliable source (OpenSSL?), and pass that into Lis' script.  His script uses the python "random" solely for testing that signing and verification work.  But as is mentioned in python's own docs "python's PRNG is wholly unsuitable for cryptographic purposes".

EDIT:  also, you can use armoryengine.py, but it doesn't use Lis' code anymore... it uses Crypto++ libraries made available to python via SWIG.  It has turned out to be pretty simple to compile and run on any Linux (even OSX, because you can use armoryengine.py without any Qt dependencies).   But that could be a lot of code just to get to its ECDSA implementation.   (you can probably strip out 90% of armoryengine.py, though, and keep just the ECDSA parts)

Alternatively, you could extract my wrapper around Crypto++ (https://github.com/etotheipi/BitcoinArmory/blob/master/cppForSwig/EncryptionUtils.h) and use that somehow.  It does exactly what you want, and you can use publicly-verifiable download the crypto++ libraries, which have no external dependencies.   (use only SecureBinaryData objects and all methods below this line (https://github.com/etotheipi/BitcoinArmory/blob/master/cppForSwig/EncryptionUtils.h#L361)).

You lost me at secp256k1 ;-)

If I install Armory on a machine, say without the Satoshi client, can I use it from a command line (or within Armory) to generate key pairs from passphrases? And if so what commands would I use? I don't see the option within Armory.


Title: Re: Zsh/OpenSSL Shell Script Key Generator
Post by: Shevek on April 24, 2013, 09:28:33 PM

But, if anyone was using what I originally posted, I've updated it to support compressed public keys as well as generating testnet addresses. It also prints the unhashed public key, which is useful if you're using the key for a multisig address. As before, any aspects original to me I put in public domain, and I sure hope there's no bug that causes you to lose your coins since I'd hate for that to happen, but I'm not going to be held liable if it does.


+100 It's a must! Thanks!

If your Linux box stumbles on "tail -r", substitute by "tac" (thanks to mintymark).