[英]How to decode or convert base64 string url to UintList in Dart?
[英]how to convert AsymmetricKeyPair to base64 encoding string in dart
我是 PointyCastle 的新手,如何在 dart 中使用 PointyCastle 生成 ecc base64密钥对
AsymmetricKeyPair<PublicKey, PrivateKey> generateKeyPair({curve = 'secp256r1'}) {
var param = curve == 'secp256r1' ? ECCurve_secp256r1() : ECCurve_secp256k1();
var keyParams = ECKeyGeneratorParameters(param);
var random = FortunaRandom();
random.seed(KeyParameter(_seed()));
var generator = ECKeyGenerator();
generator.init(ParametersWithRandom(keyParams, random));
return generator.generateKeyPair();
}
Uint8List _seed() {
var random = Random.secure();
var seed = List<int>.generate(32, (_) => random.nextInt(256));
return Uint8List.fromList(seed);
}
上面我可以生成一个 AsymmetricKeyPair 对象,如何像我已经完成的 js 包一样获取公共 base64 和私有 base64
{"priKey":"CVK3r/UxdGCwQBjtn5vN/orUMxKf9E/1TlJzLkMz9t4=","pubKey":"BJH/mWJqgchGxXGA5/E79SsWRwVo3rpduBmD8FOs7UlKiK8PIvwrkCDvUcwhKdysW35OByPjoVcwFqg1NyumLKM="}
此外,起初我想在flutter android中使用我的js包文件,但据我所知,这将非常困难
我还需要在 sha256 中使用 ecdsa 进行签名和验证功能
Signer signer = new Signer('SHA-256/ECDSA');
// 签名,参数:私钥,明文
ECSignature sign(String privateKeyStr, String txt) {
SecureRandom random = _setRadom();
// how to convert a plain txt to kTestBytes
final Uint8List kTestBytes = new Uint8List.fromList([1, 2, 3]);
// if I pass in base64 privateKey str, is the radix 64?
ECPrivateKey privateKey = new ECPrivateKey(BigInt.parse(privateKeyStr, radix: 10), ecDomain);
PrivateKeyParameter signParams = new PrivateKeyParameter(privateKey);
signer.init(true, new ParametersWithRandom(signParams, random));
ECSignature signature = signer.generateSignature(kTestBytes) as ECSignature;
return signature;
}
// 验签
// 参数: 明文,签名,公钥
bool verify(txt, signature, publicKey) {
// how to make the txt to kTestBytes
var kTestBytes = txt
signer.init(false, new PublicKeyParameter(publicKey));
bool verify = signer.verifySignature(kTestBytes, signature);
// print(verify);
return verify;
}
// I don't know what this function actually do,as I know in other language ecdsa don;t need a random number.
SecureRandom _setRadom() {
List<int> key = [67, 3, 241, 75, 143, 78, 115, 99, 21, 242, 180, 43, 26, 7, 194, 20];
List<int> iv = [87, 117, 137, 182, 2, 199, 132, 230, 120, 12, 109, 177, 34, 197, 186, 206];
KeyParameter keyParam = new KeyParameter(new Uint8List.fromList(key));
ParametersWithIV<KeyParameter> paramsIV = new ParametersWithIV(keyParam, new Uint8List.fromList(iv));
SecureRandom random = new SecureRandom('AES/CTR/AUTO-SEED-PRNG')..seed(paramsIV);
return random;
}
我在任何地方都可以找到示例代码,这些功能被互联网上的一些副本更改。我在评论中得到了很多混淆列表
EC 密钥将作为 Base64 编码的原始私钥和 Base64 编码的原始未压缩公钥导出/导入。
首先可以说,当生成新的密钥对时,使用发布的代码进行签名和验证是有效的:
AsymmetricKeyPair<PublicKey, PrivateKey> kp = generateKeyPair();
ECSignature signature = sign(kp.privateKey as ECPrivateKey, "The quick brown fox jumps over the lazy dog");
bool verified = verify("The quick brown fox jumps over the lazy dog", signature, kp.publicKey as ECPublicKey);
print(verified); // true
即在下面我将重点介绍密钥的导入/导出。 原始密钥封装在ECPrivateKey
和ECPublicKey
中,可以按如下方式导出和导入:
String p256 = 'secp256r1';
AsymmetricKeyPair<PublicKey, PrivateKey> kp = generateKeyPair(curve: p256);
// Export
BigInt d = (kp.privateKey as ECPrivateKey).d!;
BigInt x = (kp.publicKey as ECPublicKey).Q!.x!.toBigInteger()!;
BigInt y = (kp.publicKey as ECPublicKey).Q!.y!.toBigInteger()!;
// Import
ECDomainParameters domain = ECDomainParameters(p256);
ECPrivateKey ecPrivateKey = ECPrivateKey(d, domain);
ECPublicKey ecPublicKey = ECPublicKey(domain.curve.createPoint(x, y), domain);
这里d
是原始私钥, x
和y
是原始公钥的 x 和 y 坐标。
在 JavaScript 端,使用 Base64 编码的原始私钥和 Base64 编码的原始未压缩密钥,其中未压缩密钥是串联0x04 + <x> + <y>
。 因此,对于导出和导入,以下适用:
关于导出,Base64 编码的原始私钥和公钥可以从d
、 x
和y
派生如下:
String rawPrivateB64 = exportPrivate(d); String rawUncompressedPublicB64 = exportPublic(x, y, 32);
和
import 'dart:convert'; import 'package:nanodart/nanodart.dart'; String exportPrivate(BigInt d){ return base64Encode(NanoHelpers.bigIntToBytes(d)); } String exportPublic(BigInt x, BigInt y, int size){ return base64Encode(NanoHelpers.concat([Uint8List.fromList([4]), pad(NanoHelpers.bigIntToBytes(x), size), pad(NanoHelpers.bigIntToBytes(y), size)])); } Uint8List pad(Uint8List list, int size){ Uint8List padded = list; int currSize = list.length; if (currSize < size){ Uint8List pad = Uint8List(size - currSize); padded = NanoHelpers.concat([pad, list]); } return padded; }
作为第三个参数传入exportPublic()
的大小是生成器点的顺序大小(secp256r1 为 32 字节)。 如果x
或y
更小,则它们各自从前面用 0x00 值填充,直到达到所需的长度。
对于从BigInt
到Uint8List
的转换以及连接,为了简单起见,我使用了NanoDart包中的NanoHelpers
类。 当然,这里也可以使用其他实现方式。
关于导入, d
、 x
和y
可以从 Base64 编码的原始私钥和公钥导出,如下所示:
BigInt d = importPrivate(rawPrivateB64); List xy = importPublic(rawUncompressedPublicB64); BigInt x = xy.elementAt(0); BigInt y = xy.elementAt(1);
和
BigInt importPrivate(String d){ return NanoHelpers.byteToBigInt(base64Decode(d)); } List importPublic(String xy){ Uint8List xyBytes = base64Decode(xy); xyBytes = xyBytes.sublist(1, xyBytes.length); int size = xyBytes.length ~/ 2; Uint8List x = xyBytes.sublist(0, size); Uint8List y = xyBytes.sublist(size); return [NanoHelpers.byteToBigInt(x), NanoHelpers.byteToBigInt(y)]; }
发布的密钥对
{"priKey":"CVK3r/UxdGCwQBjtn5vN/orUMxKf9E/1TlJzLkMz9t4=","pubKey":"BJH/mWJqgchGxXGA5/E79SsWRwVo3rpduBmD8FOs7UlKiK8PIvwrkCDvUcwhKdysW35OByPjoVcwFqg1NyumLKM="}
可以导入并用于签名和验证,如下所示:
String rawPrivateB64 = "CVK3r/UxdGCwQBjtn5vN/orUMxKf9E/1TlJzLkMz9t4=";
String rawUncompressedPublicB64 = "BJH/mWJqgchGxXGA5/E79SsWRwVo3rpduBmD8FOs7UlKiK8PIvwrkCDvUcwhKdysW35OByPjoVcwFqg1NyumLKM=";
BigInt d = importPrivate(rawPrivateB64);
List xy = importPublic(rawUncompressedPublicB64);
BigInt x = xy.elementAt(0);
BigInt y = xy.elementAt(1);
ECDomainParameters domain = ECDomainParameters('secp256r1');
ECPrivateKey private = ECPrivateKey(d, domain);
ECPublicKey public = ECPublicKey(domain.curve.createPoint(x, y), domain);
ECSignature signature = sign(private, "The quick brown fox jumps over the lazy dog");
bool verified = verify("The quick brown fox jumps over the lazy dog", signature, public);
print(verified); // true
编辑:关于您的评论: sign()
和verify()
是您发布的方法,但根据更改,现在直接传递键(而不是字符串)并应用实际消息(而不是[1,2,3]
) 使用utf8.encode()
进行 UTF-8 编码:
import 'dart:convert';
ECSignature sign(ECPrivateKey privateKey, String txt) {
SecureRandom random = _setRandom();
Uint8List txtBytes = Uint8List.fromList(utf8.encode(txt));
PrivateKeyParameter signParams = PrivateKeyParameter(privateKey);
signer.init(true, ParametersWithRandom(signParams, random));
ECSignature signature = signer.generateSignature(txtBytes) as ECSignature;
return signature;
}
bool verify(String txt, ECSignature signature, ECPublicKey publicKey) {
signer.init(false, PublicKeyParameter(publicKey));
bool verify = signer.verifySignature(Uint8List.fromList(utf8.encode(txt)), signature);
return verify;
}
作为_setRandom()
我应用了generateKeyPair()
的实现而不是您的实现(即基于FortunaRandom()
的 CSPRNG)。
我的整个代码基于接受的答案
加密飞镖
import "dart:typed_data";
import "dart:math";
import 'dart:convert';
import "package:pointycastle/export.dart";
import './utils.dart';
Signer signer = new Signer('SHA-256/ECDSA');
class Crypto {
final ECDomainParameters ecDomain = new ECDomainParameters('secp256r1');
/// 公私钥对生成
/// 关于公私钥encoding:https://stackoverflow.com/questions/72641616/how-to-convert-asymmetrickeypair-to-base64-encoding-string-in-dart
///
Map generateKeyPair({curve = 'secp256r1'}) {
var param = curve == 'secp256r1' ? ECCurve_secp256r1() : ECCurve_secp256k1();
var keyParams = ECKeyGeneratorParameters(param);
var random = FortunaRandom();
random.seed(KeyParameter(this._seed(32)));
var generator = ECKeyGenerator();
generator.init(ParametersWithRandom(keyParams, random));
AsymmetricKeyPair<PublicKey, PrivateKey> kp = generator.generateKeyPair();
BigInt d = (kp.privateKey as ECPrivateKey).d!;
BigInt x = (kp.publicKey as ECPublicKey).Q!.x!.toBigInteger()!;
BigInt y = (kp.publicKey as ECPublicKey).Q!.y!.toBigInteger()!;
String rawPrivateB64 = exportPrivate(d);
String rawUncompressedPublicB64 = exportPublic(x, y, 32);
return {'base64Pub': rawUncompressedPublicB64, 'base64Priv': rawPrivateB64};
}
Uint8List _seed(size) {
var random = Random.secure();
var seed = List<int>.generate(size, (_) => random.nextInt(256));
return Uint8List.fromList(seed);
}
// TODO
// Restore the ECPrivateKey from 'd'.
restoreKeyFromPrivate(privateKeyStr) {
ECPrivateKey privateKey = new ECPrivateKey(BigInt.parse(privateKeyStr, radix: 10), ecDomain);
ECPoint Q = privateKey.parameters!.G * privateKey.d as ECPoint;
ECPublicKey publicKey = new ECPublicKey(Q, privateKey.parameters);
return publicKey;
}
ECSignature sign(ECPrivateKey privateKey, String txt) {
SecureRandom random = _setRandom();
Uint8List txtBytes = Uint8List.fromList(utf8.encode(txt));
PrivateKeyParameter signParams = PrivateKeyParameter(privateKey);
signer.init(true, ParametersWithRandom(signParams, random));
ECSignature signature = signer.generateSignature(txtBytes) as ECSignature;
return signature;
}
bool verify(String txt, ECSignature signature, ECPublicKey publicKey) {
signer.init(false, PublicKeyParameter(publicKey));
bool verify = signer.verifySignature(Uint8List.fromList(utf8.encode(txt)), signature);
return verify;
}
SecureRandom _setRandom() {
// List<int> key = [67, 3, 241, 75, 143, 78, 115, 99, 21, 242, 180, 43, 26, 7, 194, 20];
// List<int> iv = [87, 117, 137, 182, 2, 199, 132, 230, 120, 12, 109, 177, 34, 197, 186, 206];
List<int> key = _seed(16);
List<int> iv = _seed(16);
KeyParameter keyParam = new KeyParameter(new Uint8List.fromList(key));
ParametersWithIV<KeyParameter> paramsIV = new ParametersWithIV(keyParam, new Uint8List.fromList(iv));
SecureRandom random = new SecureRandom('AES/CTR/AUTO-SEED-PRNG')..seed(paramsIV);
return random;
}
}
crypto_test.dart
import '../lib/crypto/crypto.dart';
import "dart:typed_data";
import "dart:math";
import '../lib/crypto/utils.dart';
import "package:pointycastle/export.dart";
var crypto = new Crypto();
void main() {
// _generateTest();
// _signTest();
Map keyPair = crypto.generateKeyPair();
String rawPrivateB64 = keyPair['base64Priv'];
String rawUncompressedPublicB64 = keyPair['base64Pub'];
BigInt d = importPrivate(rawPrivateB64);
List xy = importPublic(rawUncompressedPublicB64);
BigInt x = xy.elementAt(0);
BigInt y = xy.elementAt(1);
ECDomainParameters domain = ECDomainParameters('secp256r1');
ECPrivateKey private = ECPrivateKey(d, domain);
ECPublicKey public = ECPublicKey(domain.curve.createPoint(x, y), domain);
ECSignature signature = crypto.sign(private, "The quick brown fox jumps over the lazy dog");
bool verified = crypto.verify("The quick brown fox jumps over the lazy dog", signature, public);
print('验签结果');
print(verified); // true
}
_generateTest() {
Map keyPair = crypto.generateKeyPair();
print("结果");
print(keyPair['base64Pub']);
print(keyPair['base64Priv']);
}
_signTest() {
// AsymmetricKeyPair<PublicKey, PrivateKey> kp = crypto.generateKeyPair();
// ECSignature signature = sign(kp.privateKey as ECPrivateKey, "The quick brown fox jumps over the lazy dog");
// bool verified = crypto.verify("The quick brown fox jumps over the lazy dog", signature, kp.publicKey as ECPublicKey);
// print(verified); // true
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.