如何在Flutter中从Node.js复制RSA身份验证?

flseospp  于 11个月前  发布在  Node.js
关注(0)|答案(2)|浏览(79)

我有下面的代码在node.js工作,我试图转换它,使API调用直接从我的flutter应用程序,但我有问题的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 函数?

fcg9iug3

fcg9iug31#

我关注签名部分。NodeJS代码使用RSA创建签名。对于填充和摘要,应用node-rsa默认值:PKCS#1v1.5填充和SHA 256,s。这里。私钥作为DER编码的PKCS#1密钥(Base64编码)导入。签名是Base64编码的。
请注意,在问题中发布的NodeJS代码中,关于hashString的第二个和第三个变量的$符号丢失,这可能是一个复制/粘贴错误。这必须得到修复,否则签名会有所不同!
在Dart方面,需要进行以下修复:

  • PKCS#1键将 * 直接 * 传递给RSA.signPKCS1v15(),即RSA.convertPrivateKeyToPKCS1()调用将被删除。RSA.signPKCS1v15()期望PEM编码的键,即添加页眉和页脚,并且在Base64编码的主体中,每64个字符后有一个换行符。
  • 时间戳将被转换为NodeJS代码中使用的格式:DateTime.now().millisecondsSinceEpoch.toString()
  • RSA.signPKCS1v15()返回已经base64编码的签名,即必须删除RSA.base64()调用。

一个可能的dart对应的fast_rsa库可以解决上述问题:

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的签名是确定性的,因此 * 相同 * 的输入数据提供了 * 相同 * 的签名。这使得检查两个代码的功能等价性变得容易。如果两个代码中使用了相同的时间戳(例如,注解掉的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'

7qhs6swi

7qhs6swi2#

你可以使用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=
}

字符串

相关问题