Flutter RSA 公钥加密,大坑:私钥解密后一直校验失败

498 阅读3分钟

最近在开发这个库的时候需要用到rsa加密,在 pub.dev/ 上找了一圈,最后使用了encrypt

开发中遇到了一个问题——私钥解密后一直校验失败,encrypt rsa加密填充方式只支持:OAEP 和 PKCS1,并不支持noPadding。

因此需要自己扩展一下,以支持noPadding填充。

什么是rsa填充方式

RSA加密常用的填充模式有三种:RSA_PKCS1_PADDING, RSA_PKCS1_OAEP_PADDING, RSA_NO_PADDING。

与对称加密算法DES,AES一样,RSA算法也是一个块加密算法( block cipher algorithm),总是在一个固定长度的块上进行操作。但跟AES等不同的是,block length是跟key length有关的。

每次RSA加密的明文的长度是受RSA填充模式限制的,但是RSA每次加密的块长度就是key length。

RSA_NO_PADDING是在输入数据长度比钥模长(modulus)短时,不足的部分前面补0.

pubspec.yaml 添加依赖

dependencies:# encryptencrypt: ^4.0.0pointycastle: ^1.0.2

encrypt_ext.dart

import 'dart:typed_data';

import 'package:encrypt/encrypt.dart';
import 'package:pointycastle/export.dart' hide Algorithm;

class NoPaddingEncoding extends PKCS1Encoding {
  NoPaddingEncoding(this._engine) : super(_engine);

  final AsymmetricBlockCipher _engine;

  late int _keyLength;
  late bool _forEncryption;

  @override
  void init(bool forEncryption, CipherParameters params) {
    super.init(forEncryption, params);
    this._forEncryption = forEncryption;
    if (params is AsymmetricKeyParameter<RSAAsymmetricKey>) {
      this._keyLength = (params.key.modulus!.bitLength + 7) ~/ 8;
    }
  }

  int get inputBlockSize {
    return _keyLength;
  }

  int get outputBlockSize {
    return _keyLength;
  }

  @override
  int processBlock(
      Uint8List inp, int inpOff, int len, Uint8List out, int outOff) {
    if (_forEncryption) {
      return _encodeBlock(inp, inpOff, len, out, outOff);
    } else {
      return _decodeBlock(inp, inpOff, len, out, outOff);
    }
  }

  int _encodeBlock(
      Uint8List inp, int inpOff, int inpLen, Uint8List out, int outOff) {
    if (inpLen > inputBlockSize) {
      throw new ArgumentError("Input data too large");
    }

    var block = new Uint8List(inputBlockSize);
    var padLength = (block.length - inpLen);

    // 补0
    block.fillRange(0, padLength, 0x00);

    block.setRange(padLength, block.length, inp.sublist(inpOff));

    return _engine.processBlock(block, 0, block.length, out, outOff);
  }

  int _decodeBlock(
      Uint8List inp, int inpOff, int inpLen, Uint8List out, int outOff) {
    var block = new Uint8List(outputBlockSize);
    var len = _engine.processBlock(inp, inpOff, inpLen, block, 0);
    block = block.sublist(0, len);

    if (block.length < outputBlockSize) {
      throw new ArgumentError("Block truncated");
    }

    return block.length;
  }
}

abstract class AbstractRSAExt {
  final RSAPublicKey publicKey;
  final RSAPrivateKey? privateKey;
  final PublicKeyParameter<RSAPublicKey> _publicKeyParams;
  final PrivateKeyParameter<RSAPrivateKey>? _privateKeyParams;
  final AsymmetricBlockCipher _cipher;

  AbstractRSAExt({
    required this.publicKey,
    this.privateKey,
  })  : this._publicKeyParams = PublicKeyParameter(publicKey),
        this._privateKeyParams = privateKey!=null? PrivateKeyParameter(privateKey!): null,
        this._cipher = NoPaddingEncoding(RSAEngine());
}

class RSAExt extends AbstractRSAExt implements Algorithm {
  RSAExt({required RSAPublicKey publicKey, RSAPrivateKey? privateKey})
      : super(publicKey: publicKey, privateKey: privateKey);


  @override
  Encrypted encrypt(Uint8List bytes, {IV? iv, Uint8List? associatedData}) {
    if (publicKey == null) {
      throw StateError('Can\'t encrypt without a public key, null given.');
    }

    _cipher
      ..reset()
      ..init(true, _publicKeyParams);

    return Encrypted(_cipher.process(bytes));
  }

  @override
  Uint8List decrypt(Encrypted encrypted, {IV? iv, Uint8List? associatedData}) {
    if (privateKey == null) {
      throw StateError('Can\'t decrypt without a private key, null given.');
    }

    if (_privateKeyParams == null) {
      throw StateError('Can\'t decrypt without a privateKeyParams, null given.');
    }

    _cipher
      ..reset()
      ..init(false, _privateKeyParams!);

    return _cipher.process(encrypted.bytes);
  }
}

引用:(RSA非对称加解密算法填充方式(Padding)) blog.csdn.net/makenothing…