我有下面的代码在node.js中工作,我正在尝试将其转换为直接从我的flutter应用程序进行API调用,但我在RSA加密方面遇到了问题。
import fetch from "node-fetch";
import nodeRSA from "node-rsa";
const KEYVER = '23'
const ID = '123456789123456789'
const PRIVATE_KEY = "vvkmlkmmvcmemmcmdmdmm.......cddncndndncn ="
generateRequestHeader(){
const hashString = `${ID}\n{Date.now().toString()}\n{KEYVER}\n`;
const signer = new nodeRSA(PRIVATE_KEY, "pkcs1");
const signature = signer.sign(hasString);
const sign_enc = signature.toString("base64");
return {
"AUTH_SIGNATURE": sign_enc,
"TIMESTAMP": Date.now().toString(),
"ID": ID,
"KEY_VERSION":KEYVER
};
}
async function callAPI(){
const options = {
method: 'GET',
headers: generateRequestHeader()
};
const response = await fetch(url, options);
return response;
}
身份验证在 Node.js 中工作正常,但我似乎找不到在 flutter 中复制它的包。有人推荐我fast_rsapackage:
#fast_rsa: ^3.4.6
import 'package:fast_rsa/fast_rsa.dart';
class Signature{
String Id = 'c93e7094-327b-4ff3-bf2e-c52f29a8277f';
String privateKey = "ABCDEG....Z=";
String keyVer = '23.0';
generateRequestHeaders() async {
String timeStamp = DateTime.now().toString();
String hashString = "${Id}\n${timeStamp}\n${keyVer}\n";
var signer = await RSA.convertPrivateKeyToPKCS1(privateKey);
var signature = await RSA.signPKCS1v15(signer, Hash.SHA256, privateKey);
var signature_enc = await RSA.base64(signature);
return {
"AUTH_SIGNATURE": signature_enc,
"TIMESTAMP": timeStamp,
"ID": Id,
"KEY_VERSION": keyVer,
};
}
Future<dynamic> rsaRequest() async {
var options = {'method': 'GET', 'headers': generateRequestHeaders()};
String url = 'https://api.........';
http.Response response = await http.get(url, headers: options);
try {
if (response.statusCode == 200) {
print(response.body);
var document = parse(response.body);
return document;
} else {
return "failed";
}
} catch (exp) {
print(exp);
return "failed";
}
}
}
但是服务器不断返回auth_error。
如何直接在 flutter 中使用 .js 函数?
我重点关注签名部分。 NodeJS 代码使用 RSA 创建签名。对于填充和摘要,应用节点 rsa 默认值:PKCS#1v1.5 填充和 SHA256, s。 这里。私钥作为 DER 编码的 PKCS#1 密钥(Base64 编码)导入。签名是 Base64 编码的。
请注意,在问题中发布的 NodeJS 代码中,关于
$
的第二个和第三个变量的 hashString
符号缺失,这可能是复制/粘贴错误。这个必须修复,否则签名会不同!
在 Dart 方面,需要进行以下修复:
RSA.signPKCS1v15()
,即RSA.convertPrivateKeyToPKCS1()
调用将被删除。 RSA.signPKCS1v15()
需要 PEM 编码密钥,即添加页眉和页脚,并且在 Base64 编码正文中每 64 个字符后有一个换行符。DateTime.now().millisecondsSinceEpoch.toString()
。RSA.signPKCS1v15()
返回已进行 Base64 编码的签名,即必须删除 RSA.base64()
调用。解决上述问题的 fast_rsa 库可能对应的 dart 是:
Future<Map<String,String>> generateRequestHeaders() async {
String privateKey = '''-----BEGIN RSA PRIVATE KEY-----
MIIBOwIBAAJBANoHbFSEZoOSB9Kxt7t8PoBwmauaODjECHqJgtTU3h4MW5K3857+
04Flc6x6a9xxyvCKS5RtOP2gaOlOVtrph0ECAwEAAQJBALu8LpRr2RWrdV7/tfQT
HIJd8oQnbAe9DIvuwh/fF08IwApOE/iGL+Ded49eoHHu1OXycZhpHavN/sQMnssP
FNECIQDyDIW7V5UUu16ZAeupeQ7zdV6ykVngd0bb3FEn99EchQIhAOaYe3ll211q
SIXVjKHudMn3xe6Vvguc9O7cwCB+gyqNAiEAsr3kk6/de23SMZNlf8TR8Z8eyybj
BAuQ3BMaKzWpyjECIFMR0UFNYTYIyLF12aCoH2h2mtY1GW5jj5TQ72GFUcktAiAf
WWXnts7m8kZWuKjfD0MQiW+w4iAph+51j+wiL3EMAQ==
-----END RSA PRIVATE KEY-----''';
String keyVer = "23";
String Id = "123456789123456789";
String timeStamp = DateTime.now().millisecondsSinceEpoch.toString(); // "1649917884089" for testing
String hashString = "${Id}\n${timeStamp}\n${keyVer}\n";
String signature = await RSA.signPKCS1v15(hashString, Hash.SHA256, privateKey);
return {
"AUTH_SIGNATURE": signature,
"TIMESTAMP": timeStamp,
"ID": Id,
"KEY_VERSION": keyVer,
};
}
...
var result = await generateRequestHeaders();
print(result["AUTH_SIGNATURE"]); // nRuX6eY+66Ca2ZbB/ZK6ealRdS8gYJ4UKNwUOdJySqujGnwpflE8aZ45L4PfQK3qAMJh02o0SVG8uy2Mz+BFpg== for datetime = '1649917884089'
测试:
由于使用 PKCS#1 v1.5 进行签名是确定性的,因此 same 输入数据提供 same 签名。这使得检查两个代码的功能等效性变得容易。如果两个代码中使用相同的时间戳(例如注释掉的
1649917884089
),则两个代码返回相同的签名(nRuX6eY+66Ca2ZbB/ZK6ealRdS8gYJ4UKNwUOdJySqujGnwpflE8aZ45L4PfQK3qAMJh02o0SVG8uy2Mz+BFpg==
),这证明了两个代码的等效性。
这是用于测试的固定 NodeJS 代码。它与问题中发布的 NodeJS 代码本质上相同:
// DER encoded PKCS#1 key, Base64 encoded
// Note: For testing purposes, a 512 bits key is used. In practice, key sizes >= 2048 bits must be applied for security reasons!
const PRIVATE_KEY = "MIIBOwIBAAJBANoHbFSEZoOSB9Kxt7t8PoBwmauaODjECHqJgtTU3h4MW5K3857+04Flc6x6a9xxyvCKS5RtOP2gaOlOVtrph0ECAwEAAQJBALu8LpRr2RWrdV7/tfQTHIJd8oQnbAe9DIvuwh/fF08IwApOE/iGL+Ded49eoHHu1OXycZhpHavN/sQMnssPFNECIQDyDIW7V5UUu16ZAeupeQ7zdV6ykVngd0bb3FEn99EchQIhAOaYe3ll211qSIXVjKHudMn3xe6Vvguc9O7cwCB+gyqNAiEAsr3kk6/de23SMZNlf8TR8Z8eyybjBAuQ3BMaKzWpyjECIFMR0UFNYTYIyLF12aCoH2h2mtY1GW5jj5TQ72GFUcktAiAfWWXnts7m8kZWuKjfD0MQiW+w4iAph+51j+wiL3EMAQ=="
const KEYVER = '23';
const ID = '123456789123456789';
const timeStamp = Date.now().toString(); // '1649917884089' for testing
function generateRequestHeader(){
const hashString = `${ID}\n${timeStamp}\n${KEYVER}\n`; // Fix: Add the $ sign
const signer = new nodeRSA(PRIVATE_KEY, "pkcs1");
const signature = signer.sign(hashString); // default signing scheme: PKCS#1 v1.5 with SHA256
const sign_enc = signature.toString("base64");
return {
"AUTH_SIGNATURE": sign_enc,
"TIMESTAMP": Date.now().toString(),
"ID": ID,
"KEY_VERSION":KEYVER
};
}
...
var result = generateRequestHeader();
console.log(result.AUTH_SIGNATURE); // nRuX6eY+66Ca2ZbB/ZK6ealRdS8gYJ4UKNwUOdJySqujGnwpflE8aZ45L4PfQK3qAMJh02o0SVG8uy2Mz+BFpg== for datetime = '1649917884089'
您可以使用 https://pub.dev/packages/encrypt 包在 dart 和 flutter 中执行
RSA
加密和解密。
import 'dart:io';
import 'package:encrypt/encrypt.dart';
import 'package:pointycastle/asymmetric/api.dart';
void main() {
final publicKey = await parseKeyFromFile<RSAPublicKey>('test/public.pem');
final privKey = await parseKeyFromFile<RSAPrivateKey>('test/private.pem');
final plainText = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit';
final encrypter = Encrypter(RSA(publicKey: publicKey, privateKey: privKey));
final encrypted = encrypter.encrypt(plainText);
final decrypted = encrypter.decrypt(encrypted);
print(decrypted); // Lorem ipsum dolor sit amet, consectetur adipiscing elit
print(encrypted.base64); // kO9EbgbrSwiq0EYz0aBdljHSC/rci2854Qa+nugbhKjidlezNplsEqOxR+pr1RtICZGAtv0YGevJBaRaHS17eHuj7GXo1CM3PR6pjGxrorcwR5Q7/bVEePESsimMbhHWF+AkDIX4v0CwKx9lgaTBgC8/yJKiLmQkyDCj64J3JSE=
}