Author

Topic: Vanitygen & GPG for secure private keys ** 2+ factor auth ** (software provided) (Read 2328 times)

legendary
Activity: 1792
Merit: 1008
/dev/null
lastly would be a better solutions, there are to many viruses/trojans/similiar that are spreading over USB and even infect offline pcs, afterwards transfering data from the offline PC to a online PC and to a CC of a botnet/similiar. most ppl believe a offline PC is secure, but it isnt.

the idea with the QR Code would be more secure but not 100%, it would be 100% if the QR Software on the smartphone is rock solid too (no possible exploits) and other devices if they get integrated.


greetings
legendary
Activity: 1890
Merit: 1086
Ian Knowles - CIYAM Lead Developer
altough note dont think its rock solid Wink

I think it can be made rock solid but only if the computer you run it on is never again connected to the internet (or any network or other computer just in case it was in any way compromised).

Another poster mentioned about creating QR codes which would make it convenient to transfer the encrypted private keys with an "air gap" (so there is no need to even use a USB to transfer the encrypted private keys for backup purposes).
legendary
Activity: 1792
Merit: 1008
/dev/null
Anyway for anyone that is interested I've whipped up a little utility and a couple of scripts to accomplish this (for Windows).
why not making a fifo (mkfifo) and then point vanitygen to that file? cat the fifo per pipe into gpg and there u go, basic linux stuff Tongue

Perhaps you missed that. Smiley

yea i did, sry 4 that ^^
altough note dont think its rock solid Wink
legendary
Activity: 1890
Merit: 1086
Ian Knowles - CIYAM Lead Developer
Anyway for anyone that is interested I've whipped up a little utility and a couple of scripts to accomplish this (for Windows).
why not making a fifo (mkfifo) and then point vanitygen to that file? cat the fifo per pipe into gpg and there u go, basic linux stuff Tongue

Perhaps you missed that. Smiley
legendary
Activity: 1792
Merit: 1008
/dev/null
Anyway for anyone that is interested I've whipped up a little utility and a couple of scripts to accomplish this (for Windows).

