rename b64u to b64t; rename x2y 为 x_to_; add b32 的转换函数
This commit is contained in:
parent
b2abb16e81
commit
447a3eb442
43
README.md
43
README.md
@ -57,40 +57,53 @@ const keyPair = crypto.generateKeyPairSync('rsa', {
|
|||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
这样生成的 keyPair.privateKey 开头是 -----BEGIN ENCRYPTED PRIVATE KEY-----
|
这样生成的 keyPair.privateKey 开头是 -----BEGIN ENCRYPTED PRIVATE KEY-----
|
||||||
|
|
||||||
|
如果直接
|
||||||
|
|
||||||
如果直接
|
|
||||||
```
|
```
|
||||||
crypto.privateEncrypt(kp.privateKey, Buffer.from('sdafasfdsaf'))
|
crypto.privateEncrypt(kp.privateKey, Buffer.from('sdafasfdsaf'))
|
||||||
```
|
```
|
||||||
会报错
|
|
||||||
|
会报错
|
||||||
|
|
||||||
```
|
```
|
||||||
Uncaught TypeError: Passphrase required for encrypted key
|
Uncaught TypeError: Passphrase required for encrypted key
|
||||||
```
|
```
|
||||||
所以要这样才行
|
|
||||||
|
所以要这样才行
|
||||||
|
|
||||||
```
|
```
|
||||||
crypto.privateEncrypt({key:kp.privateKey, passphrase:''}, Buffer.from('sdafasfdsaf'))
|
crypto.privateEncrypt({key:kp.privateKey, passphrase:''}, Buffer.from('sdafasfdsaf'))
|
||||||
```
|
```
|
||||||
我从 https://www.cnblogs.com/chyingp/p/nodejs-asymmetric-enc-dec.html 抄到一个 privateKey 可以直接使用,不需要 passphrase
|
|
||||||
|
|
||||||
返回 Buffer。每次结果都一样
|
我从 https://www.cnblogs.com/chyingp/p/nodejs-asymmetric-enc-dec.html 抄到一个 privateKey 可以直接使用,不需要 passphrase
|
||||||
|
|
||||||
|
|
||||||
这样生成的 keyPair.publicKey 开头是 -----BEGIN PUBLIC KEY-----
|
返回 Buffer。每次结果都一样
|
||||||
|
|
||||||
|
这样生成的 keyPair.publicKey 开头是 -----BEGIN PUBLIC KEY-----
|
||||||
|
|
||||||
|
可以直接
|
||||||
|
|
||||||
可以直接
|
|
||||||
```
|
```
|
||||||
crypto.publicEncrypt(kp.publicKey, Buffer.from('sdafasfdsaf'))
|
crypto.publicEncrypt(kp.publicKey, Buffer.from('sdafasfdsaf'))
|
||||||
```
|
```
|
||||||
|
|
||||||
返回 Buffer。每次结果不一样
|
返回 Buffer。每次结果不一样
|
||||||
|
|
||||||
|
似乎 crypto 一定要 rsa 公私钥才可以用加解密,ticCrypto.randomKeypair() 生成的 ecc 公私钥不行。
|
||||||
|
|
||||||
似乎 crypto 一定要 rsa 公私钥才可以用加解密,ticCrypto.randomKeypair() 生成的 ecc 公私钥不行。
|
而 eccrypto 和 eccrypto-js 可以用。eccrypto.generateKeyPair() 生成的和 ticCrypto.randomKeypair() 一样
|
||||||
|
|
||||||
而 eccrypto 和 eccrypto-js 可以用。eccrypto.generateKeyPair() 生成的和 ticCrypto.randomKeypair() 一样
|
eccrypto 在 windows 上的安装有麻烦,一来需要手工安装 OpenSSL 到 c:\openssl-win64\,二来 openssl 1.1.0 起把 libeay32.lib 改名为 libcrypto.dll,而 eccrypto 需要 c:\openssl-win64\lib\libeay32.lib,会报错
|
||||||
|
|
||||||
eccrypto 在 windows 上的安装有麻烦,一来需要手工安装 OpenSSL 到 c:\openssl-win64\,二来 openssl 1.1.0 起把 libeay32.lib 改名为 libcrypto.dll,而 eccrypto 需要 c:\openssl-win64\lib\libeay32.lib,会报错
|
eccrypto-js 在 devDependencies 里继承了 eccrypto,因此 npm i --production 即可
|
||||||
|
|
||||||
eccrypto-js 在 devDependencies 里继承了 eccrypto,因此 npm i --production 即可
|
base32 有多种字符集:[Base32 - Wikipedia](https://en.wikipedia.org/wiki/Base32)
|
||||||
|
|
||||||
|
IPFS用的是 RFC4648字符集
|
||||||
|
|
||||||
|
- 从数到数符串:Number(数).toString(进制数),0x数.toString(进制数), 0b数.toString(进制数)
|
||||||
|
- 从数符串到数字:parseInt(str, 进制数)
|
||||||
|
- Buffer到数符串: Buffer.toString(编码方案例如'hex','base64',默认'utf8')
|
||||||
|
- 字符串到Buffer: Buffer.from(data, 编码方案如'hex','base64',默认'utf8')
|
||||||
|
110
index.js
110
index.js
@ -13,6 +13,8 @@ const bip39 = require('bip39') // https://github.com/bitcoinjs/bip39 // 有更
|
|||||||
const hdkey = require('hdkey') // https://github.com/cryptocoinjs/hdkey // 或者用 bitcore-mnemonic 或者 ethers 里的相同功能
|
const hdkey = require('hdkey') // https://github.com/cryptocoinjs/hdkey // 或者用 bitcore-mnemonic 或者 ethers 里的相同功能
|
||||||
// const bitcorelib = require('bitcore-lib')
|
// const bitcorelib = require('bitcore-lib')
|
||||||
const secp256k1 = require('secp256k1')
|
const secp256k1 = require('secp256k1')
|
||||||
|
const base32encode = require('base32-encode')
|
||||||
|
const base32decode = require('base32-decode')
|
||||||
|
|
||||||
// 全部以hex为默认输入输出格式,方便人的阅读,以及方便函数之间统一接口
|
// 全部以hex为默认输入输出格式,方便人的阅读,以及方便函数之间统一接口
|
||||||
|
|
||||||
@ -182,7 +184,7 @@ class TICrypto {
|
|||||||
// eccrypto 能用 Uint8Array 和 Buffer
|
// eccrypto 能用 Uint8Array 和 Buffer
|
||||||
// eccrypto-js 只能用 Buffer
|
// eccrypto-js 只能用 Buffer
|
||||||
// 在浏览器里 https://github.com/bitchan/eccrypto 库报错,即使用了 Uint8Array: Failed to execute 'encrypt' on 'SubtleCrypto': The provided value is not of type '(ArrayBuffer or ArrayBufferView)'
|
// 在浏览器里 https://github.com/bitchan/eccrypto 库报错,即使用了 Uint8Array: Failed to execute 'encrypt' on 'SubtleCrypto': The provided value is not of type '(ArrayBuffer or ArrayBufferView)'
|
||||||
let cipherobject = await eccrypto.encrypt(Buffer.from(this.hex2buf(key)), data)
|
let cipherobject = await eccrypto.encrypt(Buffer.from(this.hex_to_buf(key)), data)
|
||||||
return cipherobject // 返回一个复杂的结构 {iv:Buffer, ciphertext:Buffer, ...}。对同样的key和data,每次返回的结果不一样
|
return cipherobject // 返回一个复杂的结构 {iv:Buffer, ciphertext:Buffer, ...}。对同样的key和data,每次返回的结果不一样
|
||||||
} else if (keytype === 'pwd') {
|
} else if (keytype === 'pwd') {
|
||||||
// 对称加密
|
// 对称加密
|
||||||
@ -190,7 +192,7 @@ class TICrypto {
|
|||||||
let inputEncoding = my.INPUT_LIST.indexOf(input) >= 0 ? input : my.INPUT // 'utf8' by default, 'ascii', 'latin1' for string or ignored for Buffer/TypedArray/DataView
|
let inputEncoding = my.INPUT_LIST.indexOf(input) >= 0 ? input : my.INPUT // 'utf8' by default, 'ascii', 'latin1' for string or ignored for Buffer/TypedArray/DataView
|
||||||
let outputEncoding = output === 'buf' ? undefined : my.OUTPUT_LIST.indexOf(output) >= 0 ? output : my.OUTPUT // 'latin1', 'base64', 'hex' by default or 'buf' to Buffer explicitly
|
let outputEncoding = output === 'buf' ? undefined : my.OUTPUT_LIST.indexOf(output) >= 0 ? output : my.OUTPUT // 'latin1', 'base64', 'hex' by default or 'buf' to Buffer explicitly
|
||||||
const iv = crypto.randomBytes(16)
|
const iv = crypto.randomBytes(16)
|
||||||
let encryptor = crypto.createCipheriv(my.CIPHER_LIST.indexOf(cipher) >= 0 ? cipher : my.CIPHER, this.hex2buf(this.hash(key)), iv) // cipher 和 key 的长度必须相同,例如 cipher 是 ***-192,那么 key 就必须是 192/8=24 字节 = 48 hex 的。
|
let encryptor = crypto.createCipheriv(my.CIPHER_LIST.indexOf(cipher) >= 0 ? cipher : my.CIPHER, this.hex_to_buf(this.hash(key)), iv) // cipher 和 key 的长度必须相同,例如 cipher 是 ***-192,那么 key 就必须是 192/8=24 字节 = 48 hex 的。
|
||||||
if (typeof data !== 'string' && !(data instanceof Buffer) && !(data instanceof DataView)) data = JSON.stringify(data)
|
if (typeof data !== 'string' && !(data instanceof Buffer) && !(data instanceof DataView)) data = JSON.stringify(data)
|
||||||
let ciphertext = encryptor.update(data, inputEncoding, outputEncoding)
|
let ciphertext = encryptor.update(data, inputEncoding, outputEncoding)
|
||||||
ciphertext += encryptor.final(outputEncoding) // 但是 Buffer + Buffer 还是会变成string
|
ciphertext += encryptor.final(outputEncoding) // 但是 Buffer + Buffer 还是会变成string
|
||||||
@ -198,10 +200,10 @@ class TICrypto {
|
|||||||
}
|
}
|
||||||
} else if (keytype === 'seckey') {
|
} else if (keytype === 'seckey') {
|
||||||
// 尚未走通,不能使用 ticCrypto 生成的 Elliptic curve 椭圆曲线算法公私钥,只能用 crypto.generateKeypairs() 生成的 rsa 公私钥
|
// 尚未走通,不能使用 ticCrypto 生成的 Elliptic curve 椭圆曲线算法公私钥,只能用 crypto.generateKeypairs() 生成的 rsa 公私钥
|
||||||
let seckeyPEM = await new keyman.Key('oct', this.hex2buf(key), { namedCurve: 'P-256K' }).export('pem') // 私钥导出的der格式为144字节。
|
let seckeyPEM = await new keyman.Key('oct', this.hex_to_buf(key), { namedCurve: 'P-256K' }).export('pem') // 私钥导出的der格式为144字节。
|
||||||
return crypto.privateEncrypt(seckeyPEM, Buffer.from(data)) // 返回 Buffer。每次结果都一样。
|
return crypto.privateEncrypt(seckeyPEM, Buffer.from(data)) // 返回 Buffer。每次结果都一样。
|
||||||
} else if (keytype === 'pubkey') {
|
} else if (keytype === 'pubkey') {
|
||||||
let pubkeyPEM = await new keyman.Key('oct', this.hex2buf(key), { namedCurve: 'P-256K' }).export('pem')
|
let pubkeyPEM = await new keyman.Key('oct', this.hex_to_buf(key), { namedCurve: 'P-256K' }).export('pem')
|
||||||
return crypto.publicEncrypt(pubkeyPEM, Buffer.from(data)) // 返回 Buffer。每次结果不一样。
|
return crypto.publicEncrypt(pubkeyPEM, Buffer.from(data)) // 返回 Buffer。每次结果不一样。
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
@ -220,7 +222,7 @@ class TICrypto {
|
|||||||
// data 应当是 encrypt 输出的数据类型
|
// data 应当是 encrypt 输出的数据类型
|
||||||
if (tool === 'eccrypto') {
|
if (tool === 'eccrypto') {
|
||||||
try {
|
try {
|
||||||
// eccrypto 只能接受 Buffer, 不接受 Uint8Array, 因为 eccrypto 需要调用 Buffer.compare 方法,不能在这里直接用 hex2buf
|
// eccrypto 只能接受 Buffer, 不接受 Uint8Array, 因为 eccrypto 需要调用 Buffer.compare 方法,不能在这里直接用 hex_to_buf
|
||||||
// eccrypto 也只能接受 Buffer, 不接受 Uint8Array
|
// eccrypto 也只能接受 Buffer, 不接受 Uint8Array
|
||||||
// data 需要是 eccrypto 自身encrypt方法返回的 cipherobject. key 是 private key。
|
// data 需要是 eccrypto 自身encrypt方法返回的 cipherobject. key 是 private key。
|
||||||
let plainbuffer = await eccrypto.decrypt(Buffer.from(key, 'hex'), data) // 返回的是 Buffer
|
let plainbuffer = await eccrypto.decrypt(Buffer.from(key, 'hex'), data) // 返回的是 Buffer
|
||||||
@ -236,7 +238,7 @@ class TICrypto {
|
|||||||
let outputEncoding = output === 'buf' ? undefined : my.INPUT_LIST.indexOf(output) >= 0 ? output : my.INPUT // output (=input of encrypt) could be 'latin1', 'ascii', 'utf8' by default or 'buf' to Buffer explicitly
|
let outputEncoding = output === 'buf' ? undefined : my.INPUT_LIST.indexOf(output) >= 0 ? output : my.INPUT // output (=input of encrypt) could be 'latin1', 'ascii', 'utf8' by default or 'buf' to Buffer explicitly
|
||||||
let decryptor = crypto.createDecipheriv(
|
let decryptor = crypto.createDecipheriv(
|
||||||
my.CIPHER_LIST.indexOf(cipher) >= 0 ? cipher : my.CIPHER,
|
my.CIPHER_LIST.indexOf(cipher) >= 0 ? cipher : my.CIPHER,
|
||||||
this.hex2buf(this.hash(key)),
|
this.hex_to_buf(this.hash(key)),
|
||||||
Buffer.from(data.iv, 'hex')
|
Buffer.from(data.iv, 'hex')
|
||||||
)
|
)
|
||||||
let decrypted = decryptor.update(data.ciphertext, inputEncoding, outputEncoding)
|
let decrypted = decryptor.update(data.ciphertext, inputEncoding, outputEncoding)
|
||||||
@ -246,10 +248,10 @@ class TICrypto {
|
|||||||
}
|
}
|
||||||
} else if (keytype === 'seckey') {
|
} else if (keytype === 'seckey') {
|
||||||
// 尚未走通,不能使用 ticCrypto 生成的 Elliptic curve 椭圆曲线算法公私钥
|
// 尚未走通,不能使用 ticCrypto 生成的 Elliptic curve 椭圆曲线算法公私钥
|
||||||
let seckeyPEM = await new keyman.Key('oct', this.hex2buf(key), { namedCurve: 'P-256K' }).export('pem') // 私钥导出的der格式为144字节。
|
let seckeyPEM = await new keyman.Key('oct', this.hex_to_buf(key), { namedCurve: 'P-256K' }).export('pem') // 私钥导出的der格式为144字节。
|
||||||
return crypto.privateDecrypt(seckeyPEM, Buffer.from(data)) // 返回 Buffer。每次结果都一样。
|
return crypto.privateDecrypt(seckeyPEM, Buffer.from(data)) // 返回 Buffer。每次结果都一样。
|
||||||
} else if (keytype === 'pubkey') {
|
} else if (keytype === 'pubkey') {
|
||||||
let pubkeyPEM = await new keyman.Key('oct', this.hex2buf(key), { namedCurve: 'P-256K' }).export('pem')
|
let pubkeyPEM = await new keyman.Key('oct', this.hex_to_buf(key), { namedCurve: 'P-256K' }).export('pem')
|
||||||
return crypto.publicDecrypt(pubkeyPEM, Buffer.from(data)) // 返回 Buffer。每次结果不一样。
|
return crypto.publicDecrypt(pubkeyPEM, Buffer.from(data)) // 返回 Buffer。每次结果不一样。
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
@ -279,7 +281,7 @@ class TICrypto {
|
|||||||
return signature.toString('hex')
|
return signature.toString('hex')
|
||||||
} else if (seckey.length === 64) {
|
} else if (seckey.length === 64) {
|
||||||
// 纯 crypto
|
// 纯 crypto
|
||||||
let seckeyPEM = await new keyman.Key('oct', this.hex2buf(seckey), { namedCurve: 'P-256K' }).export('pem') // 私钥导出的der格式为144字节。
|
let seckeyPEM = await new keyman.Key('oct', this.hex_to_buf(seckey), { namedCurve: 'P-256K' }).export('pem') // 私钥导出的der格式为144字节。
|
||||||
let signer = crypto.createSign(my.HASHER_LIST.indexOf(hasher) >= 0 ? hasher : my.HASHER) // 注意,不知为何,hasher必须含有'sha'才能完成签名,例如 sha1, sha256, sha512, sha3, RSA-SHA1, id-rsassa-pkcs1-v1_5-with-sha3-224, 其他都会报错。
|
let signer = crypto.createSign(my.HASHER_LIST.indexOf(hasher) >= 0 ? hasher : my.HASHER) // 注意,不知为何,hasher必须含有'sha'才能完成签名,例如 sha1, sha256, sha512, sha3, RSA-SHA1, id-rsassa-pkcs1-v1_5-with-sha3-224, 其他都会报错。
|
||||||
signer.update(this.hash(data)).end()
|
signer.update(this.hash(data)).end()
|
||||||
let signature = signer.sign(seckeyPEM, 'hex')
|
let signature = signer.sign(seckeyPEM, 'hex')
|
||||||
@ -322,7 +324,7 @@ class TICrypto {
|
|||||||
}
|
}
|
||||||
} else if (signature.length >= 140) {
|
} else if (signature.length >= 140) {
|
||||||
// 纯 crypto // 发现大小写不影响 crypto 验签!都能通过
|
// 纯 crypto // 发现大小写不影响 crypto 验签!都能通过
|
||||||
let pubkeyPEM = await new keyman.Key('oct', this.hex2buf(pubkey), { namedCurve: 'P-256K' }).export('pem') // 公钥导出的der格式为88字节。经测试,同一对压缩和非压缩公钥得出的结果一模一样。
|
let pubkeyPEM = await new keyman.Key('oct', this.hex_to_buf(pubkey), { namedCurve: 'P-256K' }).export('pem') // 公钥导出的der格式为88字节。经测试,同一对压缩和非压缩公钥得出的结果一模一样。
|
||||||
let verifier = crypto.createVerify(my.HASHER_LIST.indexOf(hasher) >= 0 ? hasher : my.HASHER)
|
let verifier = crypto.createVerify(my.HASHER_LIST.indexOf(hasher) >= 0 ? hasher : my.HASHER)
|
||||||
verifier.update(this.hash(data)).end() // end() 在 nodejs 12 里返回verifier自身,但在浏览器里返回 undefined,因此不能串联运行。
|
verifier.update(this.hash(data)).end() // end() 在 nodejs 12 里返回verifier自身,但在浏览器里返回 undefined,因此不能串联运行。
|
||||||
let verified = verifier.verify(pubkeyPEM, signature, 'hex') // 如果给signature添加1位hex,crypto 的 verify结果也是true! 估计因为一位hex不被转成字节。但减少1位会导致false
|
let verified = verifier.verify(pubkeyPEM, signature, 'hex') // 如果给signature添加1位hex,crypto 的 verify结果也是true! 估计因为一位hex不被转成字节。但减少1位会导致false
|
||||||
@ -554,13 +556,13 @@ class TICrypto {
|
|||||||
curve = my.CURVE_LIST.indexOf(curve) >= 0 ? curve : my.CURVE // 默认为 secp256k1
|
curve = my.CURVE_LIST.indexOf(curve) >= 0 ? curve : my.CURVE // 默认为 secp256k1
|
||||||
// return new crypto.createECDH(curve).setPrivateKey(seckey,'hex').getPublicKey('hex', compress===false?'uncompressed':'compressed') // ecdh.getPublicKey(不加参数) 默认为 'compressed'。用 HBuilderX 2.6.4 打包成ios或安卓 app 后 setPrivateKey() 报错:TypeError: null is not an object (evaluating 'this.rand.getBytes')
|
// return new crypto.createECDH(curve).setPrivateKey(seckey,'hex').getPublicKey('hex', compress===false?'uncompressed':'compressed') // ecdh.getPublicKey(不加参数) 默认为 'compressed'。用 HBuilderX 2.6.4 打包成ios或安卓 app 后 setPrivateKey() 报错:TypeError: null is not an object (evaluating 'this.rand.getBytes')
|
||||||
// 从 nodejs 10.0 开始,还有 crypto.ECDH.convertKey 方法,更直接。但可惜,浏览器里不存在 crypto.ECDH。
|
// 从 nodejs 10.0 开始,还有 crypto.ECDH.convertKey 方法,更直接。但可惜,浏览器里不存在 crypto.ECDH。
|
||||||
return this.buf2hex(secp256k1.publicKeyCreate(Buffer.from(seckey, 'hex'), compress !== false)) // 可用于浏览器。缺省输出压缩公钥,compress=false时输出非压缩公钥。
|
return this.buf_to_hex(secp256k1.publicKeyCreate(Buffer.from(seckey, 'hex'), compress !== false)) // 可用于浏览器。缺省输出压缩公钥,compress=false时输出非压缩公钥。
|
||||||
// 或者 bitcorelib.PublicKey.fromPrivateKey(new bitcorelib.PrivateKey(seckey)).toString('hex') // 可用于浏览器
|
// 或者 bitcorelib.PublicKey.fromPrivateKey(new bitcorelib.PrivateKey(seckey)).toString('hex') // 可用于浏览器
|
||||||
// 或者 const ecc = require('eccrypto')
|
// 或者 const ecc = require('eccrypto')
|
||||||
// if (compress===false){
|
// if (compress===false){
|
||||||
// return ecc.getPublic(this.hex2buf(seckey)).toString('hex')
|
// return ecc.getPublic(this.hex_to_buf(seckey)).toString('hex')
|
||||||
// }else{
|
// }else{
|
||||||
// return ecc.getPublicCompressed(this.hex2buf(seckey)).toString('hex')
|
// return ecc.getPublicCompressed(this.hex_to_buf(seckey)).toString('hex')
|
||||||
// }
|
// }
|
||||||
// 注意,Buffer.from(nacl.box.keyPair.fromSecretKey(Buffer.from(seckey,'hex')).publicKey).toString('hex') 得到的公钥与上面的不同
|
// 注意,Buffer.from(nacl.box.keyPair.fromSecretKey(Buffer.from(seckey,'hex')).publicKey).toString('hex') 得到的公钥与上面的不同
|
||||||
} else if (this.isSeckey(seckey) && seckey.length === 128) {
|
} else if (this.isSeckey(seckey) && seckey.length === 128) {
|
||||||
@ -685,7 +687,7 @@ class TICrypto {
|
|||||||
address = bs58check.encode(Buffer.from(prefix + position, 'hex')) // wallet import format
|
address = bs58check.encode(Buffer.from(prefix + position, 'hex')) // wallet import format
|
||||||
return address
|
return address
|
||||||
} else {
|
} else {
|
||||||
// 默认为 TIC。把纯位置转换为大小写敏感能自我验证的 b64u(base64 for url) 地址。
|
// 默认为 TIC。把纯位置转换为大小写敏感能自我验证的 b64t 地址。
|
||||||
let prefix
|
let prefix
|
||||||
switch (world) {
|
switch (world) {
|
||||||
// Base64: https://baike.baidu.com/item/base64
|
// Base64: https://baike.baidu.com/item/base64
|
||||||
@ -702,8 +704,8 @@ class TICrypto {
|
|||||||
prefix = '4c'
|
prefix = '4c'
|
||||||
}
|
}
|
||||||
let checksum = this.hash(this.hash(prefix + position)).slice(0, 6) // 添加 checksum 使得能够检测大小写错误。[todo] 校验码里要不要包含 prefix?
|
let checksum = this.hash(this.hash(prefix + position)).slice(0, 6) // 添加 checksum 使得能够检测大小写错误。[todo] 校验码里要不要包含 prefix?
|
||||||
// address = this.hex2eip55(prefix + position + checksum) // 前缀1节,位置20节,校验3节,共24节=48字符(能够完全转化为8个色彩),再转eip55。
|
// address = this.hex_to_eip55(prefix + position + checksum) // 前缀1节,位置20节,校验3节,共24节=48字符(能够完全转化为8个色彩),再转eip55。
|
||||||
address = this.hex2b64u(prefix + position + checksum) // 实际采用 b64u (named by luk.lu as base 64 for url), 共 32字符。
|
address = this.hex_to_b64t(prefix + position + checksum) // 实际采用 b64t, 共 32字符。
|
||||||
return address
|
return address
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
@ -722,13 +724,13 @@ class TICrypto {
|
|||||||
// todo: 如果是大小写敏感的,进行有效性验证
|
// todo: 如果是大小写敏感的,进行有效性验证
|
||||||
return address.toLowerCase()
|
return address.toLowerCase()
|
||||||
} else if (/^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{26,34}$/.test(address)) { // BTC
|
} else if (/^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{26,34}$/.test(address)) { // BTC
|
||||||
let hex = this.b58c2hex(address)
|
let hex = this.b58c_to_hex(address)
|
||||||
if (hex) {
|
if (hex) {
|
||||||
return hex.slice(2) // 去除网络前缀
|
return hex.slice(2) // 去除网络前缀
|
||||||
}
|
}
|
||||||
} else if (/^[Tt][0-9a-zA-Z\._]{31}$/.test(address)) { // TIC
|
} else if (/^[Tt][0-9a-zA-Z\._]{31}$/.test(address)) { // TIC
|
||||||
// 格式合法
|
// 格式合法
|
||||||
let hex = this.b64u2hex(address)
|
let hex = this.b64t_to_hex(address)
|
||||||
let [all, prefix, position, checksum] = hex.match(/^([\da-fA-F]{2})([\da-fA-F]{40})([\da-fA-F]{6})$/)
|
let [all, prefix, position, checksum] = hex.match(/^([\da-fA-F]{2})([\da-fA-F]{40})([\da-fA-F]{6})$/)
|
||||||
if (this.hash(this.hash(position)).slice(0, 6) === checksum) {
|
if (this.hash(this.hash(position)).slice(0, 6) === checksum) {
|
||||||
return position
|
return position
|
||||||
@ -750,13 +752,13 @@ class TICrypto {
|
|||||||
return 'ETH'
|
return 'ETH'
|
||||||
} else if (/^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{26,34}$/.test(address) && address.length !== 32) {
|
} else if (/^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{26,34}$/.test(address) && address.length !== 32) {
|
||||||
// 格式合法。常见的是 33或34字符长度
|
// 格式合法。常见的是 33或34字符长度
|
||||||
let prefixedPosition = this.b58c2hex(address)
|
let prefixedPosition = this.b58c_to_hex(address)
|
||||||
if (prefixedPosition && prefixedPosition.length === 42)
|
if (prefixedPosition && prefixedPosition.length === 42)
|
||||||
// 内容合法
|
// 内容合法
|
||||||
return 'BTC'
|
return 'BTC'
|
||||||
} else if (/^[Ttd][0-9a-zA-Z\._]{31}$/.test(address)) {
|
} else if (/^[Ttd][0-9a-zA-Z\._]{31}$/.test(address)) {
|
||||||
// 格式合法
|
// 格式合法
|
||||||
let hex = Buffer.from(this.b64u_to_b64(address), 'base64').toString('hex')
|
let hex = Buffer.from(this.b64t_to_b64(address), 'base64').toString('hex')
|
||||||
let [all, prefix, position, checksum] = hex.match(/^([\da-fA-F]{2})([\da-fA-F]{40})([\da-fA-F]{6})$/) // 内容合法
|
let [all, prefix, position, checksum] = hex.match(/^([\da-fA-F]{2})([\da-fA-F]{40})([\da-fA-F]{6})$/) // 内容合法
|
||||||
if (this.hash(this.hash(prefix + position)).slice(0, 6) === checksum)
|
if (this.hash(this.hash(prefix + position)).slice(0, 6) === checksum)
|
||||||
// [todo] 校验码里要不要包含 prefix?
|
// [todo] 校验码里要不要包含 prefix?
|
||||||
@ -1181,7 +1183,7 @@ class TICrypto {
|
|||||||
* @return {*}
|
* @return {*}
|
||||||
* @memberof TICrypto
|
* @memberof TICrypto
|
||||||
*/
|
*/
|
||||||
static buf2hex(buffer) {
|
static buf_to_hex(buffer) {
|
||||||
// buffer is an ArrayBuffer
|
// buffer is an ArrayBuffer
|
||||||
return Array.prototype.map.call(new Uint8Array(buffer), (x) => ('00' + x.toString(16)).slice(-2)).join('')
|
return Array.prototype.map.call(new Uint8Array(buffer), (x) => ('00' + x.toString(16)).slice(-2)).join('')
|
||||||
}
|
}
|
||||||
@ -1194,7 +1196,7 @@ class TICrypto {
|
|||||||
* @return {*}
|
* @return {*}
|
||||||
* @memberof TICrypto
|
* @memberof TICrypto
|
||||||
*/
|
*/
|
||||||
static hex2buf(hex) {
|
static hex_to_buf(hex) {
|
||||||
return new Uint8Array(
|
return new Uint8Array(
|
||||||
hex.match(/[\da-f]{2}/gi).map(function (h) {
|
hex.match(/[\da-f]{2}/gi).map(function (h) {
|
||||||
return parseInt(h, 16)
|
return parseInt(h, 16)
|
||||||
@ -1213,7 +1215,7 @@ class TICrypto {
|
|||||||
* bs58check 和 bs58 可接受string, Buffer, ArrayBuffer, Array (包括空字符串'', 各种内容的数组例如包含 undefined,{...},等等);
|
* bs58check 和 bs58 可接受string, Buffer, ArrayBuffer, Array (包括空字符串'', 各种内容的数组例如包含 undefined,{...},等等);
|
||||||
* 不可接受 undefined, null, {...}, 等等,会返回 exception
|
* 不可接受 undefined, null, {...}, 等等,会返回 exception
|
||||||
*/
|
*/
|
||||||
static hex2b58c(hex) {
|
static hex_to_b58c(hex) {
|
||||||
try {
|
try {
|
||||||
return bs58check.encode(Buffer.from(hex, 'hex'))
|
return bs58check.encode(Buffer.from(hex, 'hex'))
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
@ -1221,7 +1223,7 @@ class TICrypto {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static hex2b58(hex) {
|
static hex_to_b58(hex) {
|
||||||
try {
|
try {
|
||||||
return bs58.encode(Buffer.from(hex, 'hex'))
|
return bs58.encode(Buffer.from(hex, 'hex'))
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
@ -1237,7 +1239,7 @@ class TICrypto {
|
|||||||
* @return {*}
|
* @return {*}
|
||||||
* @memberof TICrypto
|
* @memberof TICrypto
|
||||||
*/
|
*/
|
||||||
static b58c2hex(box) {
|
static b58c_to_hex(box) {
|
||||||
try {
|
try {
|
||||||
return bs58check.decode(box).toString('hex')
|
return bs58check.decode(box).toString('hex')
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
@ -1245,7 +1247,7 @@ class TICrypto {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static b582hex(box) {
|
static b58_to_hex(box) {
|
||||||
try {
|
try {
|
||||||
return bs58.decode(box).toString('hex')
|
return bs58.decode(box).toString('hex')
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
@ -1256,45 +1258,71 @@ class TICrypto {
|
|||||||
/**
|
/**
|
||||||
* b64 字符串为 a-zA-Z0-9+/
|
* b64 字符串为 a-zA-Z0-9+/
|
||||||
* 其中,+ 和 / 会在 url query string 里被转成 %2B 和 %2F
|
* 其中,+ 和 / 会在 url query string 里被转成 %2B 和 %2F
|
||||||
* 因此定义 b64u (base64 for url),用 . 和 _ 替换。
|
* 因此定义 b64t (base64 for tic),用 . 和 _ 替换。
|
||||||
* (为何不用 -,因为 - 和空格一样导致 css white-space 自动换行。)
|
* (为何不用 base64url, 因为 base64url 把 + 变成 - 和空格一样导致 css white-space 自动换行。)
|
||||||
* @param {*} b64
|
* @param {*} b64
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
static b64_to_b64u(b64='') {
|
static b64_to_b64t(b64='') {
|
||||||
return b64.replace(/\+/g, '.').replace(/\//g, '_').replace(/=/g, '')
|
return b64.replace(/\+/g, '.').replace(/\//g, '_').replace(/=/g, '')
|
||||||
}
|
}
|
||||||
|
|
||||||
static b64u_to_b64(b64u='') {
|
static b64t_to_b64(b64t='') {
|
||||||
return b64u.replace(/\./g, '+').replace(/_/g, '/')
|
return b64t.replace(/\./g, '+').replace(/_/g, '/')
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 十六进制转b64u
|
* 十六进制转b64t
|
||||||
*
|
*
|
||||||
* @static
|
* @static
|
||||||
* @param {*} hex
|
* @param {*} hex
|
||||||
* @return {*}
|
* @return {*}
|
||||||
* @memberof TICrypto
|
* @memberof TICrypto
|
||||||
*/
|
*/
|
||||||
static hex2b64u(hex) {
|
static hex_to_b64t(hex) {
|
||||||
if (/^[0-9a-fA-F]+$/.test(hex)) {
|
if (/^[0-9a-fA-F]+$/.test(hex)) {
|
||||||
return this.b64_to_b64u(Buffer.from(hex, 'hex').toString('base64'))
|
return this.b64_to_b64t(Buffer.from(hex, 'hex').toString('base64'))
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* b64u转16进制
|
* b64t转16进制
|
||||||
*
|
*
|
||||||
* @static
|
* @static
|
||||||
* @param {*} b64u
|
* @param {*} b64t
|
||||||
* @return {*}
|
* @return {*}
|
||||||
* @memberof TICrypto
|
* @memberof TICrypto
|
||||||
*/
|
*/
|
||||||
static b64u2hex(b64u) {
|
static b64t_to_hex(b64t) {
|
||||||
if (/^[0-9a-zA-Z\._]+$/.test(b64u)) {
|
if (/^[0-9a-zA-Z\._]+$/.test(b64t)) {
|
||||||
return Buffer.from(this.b64u_to_b64(b64u), 'base64').toString('hex')
|
return Buffer.from(this.b64t_to_b64(b64t), 'base64').toString('hex')
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://en.wikipedia.org/wiki/Base32
|
||||||
|
static hex_to_b32(hex, {encoding='RFC4648'}={}) {
|
||||||
|
if (/^[0-9a-fA-F]+$/.test(hex)) {
|
||||||
|
return base32encode(Buffer.from(hex, 'hex'), encoding)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
static b32_to_hex(b32, {encoding='RFC4648'}={}) {
|
||||||
|
if (/^[A-Za-z2-7=]+$/.test(b32)) {
|
||||||
|
return Buffer.from(base32decode(b32.toUpperCase(), encoding)).toString('hex')
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
static hex_to_b32h(hex, {encoding='RFC4648-HEX'}={}) {
|
||||||
|
if (/^[0-9a-fA-F]+$/.test(hex)) {
|
||||||
|
return base32encode(Buffer.from(hex, 'hex'), encoding)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
static b32h_to_hex(b32, {encoding='RFC4648-HEX'}={}) {
|
||||||
|
if (/^[0-9A-Va-v=]+$/.test(b32)) {
|
||||||
|
return Buffer.from(base32decode(b32.toUpperCase(), encoding)).toString('hex')
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@ -1307,8 +1335,8 @@ class TICrypto {
|
|||||||
* @return {*}
|
* @return {*}
|
||||||
* @memberof TICrypto
|
* @memberof TICrypto
|
||||||
*/
|
*/
|
||||||
static hex2eip55(hex) {
|
static hex_to_eip55(hex) {
|
||||||
if (/^(0x)?[\da-fA-F]*$/.test(hex)) {
|
if (/^(0x)?[\da-fA-F]+$/.test(hex)) {
|
||||||
hex = hex.toLowerCase().replace('0x', '')
|
hex = hex.toLowerCase().replace('0x', '')
|
||||||
let hash = keccak('keccak256').update(hex).digest('hex')
|
let hash = keccak('keccak256').update(hex).digest('hex')
|
||||||
let result = ''
|
let result = ''
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"base32-decode": "^1.0.0",
|
||||||
|
"base32-encode": "^1.2.0",
|
||||||
"big-integer": "^1.6.48",
|
"big-integer": "^1.6.48",
|
||||||
"bip39": "^3.0.2",
|
"bip39": "^3.0.2",
|
||||||
"bs58check": "^2.1.2",
|
"bs58check": "^2.1.2",
|
||||||
|
2
test.js
2
test.js
@ -76,7 +76,7 @@ console.log('address = ', add)
|
|||||||
/////////////////////// keyutil
|
/////////////////////// keyutil
|
||||||
|
|
||||||
let seckeyObject = new keyutil.Key('oct', Buffer.from(acc.seckey, 'hex'), {namedCurve:'P-256K'}) // {P-256 : secp256r1, P-384 : secp384r1, P-521 : secp521r1, P-256K : secp256k1}
|
let seckeyObject = new keyutil.Key('oct', Buffer.from(acc.seckey, 'hex'), {namedCurve:'P-256K'}) // {P-256 : secp256r1, P-384 : secp384r1, P-521 : secp521r1, P-256K : secp256k1}
|
||||||
let seckeyObject2 = new keyutil.Key('oct', tic.hex2buf(acc.seckey, 'hex'), {namedCurve:'P-256K'})
|
let seckeyObject2 = new keyutil.Key('oct', tic.hex_to_buf(acc.seckey, 'hex'), {namedCurve:'P-256K'})
|
||||||
let seckeyPEM
|
let seckeyPEM
|
||||||
seckeyObject.export('pem').then(data=>seckeyPEM=data)
|
seckeyObject.export('pem').then(data=>seckeyPEM=data)
|
||||||
let seckeyDER
|
let seckeyDER
|
||||||
|
Loading…
Reference in New Issue
Block a user