You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Flutter中实现与Java等价的RSA公钥加密明文功能

Hey there! I'll help you port your Java RSA+AES encryption logic to Dart/Flutter. Let's go through each part of your code and build the equivalent Dart implementation step by step.

First, you'll need to add these dependencies to your pubspec.yaml (they handle encryption, crypto utilities, and data conversion):

dependencies:
  encrypt: ^5.0.1
  pointycastle: ^3.7.3
  convert: ^3.1.1

Step 1: Parse the .cer certificate to get the RSA public key

Your Java code uses X509Certificate.getPublicKey()—here's how to extract the RSA public key from a .cer file in Dart:

import 'dart:convert';
import 'dart:io';
import 'package:pointycastle/asn1.dart';
import 'package:pointycastle/api.dart';
import 'package:pointycastle/asymmetric/api.dart';
import 'package:pointycastle/asymmetric/rsa.dart';

RSAPublicKey parseCerCertificate(String cerFilePath) {
  final file = File(cerFilePath);
  final bytes = file.readAsBytesSync();
  
  // Decode PEM format (strip headers/footers)
  final pem = utf8.decode(bytes).replaceAll('-----BEGIN CERTIFICATE-----', '')
      .replaceAll('-----END CERTIFICATE-----', '')
      .replaceAll('\n', '')
      .replaceAll('\r', '');
  final derBytes = base64.decode(pem);
  
  // Parse ASN.1 structure to get public key
  final asn1Parser = ASN1Parser(derBytes);
  final certSeq = asn1Parser.nextObject() as ASN1Sequence;
  final tbsCertSeq = certSeq.elements[0] as ASN1Sequence;
  final subjectPublicKeyInfo = tbsCertSeq.elements[6] as ASN1Sequence;
  final publicKeyBitString = subjectPublicKeyInfo.elements[1] as ASN1BitString;
  
  final publicKeyParser = ASN1Parser(publicKeyBitString.valueBytes);
  final publicKeySeq = publicKeyParser.nextObject() as ASN1Sequence;
  final modulus = publicKeySeq.elements[0] as ASN1Integer;
  final exponent = publicKeySeq.elements[1] as ASN1Integer;
  
  return RSAPublicKey(modulus.valueAsBigInteger, exponent.valueAsBigInteger);
}

Step 2: Full Encryption Function (Matching Your Java Logic)

This function replicates every step of your Java code—generating AES key, RSA encrypting it, AES CBC encrypting the plaintext with space padding, and assembling the final payload:

import 'dart:typed_data';
import 'package:encrypt/encrypt.dart';
import 'package:pointycastle/random/fortuna_random.dart';
import 'package:convert/convert.dart';

String encrypt(String plaintext, RSAPublicKey rsaPublicKey) {
  // 1. Generate AES-256 key (32 bytes)
  final secureRandom = FortunaRandom();
  final seed = List<int>.generate(32, (_) => secureRandom.nextByte());
  secureRandom.seed(KeyParameter(Uint8List.fromList(seed)));
  final aesKeyBytes = secureRandom.nextBytes(32);
  final aesKey = Key(aesKeyBytes);

  // 2. RSA encrypt the AES key (Java's "RSA" = RSA/ECB/PKCS1Padding)
  final rsaEncrypter = Encrypter(RSA(publicKey: rsaPublicKey, encoding: RSAEncoding.PKCS1));
  final encryptedAesKey = rsaEncrypter.encryptBytes(aesKeyBytes);

  // 3. Generate 16-byte IV for AES CBC
  final ivBytes = secureRandom.nextBytes(16);
  final iv = IV(ivBytes);

  // 4. Pad plaintext with spaces to 16-byte blocks (NoPadding requires exact block size)
  final plaintextBytes = utf8.encode(plaintext);
  final remainder = 16 - (plaintextBytes.length % 16);
  final paddedBytes = Uint8List(plaintextBytes.length + remainder)
    ..setAll(0, plaintextBytes)
    ..fillRange(plaintextBytes.length, plaintextBytes.length + remainder, 0x20); // ASCII space

  // 5. AES/CBC/NoPadding encryption
  final aesEncrypter = Encrypter(AES(aesKey, mode: AESMode.cbc, padding: null));
  final encryptedPlaintext = aesEncrypter.encryptBytes(paddedBytes, iv: iv);

  // 6. Assemble the final payload in the required format
  final payloadBuilder = BytesBuilder();
  // First 4 bytes: [0, 1, 0, 0]
  payloadBuilder.add(Uint8List.fromList([0, 1, 0, 0]));
  // Next 4 bytes: IV length (16) as [16, 0, 0, 0]
  payloadBuilder.add(Uint8List.fromList([16, 0, 0, 0]));
  // Encrypted AES key (256 bytes for 2048-bit RSA)
  payloadBuilder.add(encryptedAesKey.bytes);
  // IV bytes
  payloadBuilder.add(ivBytes);
  // Encrypted plaintext
  payloadBuilder.add(encryptedPlaintext.bytes);

  // 7. Encode to Base64 (matches Java's Base64.getMimeEncoder())
  return base64.encode(payloadBuilder.toBytes());
}

Key Notes to Match Your Java Code Exactly:

  • AES Key Generation: Uses a cryptographically secure random (FortunaRandom) to generate 32 bytes for AES-256, same as your Java KeyGenerator.getInstance("AES").init(256).
  • RSA Encryption: Uses PKCS1 padding, which is what Java's Cipher.getInstance("RSA") defaults to.
  • Padding for AES: Adds space characters (ASCII 0x20) to make the plaintext length a multiple of 16, just like your Java loop.
  • Payload Structure: Follows the exact byte order from your Java ByteArrayOutputStream writes.

How to Use It:

void main() {
  // Parse your .cer certificate
  final rsaPublicKey = parseCerCertificate('path/to/your/certificate.cer');
  // Encrypt your plaintext
  final encryptedResult = encrypt('your plaintext here', rsaPublicKey);
  print(encryptedResult);
}

内容的提问来源于stack exchange,提问作者Cipher

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 07:09:07