Assuming you have GPG installed (with your public key in its keyring) create a dummy key-pair (I've used [email protected] here) and give it the password "password".

First part is a simple tool (probably could just be a shell script in Linux) which firstly sends a hard-coded password to cout (the security of the GPG "from" should be irrelevant as it being used as a "send only" address) followed by the private key line it finds from cin (it is expecting its cin to be coming from "vanitygen"). The "address" line is output to a fixed filename ("x" in this source).

Code:
[x.cpp]

#include
#include
#include

const char* const c_outfile = "x";
const char* const c_password = "password";

const char* const c_address_prefix = "Address: ";
const char* const c_privkey_prefix = "Privkey: ";

using namespace std;

int main( )
{
   ofstream outf( c_outfile );

   cout << c_password << endl;

   string str, addr, privkey;
   while( getline( cin, str ) )
   {
      if( str.find( c_address_prefix ) == 0 )
         outf << str << '\n' << endl;
      else if( str.find( c_privkey_prefix ) == 0 )
         cout << str << endl;
   }
}

The second part is a batch file you call in order to create a new bitcoin address (change Ian to your own GPG name):
Code:
@echo off

REM *** Use simple program to split out the private key for GPG encryption.
vanitygen 1 | x.exe | gpg --armor --recipient Ian --encrypt --sign --local-user [email protected] --batch --passphrase-fd 0 >y
copy /Y x + y z >nul
type z&del x y

REM *** Now rename the output file to the bitcoin address.
type z | find "Address: " > z.bat
cscript findrep.vbs z.bat "Address: " "ren z " >nul
call z.bat
del z.bat

The final part is the "findrep.vbs" tool (wouldn't be needed if using Linux):
Code:
Const ForReading = 1
Const ForWriting = 2

If Wscript.Arguments.Count > 2 Then

strFileName = Wscript.Arguments(0)
strOldText = Wscript.Arguments(1)
strNewText = Wscript.Arguments(2)

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile(strFileName, ForReading)

strText = objFile.ReadAll
objFile.Close
strNewText = Replace(strText, strOldText, strNewText)

Set objFile = objFSO.OpenTextFile(strFileName, ForWriting)
objFile.WriteLine strNewText
objFile.Close

Else
 Wscript.Echo "Usage: findrep "
 Wscript.Quit
End If

So now to generate a new bitcoin address you just type "genaddr" at the command prompt. As well as displaying the address and the GPG encrypted private key it saves the output to a file which is the name of the address (which can be safely backed up anywhere).

The following a sample of the output:
Code:
Address: 16vKwvg61UycrbhygXokVNQE3CxMSx22r7

-----BEGIN PGP MESSAGE-----
Version: GnuPG v1.4.9 (MingW32)

hQEMA1cEJ0zSVDDtAQgAnagg9KrfhOlyZrSrItrQxB0IuoOnR8GmG0m4dXFYMCtY
2g4b1HEBhQ/xytGW+lon2LyRZpCoW5BAglW+NeFJ5Oev2c3XcBpVIDlwl9C4CsUJ
w7/dUzFzqwfyiyDl662Bq8rF0qzOyQoyaj629Wz2EeBslb7yVejkg6mylc6hiPZz
zTMxr4Qz4GByty5Qx1Z5X78h49zzeZHnm+22PoiP/5CjEZgX8LohIhyrmJnTRHDJ
47/nRE8j3w45/ozj73KVMyQ936IoxvhpiaoMwTp3UQ8cxdU4xaJBz6MOnjFJn5DV
G3/Td4YpvJuYtNRNOg9xRT5lq2x0/71mYxN+4hoItNLACwFJyFrjitExf6du3Xyy
CvT+kclF73xFGcAA+OLqbbeF1wRqqMxWrYPy2fAORua2B/iWPZgIkLNcCfLFZtL4
pABGG5DV0D+Dh+kyvDf03l2iFe3v1aedUKJ4UFnrBa7me/cQcZSnl5xknNBZD1PY
R5IY1rrBC6BJ+6DtffRREwlp3tLgECpVL/zSBUGqWx68tzOxSnuwubCd0Q1z61fq
L1wfDfjat052eB0xqM7x335LUvBbAKQJ5XxEaJ56CsJsQP4oU9EXcXZrvg2I
=A3f1
-----END PGP MESSAGE-----

When you decide to "redeem" the address simply use "gpg --decode" with the file to get the private key:
Code:
gpg: encrypted with 2048-bit RSA key, ID D25430ED, created 2012-03-25
      "Ian Knowles "
Privkey: 5K6X8kvffAUYewAnmAuGHLB4wAk4UH2aZ1NBHdBf2YyzkUqzqHH
gpg: Signature made 10/13/12 12:08:32 using DSA key ID 8C155FBD
gpg: Good signature from "Sample "

why not making a fifo (mkfifo) and then point vanitygen to that file? cat the fifo per pipe into gpg and there u go, basic linux stuff Tongue
legendary
Activity: 1890
Merit: 1086
Ian Knowles - CIYAM Lead Developer
I really like the QR code idea a lot - am not sure if most GPG public keys are small enough to fit into a QR code but if so then I think that the combination of vanitygen and GPG could provide an extremely secure (and indeed "air gapped") method to generate wallet addresses.
hero member
Activity: 651
Merit: 501
My PGP Key: 92C7689C
Thanks for the neat bash script (I figured the program I whipped up shouldn't be needed for Linux but having worked for so many years under a standard Windows environment I have become accustomed to writing small programs to do such things).

I assume to do the 2-factor implementation you would assign a variable to the output of the first gpg call and then feed the password plus this into the second call?


The original version of the script makes only one call to GPG.  It's fed two lines: the passphrase of the signing key and the Bitcoin private key.  The output from GPG is appended to the address and written to disk.

In the version that generates QR codes, the second call gets the passphrase and a generated PNG with the QR code of the private key.  This way, the private key (whether as text or a QR code) never goes to disk in unencrypted form.  A QR code is also generated for the address; this is written to disk unencrypted.
legendary
Activity: 1890
Merit: 1086
Ian Knowles - CIYAM Lead Developer
Thanks for the neat bash script (I figured the program I whipped up shouldn't be needed for Linux but having worked for so many years under a standard Windows environment I have become accustomed to writing small programs to do such things).

I assume to do the 2-factor implementation you would assign a variable to the output of the first gpg call and then feed the password plus this into the second call?
hero member
Activity: 651
Merit: 501
My PGP Key: 92C7689C
Code:
#!/bin/bash
out=$(vanitygen 1 2>/dev/null | grep -v Pattern | tr "\n" " ")
addr=$(echo $out | sed "s/ Privkey.*//;s/Address: //")
key=$(echo $out | sed "s/.* Privkey: //")
(echo Address: $addr; \
echo ""; \
(echo ; echo $key) | gpg2 --armor --recipient --encrypt --sign --local-user --batch --passphrase-fd 0 )>$addr.asc

...and to generate QR codes, add these to the preceding script:

Code:
qrcode -o $addr.png -l M $addr
(echo ; qrcode -o - -l M $key) | gpg2 --armor --recipient --encrypt --sign --local-user --batch --passphrase-fd 0 >$addr-privkey.png.asc

qrcode is provided by libqrencode; a Win32 port is available.  Note that the private key QR code is also encrypted.
hero member
Activity: 651
Merit: 501
My PGP Key: 92C7689C
Awesome. Been wanting this exact tool for some time.

Any chance of porting it to linux?

Not much to it as a shell script...knocked this together in Cygwin, but it'd work the same under Linux, Mac OS X, or whatever. Output is in the same format as produced here, but there's nothing to compile:

Code:
#!/bin/bash
out=$(vanitygen 1 2>/dev/null | grep -v Pattern | tr "\n" " ")
addr=$(echo $out | sed "s/ Privkey.*//;s/Address: //")
key=$(echo $out | sed "s/.* Privkey: //")
(echo Address: $addr; \
echo ""; \
(echo ; echo $key) | gpg2 --armor --recipient --encrypt --sign --local-user --batch --passphrase-fd 0 )>$addr.asc

The address and encrypted private key are written to a file; leave out ">$addr.asc" on the last line if you'd rather have it go to stdout.  Substitute appropriate values as follows:

: address or PGP key ID for whom the private key should be encrypted
: address or PGP key ID for whom the private key should be signed
: passphrase for
legendary
Activity: 1890
Merit: 1086
Ian Knowles - CIYAM Lead Developer
Well I decided to whip up a bash script anyway as I think I will be using this with a Linux OS down the track.

(note that as the x.cpp program actually creates the file 'x' I renamed x.cpp to w.cpp for Linux and compiled it using 'g++ -o w w.cpp')

Code:
[genaddr]
gpg_1_opts="--armor --recipient Ian_1 --encrypt --sign --local-user [email protected] --batch --passphrase-fd 0"
gpg_2_opts="--armor --recipient Ian_2 --encrypt --sign --local-user [email protected] --batch --passphrase-fd 0"

# Use simple program to split out the private key for GPG encryption.
./vanitygen 1 | ./w | gpg $gpg_1_opts | ./w 2 | gpg $gpg_2_opts >y
cat x y >z
rm x y

cat z

# Now rename the output file to the bitcoin address.
cat z | grep Address > z.sh
sed -i 's/Address: /mv z /g' z.sh
chmod a+x z.sh

./z.sh
rm z.sh

Enjoy!
legendary
Activity: 1890
Merit: 1086
Ian Knowles - CIYAM Lead Developer
I just realised that with only a little minor tweaking this approach can be turned into a "roll your own" 2 factor authentication system. The idea is to encrypt the bitcoin private key to one GPG public key and then pipe this into another GPG encryption to a second GPG public key. So if you generate the GPG private keys on separate hardware then both computers would need to be compromised in order for your bitcoin private key to be obtained.

Obviously this could be extended to 3 factors or more (depending upon level of paranoia, available hardware and degree of laziness).

Updated C++ program (takes an optional argument "2" to indicate it is being used as the second pipe):
Code:
[x.cpp]
#include
#include
#include

const char* const c_outfile = "x";
const char* const c_password = "password";

const char* const c_address_prefix = "Address: ";
const char* const c_privkey_prefix = "Privkey: ";

using namespace std;

int main( int argc, char* argv[ ] )
{
   cout << c_password << endl;

   bool is_second = false;
   if( argc > 1 && string( argv[ 1 ] ) == "2" )
      is_second = true;

   string str;
   while( getline( cin, str ) )
   {
      if( is_second )
         cout << str << '\n';
      else if( str.find( c_address_prefix ) == 0 )
      {
         ofstream outf( c_outfile );
         outf << str << '\n' << endl;
      }
      else if( str.find( c_privkey_prefix ) == 0 )
         cout << str << endl;
   }
}


Updated batch file to create GPG encrypt the private key twice (thus requiring both GPG keys in order to decrypt the bitcoin private key) - change Ian_1 and Ian_2 to your own two different public GPG key names:
Code:
[genaddr.bat]
@echo off
setlocal

set GPG_1_OPTS=--armor --recipient Ian_1 --encrypt --sign --local-user [email protected] --batch --passphrase-fd 0
set GPG_2_OPTS=--armor --recipient Ian_2 --encrypt --sign --local-user [email protected] --batch --passphrase-fd 0

REM *** Use simple program to split out the private key for GPG encryption.
vanitygen 1 | x.exe | gpg %GPG_1_OPTS% | x.exe 2 | gpg %GPG_2_OPTS% >y
copy /Y x + y z >nul
type z&del x y

REM *** Now rename the output file to the bitcoin address.
type z | find "Address: " > z.bat
cscript findrep.vbs z.bat "Address: " "ren z " >nul
call z.bat
del z.bat

endlocal
legendary
Activity: 1652
Merit: 1016
I thought there was an command line option to encrypt output from vanitygen already. I might be wrong though and thinking of some other program.

Sorry, must of been thinking of some other program.
legendary
Activity: 1890
Merit: 1086
Ian Knowles - CIYAM Lead Developer
Awesome. Been wanting this exact tool for some time.

Any chance of porting it to linux?

Hmm... I guess if a small bounty were to be offered (is 1 btc too much to ask?) then I could become motivated enough to put together a bash script. Wink
hero member
Activity: 532
Merit: 500
Awesome. Been wanting this exact tool for some time.

Any chance of porting it to linux?
legendary
Activity: 1890
Merit: 1086
Ian Knowles - CIYAM Lead Developer
Anyway for anyone that is interested I've whipped up a little utility and a couple of scripts to accomplish this (for Windows).

Assuming you have GPG installed (with your public key in its keyring) create a dummy key-pair (I've used [email protected] here) and give it the password "password".

First part is a simple tool (probably could just be a shell script in Linux) which firstly sends a hard-coded password to cout (the security of the GPG "from" should be irrelevant as it being used as a "send only" address) followed by the private key line it finds from cin (it is expecting its cin to be coming from "vanitygen"). The "address" line is output to a fixed filename ("x" in this source).

Code:
[x.cpp]

#include
#include
#include

const char* const c_outfile = "x";
const char* const c_password = "password";

const char* const c_address_prefix = "Address: ";
const char* const c_privkey_prefix = "Privkey: ";

using namespace std;

int main( )
{
   ofstream outf( c_outfile );

   cout << c_password << endl;

   string str, addr, privkey;
   while( getline( cin, str ) )
   {
      if( str.find( c_address_prefix ) == 0 )
         outf << str << '\n' << endl;
      else if( str.find( c_privkey_prefix ) == 0 )
         cout << str << endl;
   }
}

The second part is a batch file you call in order to create a new bitcoin address (change Ian to your own GPG name):
Code:
@echo off

REM *** Use simple program to split out the private key for GPG encryption.
vanitygen 1 | x.exe | gpg --armor --recipient Ian --encrypt --sign --local-user [email protected] --batch --passphrase-fd 0 >y
copy /Y x + y z >nul
type z&del x y

REM *** Now rename the output file to the bitcoin address.
type z | find "Address: " > z.bat
cscript findrep.vbs z.bat "Address: " "ren z " >nul
call z.bat
del z.bat

The final part is the "findrep.vbs" tool (wouldn't be needed if using Linux):
Code:
Const ForReading = 1
Const ForWriting = 2

If Wscript.Arguments.Count > 2 Then

strFileName = Wscript.Arguments(0)
strOldText = Wscript.Arguments(1)
strNewText = Wscript.Arguments(2)

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile(strFileName, ForReading)

strText = objFile.ReadAll
objFile.Close
strNewText = Replace(strText, strOldText, strNewText)

Set objFile = objFSO.OpenTextFile(strFileName, ForWriting)
objFile.WriteLine strNewText
objFile.Close

Else
 Wscript.Echo "Usage: findrep "
 Wscript.Quit
End If

So now to generate a new bitcoin address you just type "genaddr" at the command prompt. As well as displaying the address and the GPG encrypted private key it saves the output to a file which is the name of the address (which can be safely backed up anywhere).

The following a sample of the output:
Code:
Address: 16vKwvg61UycrbhygXokVNQE3CxMSx22r7

-----BEGIN PGP MESSAGE-----
Version: GnuPG v1.4.9 (MingW32)

hQEMA1cEJ0zSVDDtAQgAnagg9KrfhOlyZrSrItrQxB0IuoOnR8GmG0m4dXFYMCtY
2g4b1HEBhQ/xytGW+lon2LyRZpCoW5BAglW+NeFJ5Oev2c3XcBpVIDlwl9C4CsUJ
w7/dUzFzqwfyiyDl662Bq8rF0qzOyQoyaj629Wz2EeBslb7yVejkg6mylc6hiPZz
zTMxr4Qz4GByty5Qx1Z5X78h49zzeZHnm+22PoiP/5CjEZgX8LohIhyrmJnTRHDJ
47/nRE8j3w45/ozj73KVMyQ936IoxvhpiaoMwTp3UQ8cxdU4xaJBz6MOnjFJn5DV
G3/Td4YpvJuYtNRNOg9xRT5lq2x0/71mYxN+4hoItNLACwFJyFrjitExf6du3Xyy
CvT+kclF73xFGcAA+OLqbbeF1wRqqMxWrYPy2fAORua2B/iWPZgIkLNcCfLFZtL4
pABGG5DV0D+Dh+kyvDf03l2iFe3v1aedUKJ4UFnrBa7me/cQcZSnl5xknNBZD1PY
R5IY1rrBC6BJ+6DtffRREwlp3tLgECpVL/zSBUGqWx68tzOxSnuwubCd0Q1z61fq
L1wfDfjat052eB0xqM7x335LUvBbAKQJ5XxEaJ56CsJsQP4oU9EXcXZrvg2I
=A3f1
-----END PGP MESSAGE-----

When you decide to "redeem" the address simply use "gpg --decode" with the file to get the private key:
Code:
gpg: encrypted with 2048-bit RSA key, ID D25430ED, created 2012-03-25
      "Ian Knowles "
Privkey: 5K6X8kvffAUYewAnmAuGHLB4wAk4UH2aZ1NBHdBf2YyzkUqzqHH
gpg: Signature made 10/13/12 12:08:32 using DSA key ID 8C155FBD
gpg: Good signature from "Sample "
legendary
Activity: 1890
Merit: 1086
Ian Knowles - CIYAM Lead Developer
I thought there was an command line option to encrypt output from vanitygen already. I might be wrong though and thinking of some other program.

Really - using GPG (and only the private key to be encrypted)?

(certainly not the version that I am running but it is probably a bit old)
legendary
Activity: 1652
Merit: 1016
I thought there was an command line option to encrypt output from vanitygen already. I might be wrong though and thinking of some other program.
legendary
Activity: 1890
Merit: 1086
Ian Knowles - CIYAM Lead Developer
There have been numerous threads discussing how to generate offline wallets and typically the problem ends up being that if your keys a) generated from a password or b) stored in clear text on a disk then they are subject to attack keyloggers or malware.

I am wondering if using GPG would provide a simple solution by having the output of vanitygen encrypted to a GPG public key (the private key not being known to the "offline" system). With this approach even if the "offline" system was stolen no generated private keys could be taken (apart from the GPG key which is actually of no consequence).

I will probably put together a small script/program to accomplish this but I guess if the GPG functionality could be built in to vanitygen itself this would be even more secure (i.e. so the private key is never output as clear text).

(see source in posts below)
Jump to: