Last active
June 5, 2024 01:15
-
-
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)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 |
Author
GregTonoski
commented
Dec 19, 2023
via email
Not purely bash but maybe suits your needs:
https://github.com/grondilu/bitcoin-bash-tools
…On Tue, 19 Dec 2023, 16:07 st3b1t, ***@***.***> wrote:
***@***.**** commented on this gist.
------------------------------
mmh many thanks! I was looking for a compact solution that uses only bash,
I was hoping you had some other such gist, but I guess generating addresses
from a WIF is a very complicated thing in bash
—
Reply to this email directly, view it on GitHub
<https://gist.github.com/GregTonoski/438992249df6e4bd613f9758421ff38a#gistcomment-4800229>
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/A2RBO2KYYQQ7S65EG53EUCTYKGUTLBFKMF2HI4TJMJ2XIZLTSKBKK5TBNR2WLJDHNFZXJJDOMFWWLK3UNBZGKYLEL52HS4DFQKSXMYLMOVS2I5DSOVS2I3TBNVS3W5DIOJSWCZC7OBQXE5DJMNUXAYLOORPWCY3UNF3GS5DZVRZXKYTKMVRXIX3UPFYGLK2HNFZXIQ3PNVWWK3TUUZ2G64DJMNZZDAVEOR4XAZNEM5UXG5FFOZQWY5LFVEYTCNZZGQ3TKMRYU52HE2LHM5SXFJTDOJSWC5DF>
.
You are receiving this email because you authored the thread.
Triage notifications on the go with GitHub Mobile for iOS
<https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
or Android
<https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>
.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment