
瑞波币--私钥解析
Hello , 我是09
此篇内容为我2018~2020年在【库神】工作时,对部分重要知识做的学习总结
随着比特币冲破10万美金,行业又再次兴起,希望相关从业者可以学习到你需要的内容
前提
瑞波币市面上能见到的私钥大概分为两种。
(1). 根据助记词,通过分层确定性一步一步通过Secp256K1生成的32个Byte64个长度的私钥。
例: 2a6b9bf9a391f0055dbfe70c0ebc7393a81513377c53d276d63f3d243a079dca
(2). 瑞波币官方使用的私钥最后通过base58得到的长度为29的私钥。
例:shtrUE17GiDF27C9yan4E55vfCsR。
本文主要讲解瑞波币官方使用的私钥格式。
私钥解析
解析场景:
种子:shtrUE17GiDF27CV9yan4E55vfCsR
算法:Secp256k1
特殊:超级长的大整数的和以及模运算。
如果大家用的Secp256k1,在分层确定性中,就有通过(a + b) % c的方法,如果没有那只能自己写了,作者这写过一套大数加法,如果需要可以拿去。
#pragma mark -- 超级大的两个数相加(两个长度在100位以内的数字测试没有问题) --
- (NSString )addSuperBigNumber:(NSString )number
{
// 1. 初始化部分参数
BOOL isCarry = 0; // 是否需要进位
NSInteger total_len = self.length > number.length ? self.length : number.length;
NSString *sum_str = [NSString string];
// 2. 循环计算
for (NSInteger i = 1; i < total_len + 1; i++) {
@autoreleasepool {
// 2.1 取出两个加数的对应位置上的数字
int a = self.length >= i ? [[self substringWithRange:NSMakeRange(self.length - i, 1)] intValue] : 0;
int b = number.length >= i ? [[number substringWithRange:NSMakeRange(number.length - i, 1)] intValue] : 0;
// 2.2 计算a + b + 进位
int sum = a + b + (int)isCarry;
// 2.3 拼接和
sum_str = [NSString stringWithFormat:@"%d%@",sum % 10,sum_str];
// 2.4 计算是否有进位
isCarry = sum > 9 ? YES : NO;
if (i == total_len && isCarry) {
sum_str = [NSString stringWithFormat:@"1%@",sum_str];
}
}
}
return sum_str;
}
大家可以根据自己的需求来添加判断条件,网络上也可以搜索到C函数的方法大家自行选择。
解析重点
这个是XRP解析的hash散列的计算方式,作者写的时候参考的官方JAVA代码,作者OC的代码如下:
#define SECP256K1_ORDER @“115792089237316195423570985008687907852837564279074904382605163141518161494337"
+ (NSData )p_SpacingAndHash:(NSData )data discriminator:(int)discriminator
{
// 1. 实例化可变的要进行拼接的data,并赋值
NSMutableData *spacing_data = [NSMutableData dataWithData:data];
// 2. 实例化一个返回Data
NSData *result_data = [NSData data];
for (long i = 0L; i <= 4294967295L; i++) {
// 3. 判别器,判别是否需要进行拼接数据
if (discriminator >= 0) {
[spacing_data appendUInt32:discriminator];
}
// 4. 拼接i
[spacing_data appendUInt32:(int)i];
// 5. 进行SHA512操作,并截取前32个byte
result_data = [spacing_data.SDK_SHA512 subdataWithRange:NSMakeRange(0, 32)];
// 6. 将结果转换为10机制
NSString *decimalString = [KSHotWalletBigNumber bigNumberWithData:result_data].decimalString;
// 7. 判断结果是否大于算法的N值,这个值是固定的,要求:1 < 结果要求必须 < N值,如果满足就Break,不满足就继续循环
if ([[SECP256K1_SORT minusSuperBigNumber:decimalString] isEqualToString:@"0"]) {
continue;
}else if ([[decimalString minusSuperBigNumber:@"1"] isEqualToString:@"0"]) {
continue;
}else{
break;
}
}
return result_data;
}
开始解析
第一步:将私钥进行Base58解码。
NSData *private_base58_encode = [privateKey dataUsingEncoding:NSUTF8StringEncoding];
NSMutableData *private_base58_decode = [NSMutableData secureDataWithLength:21];
xrp_decode_base58(private_base58_encode.bytes, private_base58_encode.length, private_base58_decode.mutableBytes);
// 解码后的结果
po [NSString hexWithData:private_base58_decode]
结果:219ccd8eff8f7a84600a8f6fe5ca2ba4b8268ac004
第二步,截取私钥,就是去掉第一步得到的私钥中的Version(版本号)以及CheckCode(校验码)。
私钥结构:
219ccd8eff8f7a84600a8f6fe5ca2ba4b8268ac004
version + private + hash校验码
// 截取私钥
NSData *private_data = [private_base58_decode subdataWithRange:NSMakeRange(1, private_base58_decode.length - 5)];
// 我们得到截取后的私钥
po [NSString hexWithData:private_data]
结果:9ccd8eff8f7a84600a8f6fe5ca2ba4b8
第三步,将私钥进行hash散列运算,方法上面已经po上了。
NSData *private_hash_data = [self p_SpacingAndHash:private_data discriminator:-1];
// 私钥散列结果
po [NSString hexWithData:private_hash_data]
结果:f938695eae97422459372885edabe031a635c52447858f9051f5ed98c27ab185
第四步,我们通过第第三步得到的散列结果推算出公钥,这里用到的Secp256k1,算法不做过多介绍。
NSData pub_data = [KSHotWalletImplement_PublicKey o_GetPublicKeyWithSeckey:(const UInt256 *)data_in_0_sha512.bytes slip44:144 compressed:YES];
// 十六进制公钥
po [NSString hexWithData:pub_data]
结果:02f9e0fa55dc0d9111b84da7688215c72dbd7eea6aeee063240fdbf020d194707f
第五步,将公钥进行hash散列运算,结果就是我们终签名秘钥的第一部分。
NSData *public_hash_data = [self p_SpacingAndHash:pub_data discriminator:0];
// 公钥散列结果
po [NSString hexWithData:public_hash_data]
结果:3133329af4faade10488be8621109360bc8e2af9e416e322441bae1847c32d86
第六步,计算秘钥 秘钥 = (私钥散列 + 公钥散列)mod secp256k1 order
UInt256 result_256 = (const UInt256 )private_hash_data.bytes;
UInt256 result_256_2 = (const UInt256 )public_hash_data.bytes;
KSBRSecp256k1ModAdd(&result_256, &result_256_2);
// adds 256bit big endian ints a and b (mod secp256k1 order) and stores the result in a
// returns true on success
int KSBRSecp256k1ModAdd(UInt256 a, const UInt256 b)
// 最终秘钥的十六进制
结果:2a6b9bf9a391f0055dbfe70c0ebc7393a81513377c53d276d63f3d243a079dca
到这里我们就成功的解出来正确的瑞波币秘钥了!
作者举例用的私钥是随机生成的,里面没有币,大家不要白费力气在这个上面哦。
注意点
瑞波币私钥解析的散列计算函数的时候,一定要注意作者po的代码,hash出一个正确的私钥必须要小于secp256k1 order。
调用瑞波币散列计算函数,私钥散列和公钥三笠传入的判别器的值是不一样的,一定要注意!
由于OC没有相关的资源,大部分项目为了实现功能都是直接继承JavaScript,通过交互实现交易等功能,这导致圈里为数不多的开发者根本不知道解析都做了什么,如果没有资料根本无法实现功能。所以作者极力不推荐这种方式。很多做过以太坊等钱包的小伙伴来面试,一问三不知,希望大家既然选择做这一块,就一定要做到懂这一块!
以上为纯OC原生开发的总结,如果有问题欢迎大家留言提出!