https://github.com/openssl/openssl/blob/master/crypto/ec/ec_key.c
https://github.com/openssl/openssl/blob/master/apps/ecparam.c
查看手册:man --manpath=/d/local/ssl/man OBJ_sn2nid
EC_KEY *EC_KEY_new(void) { EC_KEY *ret; ret=(EC_KEY *)OPENSSL_malloc(sizeof(EC_KEY)); if (ret == NULL) { ECerr(EC_F_EC_KEY_NEW, ERR_R_MALLOC_FAILURE); return(NULL); } ret->version = 1; ret->flags = 0; ret->group = NULL; ret->pub_key = NULL; ret->priv_key= NULL; ret->enc_flag= 0; ret->conv_form = POINT_CONVERSION_UNCOMPRESSED; ret->references= 1; ret->method_data = NULL; return(ret); }
EC_KEY_set_group(eckey,group)
来设置group参数。
从曲线名称可以得到group参数结构
int nid = OBJ_sn2nid(curve_name); int nid = EC_curve_nist2nid(curve_name); EC_GROUP * group = EC_GROUP_new_by_curve_name(nid);
也可以直接从curve_name创建EC_KEY结构体并设置EC_GROUP成员
EC_KEY *EC_KEY_new_by_curve_name(int nid) { EC_KEY *ret = EC_KEY_new(); if (ret == NULL) return NULL; ret->group = EC_GROUP_new_by_curve_name(nid); if (ret->group == NULL) { EC_KEY_free(ret); return NULL; } return ret; }
void EC_KEY_free(EC_KEY *r) { int i; if (r == NULL) return; i=CRYPTO_add(&r->references,-1,CRYPTO_LOCK_EC); #ifdef REF_PRINT REF_PRINT("EC_KEY",r); #endif if (i > 0) return; #ifdef REF_CHECK if (i < 0) { fprintf(stderr,"EC_KEY_free, bad reference count\n"); abort(); } #endif if (r->group != NULL) EC_GROUP_free(r->group); if (r->pub_key != NULL) EC_POINT_free(r->pub_key); if (r->priv_key != NULL) BN_clear_free(r->priv_key); EC_EX_DATA_free_all_data(&r->method_data); OPENSSL_cleanse((void *)r, sizeof(EC_KEY)); OPENSSL_free(r); }
int EC_KEY_generate_key(EC_KEY *eckey) { int ok = 0; BN_CTX *ctx = NULL; BIGNUM *priv_key = NULL, *order = NULL; EC_POINT *pub_key = NULL; #ifdef OPENSSL_FIPS if(FIPS_selftest_failed()) { FIPSerr(FIPS_F_EC_KEY_GENERATE_KEY,FIPS_R_FIPS_SELFTEST_FAILED); return 0; } #endif if (!eckey || !eckey->group) { ECerr(EC_F_EC_KEY_GENERATE_KEY, ERR_R_PASSED_NULL_PARAMETER); return 0; } if ((order = BN_new()) == NULL) goto err; if ((ctx = BN_CTX_new()) == NULL) goto err; if (eckey->priv_key == NULL) { priv_key = BN_new(); if (priv_key == NULL) goto err; } else priv_key = eckey->priv_key; if (!EC_GROUP_get_order(eckey->group, order, ctx)) goto err; #ifdef OPENSSL_FIPS if (!fips_check_ec_prng(eckey)) goto err; #endif do // 开始随机化私钥数据,脑钱包可以在这里设置随机数据(256bits的Hash) if (!BN_rand_range(priv_key, order)) goto err; while (BN_is_zero(priv_key)); if (eckey->pub_key == NULL) { pub_key = EC_POINT_new(eckey->group); if (pub_key == NULL) goto err; } else pub_key = eckey->pub_key; if (!EC_POINT_mul(eckey->group, pub_key, priv_key, NULL, NULL, ctx)) goto err; eckey->priv_key = priv_key; eckey->pub_key = pub_key; #ifdef OPENSSL_FIPS if(!fips_check_ec(eckey)) { eckey->priv_key = NULL; eckey->pub_key = NULL; goto err; } #endif ok=1; err: if (order) BN_free(order); if (pub_key != NULL && eckey->pub_key == NULL) EC_POINT_free(pub_key); if (priv_key != NULL && eckey->priv_key == NULL) BN_free(priv_key); if (ctx != NULL) BN_CTX_free(ctx); return(ok); }
int EC_KEY_set_private_key(EC_KEY *key, const BIGNUM *priv_key) { if (key->priv_key) BN_clear_free(key->priv_key); key->priv_key = BN_dup(priv_key); return (key->priv_key == NULL) ? 0 : 1; }
但是设置新的私钥数据并不会导致EC_KEY_generate_key去计算新的公钥。
所以比特币的代码里面加入了EC_KEY_regenerate_key()这个实现。
openssl始终是使用BN_rand_range(,,,)来设置私钥数据。
EC_KEY_print_fp(FILE * stdout, EC_KEY * eckey, int offset);
#include <openssl/opensslconf.h> #include <stdlib.h> #include <time.h> #include <string.h> #include <openssl/ec.h> #include <openssl/objects.h> int main(int argc, char * argv[]) { int nid; EC_KEY * eckey ;//= NULL; eckey = EC_KEY_new(); nid = OBJ_sn2nid("secp256k1"); EC_GROUP * group = EC_GROUP_new_by_curve_name(nid); EC_KEY_set_group(eckey, group); EC_KEY_generate_key(eckey); EC_KEY_print_fp(stdout, eckey, 0); EC_KEY_free(eckey); return 0; }
g++ -DMONOLITH -DOPENSSL_THREADS -D_REENTRANT -DDSO_DLFCN -DHAVE_DLFCN_H -DOPENSSL_EC_BIN_PT_COMP -Wa,--noexecstack -DL_ENDIAN -DTERMIO -O3 -fomit-frame-pointer -Wall -DOPENSSL_BN_ASM_PART_WORDS -DOPENSSL_IA32_SSE2 -DOPENSSL_BN_ASM_MONT -DOPENSSL_BN_ASM_GF2m -DSHA1_ASM -DSHA256_ASM -DSHA512_ASM -DMD5_ASM -DRMD160_ASM -DAES_ASM -DVPAES_ASM -DWHIRLPOOL_ASM -DGHASH_ASM -I/d/local/include -L/d/local/lib -O0 -g brainwallet.c -o brainwallet -lcrypto -ldl
localhost ~ # ./brainwallet Private-Key: (256 bit) priv: 00:d5:28:8d:89:65:ba:29:5b:e3:75:68:de:3f:db: a2:17:16:d7:ed:ca:ce:06:63:7f:0e:c9:bc:94:b4: a6:04:e3 pub: 04:9b:01:ab:2d:da:2e:9e:47:90:89:7a:ba:59:d2: 52:19:de:8d:51:a2:51:74:db:44:c0:8c:a2:ab:36: 81:6c:5c:cf:c5:71:c9:e2:e4:fd:dd:78:50:e1:0e: c9:85:49:84:a3:1f:cf:4c:f1:e5:0a:ad:bf:0e:4b: 0a:b7:85:67:9d Field Type: prime-field Prime: 00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff: ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:fe:ff: ff:fc:2f A: 0 B: 7 (0x7) Generator (uncompressed): 04:79:be:66:7e:f9:dc:bb:ac:55:a0:62:95:ce:87: 0b:07:02:9b:fc:db:2d:ce:28:d9:59:f2:81:5b:16: f8:17:98:48:3a:da:77:26:a3:c4:65:5d:a4:fb:fc: 0e:11:08:a8:fd:17:b4:48:a6:85:54:19:9c:47:d0: 8f:fb:10:d4:b8 Order: 00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff: ff:fe:ba:ae:dc:e6:af:48:a0:3b:bf:d2:5e:8c:d0: 36:41:41 Cofactor: 1 (0x1)
secp256k1 refers to the parameters of the ECDSA curve used in Bitcoin, and is defined in Standards for Efficient Cryptography (SEC) (Certicom Research, http://www.secg.org/collateral/sec2_final.pdf).
secp256k1 was almost never used before Bitcoin became popular, but it is now gaining in popularity due to its several nice properties. Most commonly-used curves have a random structure, but secp256k1 was constructed in a special non-random way which allows for especially efficient computation. As a result, it is often more than 30% faster than other curves if the implementation is sufficiently optimized. Also, unlike the popular NIST curves, secp256k1's constants were selected in a predictable way, which significantly reduces the possibility that the curve's creator inserted any sort of backdoor into the curve.
As excerpted from Standards:
The elliptic curve domain parameters over Fp associated with a Koblitz curve secp256k1 are specified by the sextuple T = (p,a,b,G,n,h) where the finite field Fp is defined by:
The curve E: y2 = x3+ax+b over Fp is defined by:
The base point G in compressed form is:
and in uncompressed form is:
Finally the order n of G and the cofactor are:
从上面这些参数可以看出,官方oepnssl ecparam -genkey的实现,
使用BN_rand_range(privkey, maxrange=FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE BAAEDCE6 AF48A03B BFD25E8C D0364141)。
void SetSecretBytes(const unsigned char vch[32]) { // 设置32字节的数组到大整数(256b) bool ret; BIGNUM bn; BN_init(&bn); ret = BN_bin2bn(vch, 32, &bn); assert(ret); ret = EC_KEY_regenerate_key(pkey, &bn);// 生成私钥和公钥 assert(ret); BN_clear_free(&bn); }
// Generate a private key from just the secret parameter int EC_KEY_regenerate_key(EC_KEY *eckey, BIGNUM *priv_key) { int ok = 0; BN_CTX *ctx = NULL; EC_POINT *pub_key = NULL; if (!eckey) return 0; const EC_GROUP *group = EC_KEY_get0_group(eckey); // 获取ec计算群 if ((ctx = BN_CTX_new()) == NULL) goto err; pub_key = EC_POINT_new(group); // (x,y)坐标 if (pub_key == NULL) goto err; if (!EC_POINT_mul(group, pub_key, priv_key, NULL, NULL, ctx)) // 公钥=私钥×基点 goto err; EC_KEY_set_private_key(eckey,priv_key); EC_KEY_set_public_key(eckey,pub_key); ok = 1; err: if (pub_key) EC_POINT_free(pub_key); if (ctx != NULL) BN_CTX_free(ctx); return(ok); }
#include <openssl/opensslconf.h> #include <stdlib.h> #include <time.h> #include <string.h> #include <openssl/ec.h> #include <openssl/objects.h> #include <openssl/bn.h> #include <assert.h> // Generate a private key from just the secret parameter int EC_KEY_regenerate_key(EC_KEY *eckey, BIGNUM *priv_key) { int ok = 0; BN_CTX *ctx = NULL; EC_POINT *pub_key = NULL; if (!eckey) return 0; const EC_GROUP *group = EC_KEY_get0_group(eckey); // 获取ec计算群 if ((ctx = BN_CTX_new()) == NULL) goto err; pub_key = EC_POINT_new(group); // (x,y)坐标 if (pub_key == NULL) goto err; if (!EC_POINT_mul(group, pub_key, priv_key, NULL, NULL, ctx)) // 公钥=私钥×基点 goto err; EC_KEY_set_private_key(eckey,priv_key); EC_KEY_set_public_key(eckey,pub_key); ok = 1; err: if (pub_key) EC_POINT_free(pub_key); if (ctx != NULL) BN_CTX_free(ctx); return(ok); } // 从32字节数组重新设置EC_KEY结构 void SetSecretBytes(EC_KEY * pkey, const unsigned char vch[32]) { // 设置32字节的数组到大整数(256b) bool ret; BIGNUM bn; BN_init(&bn); ret = BN_bin2bn(vch, 32, &bn); assert(ret); ret = EC_KEY_regenerate_key(pkey, &bn);// 生成私钥和公钥 assert(ret); BN_clear_free(&bn); } int main(int argc, char * argv[]) { unsigned char vch[32]; int nid; EC_KEY * eckey ;//= NULL; eckey = EC_KEY_new(); nid = OBJ_sn2nid("secp256k1"); EC_GROUP * group = EC_GROUP_new_by_curve_name(nid); EC_KEY_set_group(eckey, group); // 官方的随机数方式生成EC_KEY EC_KEY_generate_key(eckey); // 打印结果 EC_KEY_print_fp(stdout, eckey, 0); // 设置随机数 memset(vch, 0xa0, sizeof(vch)); // 重新计算 SetSecretBytes(eckey, vch); // 打印结果 EC_KEY_print_fp(stdout, eckey, 0); EC_KEY_free(eckey); return 0; }
localhost ~ # ./brainwallet Private-Key: (256 bit) priv://随机数组成的私钥 00:e6:86:5e:09:25:b6:48:e8:50:7b:25:91:2b:cf: 9d:6f:82:61:f1:3e:b9:12:d6:74:87:c4:39:77:b1: cc:c8:c1 pub: 04:54:f4:b6:19:e3:c6:74:d4:ec:ae:b0:e2:df:10: 95:b2:03:48:59:0b:ff:c6:41:1c:ec:da:d6:9b:5f: 81:32:cb:3d:06:6d:5f:15:a4:cb:a5:97:1b:d2:5c: ec:0f:7e:2b:a6:b7:c0:57:91:62:87:b2:b9:bd:6a: 35:f4:f2:e8:9f Field Type: prime-field Prime: 00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff: ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:fe:ff: ff:fc:2f A: 0 B: 7 (0x7) Generator (uncompressed): 04:79:be:66:7e:f9:dc:bb:ac:55:a0:62:95:ce:87: 0b:07:02:9b:fc:db:2d:ce:28:d9:59:f2:81:5b:16: f8:17:98:48:3a:da:77:26:a3:c4:65:5d:a4:fb:fc: 0e:11:08:a8:fd:17:b4:48:a6:85:54:19:9c:47:d0: 8f:fb:10:d4:b8 Order: 00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff: ff:fe:ba:ae:dc:e6:af:48:a0:3b:bf:d2:5e:8c:d0: 36:41:41 Cofactor: 1 (0x1)
Private-Key: (256 bit) priv://用户设置的私钥 00:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0: a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0: a0:a0:a0 pub: 04:d5:4c:d3:79:30:b0:c5:58:73:33:d5:5b:f4:84: 18:43:a9:22:a5:af:75:46:81:8b:a8:ac:2c:5c:fa: 2c:f9:3d:c8:a9:86:2d:8d:b5:97:49:44:b7:cf:ea: 53:03:92:80:ab:b7:cd:26:dd:f4:a5:28:b4:67:4b: 68:d0:73:e0:57 Field Type: prime-field Prime: 00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff: ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:fe:ff: ff:fc:2f A: 0 B: 7 (0x7) Generator (uncompressed): 04:79:be:66:7e:f9:dc:bb:ac:55:a0:62:95:ce:87: 0b:07:02:9b:fc:db:2d:ce:28:d9:59:f2:81:5b:16: f8:17:98:48:3a:da:77:26:a3:c4:65:5d:a4:fb:fc: 0e:11:08:a8:fd:17:b4:48:a6:85:54:19:9c:47:d0: 8f:fb:10:d4:b8 Order: 00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff: ff:fe:ba:ae:dc:e6:af:48:a0:3b:bf:d2:5e:8c:d0: 36:41:41 Cofactor: 1 (0x1)
#include <openssl/opensslconf.h> #include <stdlib.h> #include <time.h> #include <string.h> #include <openssl/ec.h> #include <openssl/objects.h> #include <openssl/bn.h> #include <assert.h> #include <string> #include <vector> const signed char p_util_hexdigit[256] = { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 0,1,2,3,4,5,6,7,8,9,-1,-1,-1,-1,-1,-1, -1,0xa,0xb,0xc,0xd,0xe,0xf,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,0xa,0xb,0xc,0xd,0xe,0xf,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, }; signed char HexDigit(char c) { return p_util_hexdigit[(unsigned char)c]; } std::vector<unsigned char> ParseHex(const char* psz) { // convert hex dump to vector std::vector<unsigned char> vch; while (true) { while (isspace(*psz)) psz++; signed char c = HexDigit(*psz++); if (c == (signed char)-1) break; unsigned char n = (c << 4); c = HexDigit(*psz++); if (c == (signed char)-1) break; n |= c; vch.push_back(n); } return vch; } // Generate a private key from just the secret parameter int EC_KEY_regenerate_key(EC_KEY *eckey, BIGNUM *priv_key) { int ok = 0; BN_CTX *ctx = NULL; EC_POINT *pub_key = NULL; if (!eckey) return 0; const EC_GROUP *group = EC_KEY_get0_group(eckey); // 获取ec计算群 if ((ctx = BN_CTX_new()) == NULL) goto err; pub_key = EC_POINT_new(group); // (x,y)坐标 if (pub_key == NULL) goto err; if (!EC_POINT_mul(group, pub_key, priv_key, NULL, NULL, ctx)) // 公钥=私钥×基点 goto err; EC_KEY_set_private_key(eckey,priv_key); EC_KEY_set_public_key(eckey,pub_key); ok = 1; err: if (pub_key) EC_POINT_free(pub_key); if (ctx != NULL) BN_CTX_free(ctx); return(ok); } // 从32字节数组重新设置EC_KEY结构 void SetSecretBytes(EC_KEY * pkey, const std::string & sHexString) { // 十六进制字符串数组 bool ret; BIGNUM * bn = NULL; ret = BN_hex2bn(&bn, sHexString.c_str());// 大端形式的16进制数组转换成bigNumber来运算 // 如果长度超多64字节(256bit),那么计算结果是错误的; // 整数溢出了,但是openssl不会检测这个错误。 assert(ret); ret = EC_KEY_regenerate_key(pkey, bn);// 生成私钥和公钥 assert(ret); BN_clear_free(bn); bn = NULL; } int main(int argc, char * argv[]) { int nid; EC_KEY * eckey; eckey = EC_KEY_new(); nid = OBJ_sn2nid("secp256k1"); EC_GROUP * group = EC_GROUP_new_by_curve_name(nid); EC_KEY_set_group(eckey, group); // 官方的随机数方式生成EC_KEY EC_KEY_generate_key(eckey); // 重新计算 SetSecretBytes(eckey, argv[1]); // 打印结果 EC_KEY_print_fp(stdout, eckey, 0); EC_KEY_free(eckey); return 0; }
localhost ~ # ./brainwallet aaaabbbbccccdddd0000111122223333aaaabbbbccccdddd0000111122223333 Private-Key: (256 bit) priv: 00:aa:aa:bb:bb:cc:cc:dd:dd:00:00:11:11:22:22: 33:33:aa:aa:bb:bb:cc:cc:dd:dd:00:00:11:11:22: 22:33:33 pub: 04:18:13:fc:6a:a4:2f:e3:b0:f1:82:d1:c8:b3:29: 68:81:b3:c9:ad:b7:ac:eb:88:d1:ff:10:1d:d7:bc: d3:48:a0:93:b3:d1:8f:7c:4e:d1:8d:0b:61:1a:35: 1d:3e:cf:70:aa:a0:7b:87:67:4f:1b:34:fe:6a:bd: a8:e8:48:24:d4 Field Type: prime-field Prime: 00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff: ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:fe:ff: ff:fc:2f A: 0 B: 7 (0x7) Generator (uncompressed): 04:79:be:66:7e:f9:dc:bb:ac:55:a0:62:95:ce:87: 0b:07:02:9b:fc:db:2d:ce:28:d9:59:f2:81:5b:16: f8:17:98:48:3a:da:77:26:a3:c4:65:5d:a4:fb:fc: 0e:11:08:a8:fd:17:b4:48:a6:85:54:19:9c:47:d0: 8f:fb:10:d4:b8 Order: 00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff: ff:fe:ba:ae:dc:e6:af:48:a0:3b:bf:d2:5e:8c:d0: 36:41:41 Cofactor: 1 (0x1)
常规做法可能是执行./brainwallet $(sha256sum myphoto | sed -e "s/\([^ ]*\)[ ]*.*/\1/g")
localhost ~ # ./brainwallet $(sha256sum testfile | sed -e "s/\([^ ]*\)[ ]*.*/\1/g") Private-Key: (256 bit) priv: 2c:f2:4d:ba:5f:b0:a3:0e:26:e8:3b:2a:c5:b9:e2: 9e:1b:16:1e:5c:1f:a7:42:5e:73:04:33:62:93:8b: 98:24 pub: 04:87:d8:20:42:d9:34:47:00:8d:fe:2a:f7:62:06: 8a:1e:53:ff:39:4a:5b:f8:f6:8a:04:5f:a6:42:b9: 9e:a5:d1:53:f5:77:dd:2d:ba:6c:7a:e4:cf:d7:b6: 62:24:09:d7:ed:d2:d7:6d:d1:3a:80:92:cd:3a:f9: 7b:77:bd:2c:77 Field Type: prime-field Prime: 00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff: ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:fe:ff: ff:fc:2f A: 0 B: 7 (0x7) Generator (uncompressed): 04:79:be:66:7e:f9:dc:bb:ac:55:a0:62:95:ce:87: 0b:07:02:9b:fc:db:2d:ce:28:d9:59:f2:81:5b:16: f8:17:98:48:3a:da:77:26:a3:c4:65:5d:a4:fb:fc: 0e:11:08:a8:fd:17:b4:48:a6:85:54:19:9c:47:d0: 8f:fb:10:d4:b8 Order: 00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff: ff:fe:ba:ae:dc:e6:af:48:a0:3b:bf:d2:5e:8c:d0: 36:41:41 Cofactor: 1 (0x1)
localhost ~ # echo -n $(sha256sum testfile | sed -e "s/\([^ ]*\)[ ]*.*/\1/g") 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
localhost ~ # cat testfile hello
和js语言的在线版本对比(算法是一次SHA256)
得到你的比特币收款地址
脑钱包的原理大致如此,但是各个脑钱包工具的生成算法可能各异的,比如有的是2此的sha256sum,有的是3次,有的是pad了一些数据。所以,这些工具不一定兼容,也可能导致的你的脑钱包丢失,如果在试图使用多个脑钱包,而不知道他的算法的话。