Bitcoin Forum
May 08, 2024, 02:58:59 AM *
News: Latest Bitcoin Core release: 27.0 [Torrent]
 
   Home   Help Search Login Register More  
Pages: [1] 2 »  All
  Print  
Author Topic: Zsh/OpenSSL Shell Script Key Generator  (Read 5446 times)
pc (OP)
Sr. Member
****
Offline Offline

Activity: 253
Merit: 250


View Profile
December 25, 2012, 03:01:55 AM
 #1

I've been intrigued for a while by the use of bitaddress.org and Bitcoin off-the-grid (BOTG) 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
1715137139
Hero Member
*
Offline Offline

Posts: 1715137139

View Profile Personal Message (Offline)

Ignore
1715137139
Reply with quote  #2

1715137139
Report to moderator
1715137139
Hero Member
*
Offline Offline

Posts: 1715137139

View Profile Personal Message (Offline)

Ignore
1715137139
Reply with quote  #2

1715137139
Report to moderator
Advertised sites are not endorsed by the Bitcoin Forum. They may be unsafe, untrustworthy, or illegal in your jurisdiction.
K1773R
Legendary
*
Offline Offline

Activity: 1792
Merit: 1008


/dev/null


View Profile
December 25, 2012, 03:12:36 AM
 #2

2. how about making a POSIX version? (runs on every *nix, atleast it should)

[GPG Public Key]
BTC/DVC/TRC/FRC: 1K1773RbXRZVRQSSXe9N6N2MUFERvrdu6y ANC/XPM AK1773RTmRKtvbKBCrUu95UQg5iegrqyeA NMC: NK1773Rzv8b4ugmCgX789PbjewA9fL9Dy1 LTC: LKi773RBuPepQH8E6Zb1ponoCvgbU7hHmd EMC: EK1773RxUes1HX1YAGMZ1xVYBBRUCqfDoF BQC: bK1773R1APJz4yTgRkmdKQhjhiMyQpJgfN
pc (OP)
Sr. Member
****
Offline Offline

Activity: 253
Merit: 250


View Profile
December 25, 2012, 03:19:38 AM
 #3

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.
DannyHamilton
Legendary
*
Offline Offline

Activity: 3388
Merit: 4653



View Profile
December 26, 2012, 03:10:58 PM
 #4

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!
DannyHamilton
Legendary
*
Offline Offline

Activity: 3388
Merit: 4653



View Profile
December 27, 2012, 01:39:35 AM
 #5

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}
pc (OP)
Sr. Member
****
Offline Offline

Activity: 253
Merit: 250


View Profile
December 27, 2012, 02:09:12 AM
 #6

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.
DannyHamilton
Legendary
*
Offline Offline

Activity: 3388
Merit: 4653



View Profile
December 27, 2012, 02:14:12 AM
 #7

. . . I suspect you'll need an actual expert here.
And here I was hoping you were that "actual expert"  Grin

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.
K1773R
Legendary
*
Offline Offline

Activity: 1792
Merit: 1008


/dev/null


View Profile
December 27, 2012, 03:35:59 AM
 #8

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 Smiley No luck here either.
openssl dosnt support this.

[GPG Public Key]
BTC/DVC/TRC/FRC: 1K1773RbXRZVRQSSXe9N6N2MUFERvrdu6y ANC/XPM AK1773RTmRKtvbKBCrUu95UQg5iegrqyeA NMC: NK1773Rzv8b4ugmCgX789PbjewA9fL9Dy1 LTC: LKi773RBuPepQH8E6Zb1ponoCvgbU7hHmd EMC: EK1773RxUes1HX1YAGMZ1xVYBBRUCqfDoF BQC: bK1773R1APJz4yTgRkmdKQhjhiMyQpJgfN
DannyHamilton
Legendary
*
Offline Offline

Activity: 3388
Merit: 4653



View Profile
December 27, 2012, 03:56:38 AM
 #9

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 Smiley 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?
K1773R
Legendary
*
Offline Offline

Activity: 1792
Merit: 1008


/dev/null


View Profile
December 27, 2012, 05:37:37 AM
 #10

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 Smiley 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!

[GPG Public Key]
BTC/DVC/TRC/FRC: 1K1773RbXRZVRQSSXe9N6N2MUFERvrdu6y ANC/XPM AK1773RTmRKtvbKBCrUu95UQg5iegrqyeA NMC: NK1773Rzv8b4ugmCgX789PbjewA9fL9Dy1 LTC: LKi773RBuPepQH8E6Zb1ponoCvgbU7hHmd EMC: EK1773RxUes1HX1YAGMZ1xVYBBRUCqfDoF BQC: bK1773R1APJz4yTgRkmdKQhjhiMyQpJgfN
DannyHamilton
Legendary
*
Offline Offline

Activity: 3388
Merit: 4653



View Profile
December 27, 2012, 05:50:03 AM
 #11

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 Smiley 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
K1773R
Legendary
*
Offline Offline

Activity: 1792
Merit: 1008


/dev/null


View Profile
December 27, 2012, 07:48:45 AM
 #12

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 Smiley 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

[GPG Public Key]
BTC/DVC/TRC/FRC: 1K1773RbXRZVRQSSXe9N6N2MUFERvrdu6y ANC/XPM AK1773RTmRKtvbKBCrUu95UQg5iegrqyeA NMC: NK1773Rzv8b4ugmCgX789PbjewA9fL9Dy1 LTC: LKi773RBuPepQH8E6Zb1ponoCvgbU7hHmd EMC: EK1773RxUes1HX1YAGMZ1xVYBBRUCqfDoF BQC: bK1773R1APJz4yTgRkmdKQhjhiMyQpJgfN
jim618
Legendary
*
Offline Offline

Activity: 1708
Merit: 1066



View Profile WWW
December 27, 2012, 08:27:12 AM
 #13

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

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

MultiBit HD   Lightweight desktop client.                    Bitcoin Solutions Ltd   Bespoke software. Consultancy.
BkkCoins
Hero Member
*****
Offline Offline

Activity: 784
Merit: 1009


firstbits:1MinerQ


View Profile WWW
December 30, 2012, 07:20:49 AM
Last edit: December 30, 2012, 08:48:57 AM by BkkCoins
 #14

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'

mintymark
Sr. Member
****
Offline Offline

Activity: 286
Merit: 251


View Profile
December 30, 2012, 02:36:36 PM
 #15

To get this to work with Ubuntu, I had to change tail -r to tac.

Both reverse the order of lines in a file.

[[ All Tips gratefully received!!  ]]
15ta5d1N8mKkgC47SRWmnZABEFyP55RrqD
etotheipi
Legendary
*
Offline Offline

Activity: 1428
Merit: 1093


Core Armory Developer


View Profile WWW
December 30, 2012, 07:22:26 PM
Last edit: January 08, 2013, 05:13:22 AM by etotheipi
 #16

The predecessor to Armory (PyBtcEngine) 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 Smiley)

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++ 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).

Founder and CEO of Armory Technologies, Inc.
Armory Bitcoin Wallet: Bringing cold storage to the average user!
Only use Armory software signed by the Armory Offline Signing Key (0x98832223)

Please donate to the Armory project by clicking here!    (or donate directly via 1QBDLYTDFHHZAABYSKGKPWKLSXZWCCJQBX -- yes, it's a real address!)
pc (OP)
Sr. Member
****
Offline Offline

Activity: 253
Merit: 250


View Profile
January 08, 2013, 03:01:42 AM
 #17

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
grondilu
Legendary
*
Offline Offline

Activity: 1288
Merit: 1076


View Profile
January 08, 2013, 03:09:25 AM
 #18


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 '', <>"
}

Scrat Acorns
Sr. Member
****
Offline Offline

Activity: 293
Merit: 250



View Profile
January 31, 2013, 10:25:48 AM
Last edit: January 31, 2013, 11:14:29 AM by Scrat Acorns
 #19

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 and 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.
samadamsbeer
Member
**
Offline Offline

Activity: 93
Merit: 10


View Profile
April 24, 2013, 12:52:18 AM
 #20

The predecessor to Armory (PyBtcEngine) 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 Smiley)

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++ 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).

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.
Pages: [1] 2 »  All
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.19 | SMF © 2006-2009, Simple Machines Valid XHTML 1.0! Valid CSS!