【flutter大杂烩】encrypt库rsa加密支持noPadding填充方式

5,017 阅读2分钟

如果您觉得文章对您有帮助,欢迎 👍, 我的 Github.


最近在开发这个库的时候需要用到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_PKCS1_PADDING 必须 比 RSA 钥模长(modulus) 短至少11个字节, 也就是RSA_size(rsa) – 11 和modulus一样长
RSA_PKCS1_OAEP_PADDING RSA_size(rsa) – 41 和modulus一样长
RSA_NO_PADDING 可以和RSA钥模长一样长,如果输入的明文过长,必须切割,然后填充 和modulus一样长

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


实现如下:

pubspec.yaml 添加依赖

dependencies:
  # encrypt
  encrypt: ^4.0.0
  pointycastle: ^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;

  int _keyLength;
  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({
    this.publicKey,
    this.privateKey,
  })  : this._publicKeyParams = PublicKeyParameter(publicKey),
        this._privateKeyParams = PrivateKeyParameter(privateKey),
        this._cipher = NoPaddingEncoding(RSAEngine());
}

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

  @override
  Encrypted encrypt(Uint8List bytes, {IV iv}) {
    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}) {
    if (privateKey == null) {
      throw StateError('Can\'t decrypt without a private key, null given.');
    }

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

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

使用

final parser = RSAKeyParser();
final encrypter =
  Encrypter(RSAExt(publicKey: parser.parse(_publicKey)));
encrypter.doSome...

引用:RSA非对称加解密算法填充方式(Padding)