Skip to content

Instantly share code, notes, and snippets.

@GregTonoski
Last active April 3, 2024 21:00
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save GregTonoski/438992249df6e4bd613f9758421ff38a to your computer and use it in GitHub Desktop.
Save GregTonoski/438992249df6e4bd613f9758421ff38a to your computer and use it in GitHub Desktop.
Convert secp256k1 private key in hexadicimal number (HEX) to Bitcoin Wallet Import Format (WIF)
#!/bin/bash
# private_key_into_bitcoin_wif.sh: convert secp256k1 private key in hexadicimal number (HEX) to Bitcoin Wallet Import Format (WIF)
# Examples:
# $ bash private_key_into_bitcoin_wif.sh "000ffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364139" > private_key_bitcoin.wif
# $ bash private_key_into_bitcoin_wif.sh $(< input_file.txt) > private_key_bitcoin.wif
# $ cat priv_key.txt | xargs bash private_key_into_bitcoin_wif.sh > private_key_bitcoin.wif
# $ bash private_key_into_bitcoin_wif.sh fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364139 | echo "$(cat) importedkeylabel false" | xargs bitcoin-cli importprivkey && bitcoin-cli getaddressesbylabel importedkeylabel
# $ openssl rand -hex 32 | xargs bash private_key_into_bitcoin_wif.sh
# $ xxd -l 32 -p -c 32 /dev/random | xargs bash private_key_into_bitcoin_wif.sh
# $ od -t x --width=32 --endian=big -A n -v --skip-bytes=7 --read-bytes=32 priv_key_secp256k1.openssl.der | tr -d [:blank:] | xargs bash private_key_into_bitcoin_wif.sh
# $ openssl asn1parse -in priv_key_secp256k1.der -inform der -offset=5 -length=34 | cut -d ':' -f 4 | xargs bash private_key_into_bitcoin_wif.sh
# (Extracting a private key using certutil.exe parser in cmd.exe only) C:\> for /f "usebackq tokens=3-19" %a in (`certutil.exe -asn priv_key_secp256k1.openssl.der ^| findstr "\<|....................................................;\>"`) do @echo | set /p=%a%b%c%d%e%f%g%h%i%j%k%l%m%n%o%p
# $ openssl ec -in private_key.openssl.pem -text | grep -A 3 'priv:' | tail -n 3 | tr -d -c [:xdigit:] | xargs bash private_key_into_bitcoin_wif.sh
# $ gpg --list-packets --verbose gpg.key | grep skey | grep -o -w -m 1 '[[:xdigit:]]\{64\}' | xargs bash private_key_into_bitcoin_wif.sh
# $ zsh private_key_into_bitcoin_wif.sh "000ffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364139"
declare -r BASE58_CHARSET="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
declare hex_priv_key="$1"
declare u_limit=fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140
declare -i counter=0
#######################################
# Input validation - 64 characters and within the elliptic curve secp256k1 private key limit
#######################################
if [ ${#hex_priv_key} -ne 64 ]; then
echo "ERROR: The input argument is not 64 characters long. If the number is shorter then it needs to be prepanded with zeros so it looks like in the example: 000ffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364139"
exit 1
fi
for (( counter=0; counter<64; counter+=2 )); do
if [[ $(( 16#${hex_priv_key:$counter:2} )) -lt $(( 16#${u_limit:$counter:2} )) ]]; then
break
elif [[ $(( 16#${hex_priv_key:$counter:2} )) -eq $(( 16#${u_limit:$counter:2} )) ]]; then
continue
else
echo "ERROR: The input priv key is not within valid secp256k1 range of ${u_limit}"
exit 2
fi
done
#######################################
# Conversion from HEX to WIF
#######################################
declare -r PRIVATE_KEY_WIF_PREFIX="80"
declare -r COMPRESSION_FLAG_WIF_SUFFIX="01"
declare hex_annotated_priv_key="${PRIVATE_KEY_WIF_PREFIX}${hex_priv_key}${COMPRESSION_FLAG_WIF_SUFFIX}"
if command -v sha256sum >/dev/null
then
declare sha256_digest=$( for (( counter=0; counter<${#hex_annotated_priv_key}; counter+=2 )) ; do printf "\x${hex_annotated_priv_key:$counter:2}" ; done | sha256sum -b )
declare double_sha256_digest=$( for (( counter=0; counter<64; counter+=2 )) ; do printf "\x${sha256_digest:$counter:2}" ; done | sha256sum -b )
elif command -v shasum >/dev/null
then
declare sha256_digest=$( for (( counter=0; counter<${#hex_annotated_priv_key}; counter+=2 )) ; do printf "\x${hex_annotated_priv_key:$counter:2}" ; done | shasum -a 256 -b )
declare double_sha256_digest=$( for (( counter=0; counter<64; counter+=2 )) ; do printf "\x${sha256_digest:$counter:2}" ; done | shasum -a 256 -b )
elif command -v openssl >/dev/null
then
declare double_sha256_digest=$( for (( counter=0; counter<${#hex_annotated_priv_key}; counter+=2 )) ; do printf "\x${hex_annotated_priv_key:$counter:2}" ; done | openssl dgst -digest -sha256 -binary | openssl dgst -digest -sha256 -r )
else
echo "There isn't sha256sum or shasum or openssl installed. Script execution interrupted."
exit 1
fi
hex_annotated_priv_key="${hex_annotated_priv_key}${double_sha256_digest:0:8}"
declare dividend="${hex_annotated_priv_key}"
declare -i -r divisor=${#BASE58_CHARSET}
declare -i -r divisor_length=${#divisor}
declare -i subdividend=0
declare quotient="1"
declare -i i=0
declare -i j=0
declare -i position=0
declare -i leading_zeros=0
declare -i initial_zeros_in_priv_key=0
declare b58_int_string=""
while ((${#dividend} >= ${divisor_length})); do
quotient=""
for (( position=0; position<${#dividend}; position++ )); do
subdividend=$((subdividend*16+16#${dividend:$position:1}))
printf -v quotient "%s%X" "${quotient}" $((subdividend/divisor))
subdividend=$((subdividend%divisor))
done
printf -v b58_int_string "%s%02d" "${b58_int_string}" "${subdividend}"
subdividend=0
for (( leading_zeros=0; leading_zeros<${#quotient}; leading_zeros++ )); do
if [ ${quotient:$leading_zeros:1} != "0" ]; then
break
fi
done
dividend=${quotient:$leading_zeros}
done
if (( leading_zeros != ${#quotient} )); then
printf -v b58_int_string "%s%02d" "${b58_int_string}" $(( 16#${dividend} ))
fi
for (( initial_zeros_in_priv_key=0; initial_zeros_in_priv_key<${#hex_annotated_priv_key}; initial_zeros_in_priv_key+=2 )); do
if [[ ${hex_annotated_priv_key:$initial_zeros_in_priv_key:2} == "00" ]]; then
printf -v b58_int_string "%s%c" "${b58_int_string}" "0"
else
break
fi
done
declare -i dec=0
for (( j=${#b58_int_string}-2; j >= 0; j-=2 )); do
dec=$(( 10#${b58_int_string:$j:2} ))
printf "%c" ${BASE58_CHARSET:$dec:1}
done
printf "\n"
# Release date: 2022-08-23
@GregTonoski
Copy link
Author

GregTonoski commented Dec 19, 2023 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment