如何在扑波中复制node.js的RSA身份验证?

发布于 2025-01-21 09:06:06 字数 2255 浏览 3 评论 0原文

我有以下代码在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中正常工作,但我似乎找不到一个包裹以颤动复制的软件包。建议我 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

如何直接在颤音中使用 .js 功能?

I have the code below working in node.js and I am trying to convert it to make the API call directly from my flutter app, but I am having problems with the RSA encryption.

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;
}

The authentication works fine in Node.js but I can't seem to find a package to replicate it in flutter. I was recommended 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";
  }
 }
  
}

But the server keeps returning auth_error.

How do I use the .js function directly inside flutter?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

§普罗旺斯的薰衣草 2025-01-28 09:06:06

我专注于签名部分。 NodeJS代码使用RSA创建签名。对于填充和消化,应用节点RSA默认值:PKCS#1V1.5填充和SHA256,s。 在这里。私钥被导入DER编码的PKCS#1键(基本64编码)。签名为基本64编码。

请注意,在问题中发布的nodejs代码中,缺少有关$符号的$符号 hashstring 缺失,这可能是复制/粘贴错误。必须修复这一点,否则签名将有所不同!

在飞镖一侧,需要以下修复程序:

  • PKCS#1关键将直接传递到 rsa.signpkcs1v15(),即rsa.convertprivatekeytopkcs11 ()呼叫将被删除。 rsa.signpkcs1v15()期望PEM编码的密钥,IE标头和页脚,并且在Base64编码的主体中,每64个字符后都有一个线路休息。
  • 时间戳将转换为nodejs代码中使用的格式:dateTime.now()。milliseconceSeph.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'

test:
由于使用PKCS#1 V1.5签名是确定性的,因此相同输入数据提供了相同的签名。这使得可以轻松检查两个代码的功能等效性。如果在两个代码中使用相同的时间戳(例如,评论<代码> 1649917884089 ),两个代码返回相同的签名(nrux6ey+66ca2zbb/zk6cabb/zk66 exter =),哪个证明了这两个代码的等效性。

这是用于测试的固定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' 

I focus on the signing part. The NodeJS code creates a signature using RSA. For padding and digest the node-rsa default values are applied: PKCS#1v1.5 padding and SHA256, s. here. The private key is imported as DER encoded PKCS#1 key (Base64 encoded). The signature is Base64 encoded.

Note that in the NodeJS code posted in the question, the $ signs for the 2nd and 3rd variables regarding hashString are missing, which is probably a copy/paste error. This must be fixed, otherwise the signatures will differ!

On the Dart side, the following fixes are needed:

  • The PKCS#1 key is to be passed directly to RSA.signPKCS1v15(), i.e. the RSA.convertPrivateKeyToPKCS1() call is to be removed. RSA.signPKCS1v15() expects a PEM encoded key, i.e. header and footer are to be added and in the Base64 encoded body there is a line break after every 64 characters.
  • The timestamp is to be converted to the format used in the NodeJS code: DateTime.now().millisecondsSinceEpoch.toString().
  • RSA.signPKCS1v15() returns the signature already base64 encoded, i.e. the RSA.base64() call must be removed.

A possible dart counterpart with the fast_rsa library that fixes the above issues is:

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'

Test:
Since signing with PKCS#1 v1.5 is deterministic, the same input data provides the same signature. This makes it easy to check the functional equivalence of both codes. If the same timestamp is used in both codes (e.g. the commented out 1649917884089), both codes return the same signature (nRuX6eY+66Ca2ZbB/ZK6ealRdS8gYJ4UKNwUOdJySqujGnwpflE8aZ45L4PfQK3qAMJh02o0SVG8uy2Mz+BFpg==), which proves the equivalence of both codes.

This is the fixed NodeJS code used for the test. It is essentially the same as the NodeJS code posted in the question:

// 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' 
ぃ双果 2025-01-28 09:06:06

您可以使用 https://pub.dev/packages/packages/encrypt package 可以执行rsa rsa < /code>在飞镖和扑打中加密和解密。

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=
}

you can use https://pub.dev/packages/encrypt package to perform RSA encryption and decryption in dart and flutter.

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=
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文