是什么
encrypt 是一个 Dart/Flutter 的加密库,主要提供了对常见对称加密(AES)、非对称加密(RSA)、以及编码(Base64)等的支持。
- 对称加密:加密和解密用同一把密钥(例如 AES)。
- 非对称加密:加密和解密用不同的密钥(例如 RSA)。
- 编码:Base64、Hex 这些只是编码,不是真正的加密。
encrypt 库主要基于 pointycastle 这个更底层的 Dart 加密库,提供了更易用的 API。
基本使用
安装
flutter pub add encrypt
AES 加解密(对称)
import 'package:encrypt/encrypt.dart' as encrypt;
void main() {
// 密钥和初始向量(IV)
// Key 必须 16/24/32 长度
final key = encrypt.Key.fromUtf8('1234567890123456');
final iv = encrypt.IV.fromLength(16);
print(key.bytes);// [49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, 54]
print(iv.bytes); // [144, 201, 60, 163, 168, 140, 144, 216, 131, 86, 191, 37, 96, 76, 58, 35]
// AES 加密器
final encrypter = encrypt.Encrypter(encrypt.AES(key));
// 加密
final encrypted = encrypter.encrypt('hello flutter', iv: iv);
// 输出加密后的 Base64 字符串
print(encrypted.base64); //ksLEH/Cge3qzTGkdBehNxw==
// 解密
final decrypted = encrypter.decrypt(encrypted, iv: iv);
print(decrypted); // hello flutter
}
fromUtf8(key)
Key.fromUtf8() 的参数长度必须是 16 / 24 / 32 个字节,这是 AES 算法本身的要求,不是 Flutter 特有的。
final key = Key.fromUtf8('1234567890123456'); // ✅ 16 字节,AES-128
final key = Key.fromUtf8('123456789012345678901234'); // ✅ 24 字节,AES-192
final key = Key.fromUtf8('12345678901234567890123456789012'); // ✅ 32 字节,AES-256
但如果是其他长度(比如 10、20、30),会直接报错:
Invalid argument(s): Key length must be 16/24/32 bytes
fromUtf8 是把字符串按 UTF-8 编码转成字节,所以要确保你的字符串长度刚好是 16/24/32 个 ASCII 字符(因为一个 ASCII 字符占一个字节)。
- 如果包含中文、emoji 之类的多字节字符,长度可能对不上。
- 推荐只用 ASCII(字母、数字、符号)来做 key。
如果你需要随机 key,可以用:
final key = Key.fromSecureRandom(32); // 生成 32 字节随机 key
AES(...)
AES(
Key key, // 必填,加密用的 key,长度必须 16/24/32 字节
{
AESMode mode = AESMode.sic, // 可选,加密模式,默认是 sic
String? padding = 'PKCS7' // 可选,填充方式,默认是 PKCS7
}
)
key(必须)类型:
Key作用:AES 加密/解密的密钥
长度要求:16 / 24 / 32 字节(AES-128 / AES-192 / AES-256)
mode(可选)类型:
AESMode默认:
AESMode.sic可选值:
AESMode.cbc→ 常见,需 IV,安全性高AESMode.ecb→ 不推荐,模式弱,无 IVAESMode.sic→ 类似 CTR,常用AESMode.cfb/AESMode.ofb→ 流模式AESMode.gcm→ 带认证的模式,常用在网络通信
padding(可选)类型:
String?默认:
PKCS7作用:当数据长度不是 16 字节的整数倍时,需要填充
常用值:
'PKCS7'→ 最常见null→ 不填充(通常在流模式/分组对齐时用)
IV 的作用
IV(初始化向量, Initialization Vector)是 分组加密算法(比如 AES)在某些模式下必需的一个额外参数- 保证加密结果不一样:如果你用同一个 key 去加密同一份明文,没有 IV 的话,密文每次都是一样的。有了随机的 IV,即使明文和 key 都一样,密文也会不一样,增加安全性。
- 防止模式漏洞:在 CBC、CFB、OFB、CTR(SIC) 等模式下,第一块数据的加密需要一个“起始块”。如果没有 IV,就会默认用固定值(比如全 0),这样会泄露数据规律。
- 增加随机性,但不增加安全强度:IV 不需要保密(可以明文传输),但必须随机或唯一。真正控制安全的是 key,IV 只是用来避免“重复密文”的。
fromLength的作用
final iv = IV.fromLength(16);
// [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
fromLength(n) 会创建一个 长度为 n 的 IV,内容全是 0。一般在以下情况中使用:
调试/测试:用固定的全 0 IV,可以让你每次加密的结果一致,方便对比和验证。
不关心安全性时(例如课堂示例、简单本地存储)只是演示 AES 的功能,可以用全 0 IV。
但不推荐在生产环境中使用 。因为 IV 全 0 就失去了“随机性”的意义,容易被攻击者推测出数据模式。
Key和IV底层都是一个List
/// Represents an Encryption Key.
class Key extends Encrypted {
Key(Uint8List bytes) : super(bytes);
Key.fromBase64(String encoded) : super.fromBase64(encoded);
Key.fromUtf8(String input) : super.fromUtf8(input);
}
/// Represents an Initialization Vector.
class IV extends Encrypted {
IV(Uint8List bytes) : super(bytes);
IV.fromLength(int length) : super.fromLength(length);
}
class Encrypted {
Encrypted(this._bytes);
final Uint8List _bytes;
/// Gets the Encrypted bytes.
Uint8List get bytes => _bytes;
/// Creates an Encrypted object from a UTF-8 string.
Encrypted.fromUtf8(String input)
: _bytes = Uint8List.fromList(convert.utf8.encode(input));
Encrypted.fromLength(int length) : _bytes = SecureRandom(length).bytes;
}
RSA 加解密(非对称)
import 'package:encrypt/encrypt.dart' as encrypt;
import 'package:pointycastle/asymmetric/api.dart';
void main() {
// 模拟一对 RSA 公钥和私钥(通常是从文件/服务端获取)
final publicKey = encrypt.RSAKeyParser().parse('-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----') as RSAPublicKey;
final privateKey = encrypt.RSAKeyParser().parse('-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----') as RSAPrivateKey;
// 同时传公钥 + 私钥(支持加解密)
final encrypter = encrypt.Encrypter(encrypt.RSA(publicKey: publicKey, privateKey: privateKey));
// 加密
final encrypted = encrypter.encrypt('hello flutter');
print(encrypted.base64);
// 解密
final decrypted = encrypter.decrypt(encrypted);
print(decrypted);
}
RSA
class RSA extends AbstractRSA implements Algorithm {
RSA(
{RSAPublicKey? publicKey,
RSAPrivateKey? privateKey,
RSAEncoding encoding = RSAEncoding.PKCS1,
RSADigest digest = RSADigest.SHA1})
: super(
publicKey: publicKey,
privateKey: privateKey,
encoding: encoding,
digest: digest,
);
}
参数解析
publicKey:公钥(加密/验证签名)
- 类型:
RSAPublicKey? - 作用:加密数据 / 验证签名
- Flutter 端一般只需要传公钥(用来加密),私钥放在服务端。
privateKey:私钥(解密/生成签名)
- 类型:
RSAPrivateKey? - 作用:解密数据 / 生成签名
- 如果你只需要加密,可以不传私钥。
- 如果你要做解密,就必须传入私钥。
encoding:填充方式(默认 OAEP,更安全)
类型:
RSAEncoding(枚举)默认值:
RSAEncoding.OAEP(更安全的填充方式)可选值:
RSAEncoding.PKCS1→ 传统 PKCS#1 填充RSAEncoding.OAEP→ 更现代,推荐
RSA 本身不能直接加密长数据,所以需要填充(padding)方式。
加密(只用公钥)
final publicKey = RSAKeyParser().parse(publicPem) as RSAPublicKey;
final encrypter = Encrypter(RSA(publicKey: publicKey));
final encrypted = encrypter.encrypt('hello flutter');
print(encrypted.base64);
解密(只用私钥)
final privateKey = RSAKeyParser().parse(privatePem) as RSAPrivateKey;
final encrypter = Encrypter(RSA(privateKey: privateKey));
final decrypted = encrypter.decrypt(encrypted);
print(decrypted); // hello flutter
同时传公钥 + 私钥(支持加解密)
final encrypter = Encrypter(RSA(
publicKey: publicKey,
privateKey: privateKey,
encoding: RSAEncoding.OAEP,
));
使用OpenSSL获取公钥和私钥
生成 RSA 私钥
openssl genrsa -out private.pem 2048private.pem就是私钥文件2048是密钥长度(常用 2048 或 4096)
从私钥导出公钥
openssl rsa -in private.pem -pubout -out public.pempublic.pem就是公钥文件- 默认就是 PEM 格式(
-----BEGIN PUBLIC KEY-----)
在服务端(Linux/Mac/Windows)可以用 OpenSSL 。如果没有这个命令,就需要去下载。
案例: RSA + AES 混合加密
完整示例代码
import 'package:encrypt/encrypt.dart';
import 'dart:typed_data';
import 'package:pointycastle/asymmetric/api.dart';
void main() {
// ============= 1. 生成 RSA 公钥和私钥(实际一般是后端生成) =============
final rsaPublicKeyPem = '''
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlqOzzCwejyWdahzqw/4N
flxlQWn/LEERkEQuHFjbT4T5ixEFsAoZIsVSoZ9paWyDC/Cvt5m7ex9jibxXE3tO
Oq12BKplf9Oi6eDAlnVeVQOm5u/uQ9y1uH05pC5lZfLKSmjm9YLKbBBTBuoHk1Xi
/wnJ5X7jlreQ97b6TOwi6NAH4BhPG830LV1AuCnWJPq4wHsXLOxx3bwBObSFEOPk
FCo592jLdvsq2YTH31AQodq03vvFUUgCABv4YXg87FkCahU7cIl4MGu844CXy2ze
5ErqxkTxD4KkBXVWkBb5cK+ydR/F/7p0tFmUWmcuCDKaT24p3/6rzlEAnG8zGzvS
PQIDAQAB
-----END PUBLIC KEY-----
''';
final rsaPrivateKeyPem = '''
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCWo7PMLB6PJZ1q
HOrD/g1+XGVBaf8sQRGQRC4cWNtPhPmLEQWwChkixVKhn2lpbIML8K+3mbt7H2OJ
vFcTe046rXYEqmV/06Lp4MCWdV5VA6bm7+5D3LW4fTmkLmVl8spKaOb1gspsEFMG
6geTVeL/CcnlfuOWt5D3tvpM7CLo0AfgGE8bzfQtXUC4KdYk+rjAexcs7HHdvAE5
tIUQ4+QUKjn3aMt2+yrZhMffUBCh2rTe+8VRSAIAG/hheDzsWQJqFTtwiXgwa7zj
gJfLbN7kSurGRPEPgqQFdVaQFvlwr7J1H8X/unS0WZRaZy4IMppPbinf/qvOUQCc
bzMbO9I9AgMBAAECggEAEeeML8jQqwf4AUxFlR0Foo691DwSgcKMIV8RMfpXushd
1qsjx2wz15+ndiFZ6Jdmrg0h/YsOrxAohw71NJIT1sKWQ/Cy1rIja3P0wA1cNHOW
K126LqO1j6OGrd5729WzOIvmY6jzgUuharF3TApJzDFwJ1BhIaFY4DCF6I2qiow8
qAIOa+pAvMMSF9fE7hLsfUQm7nH/kYHTL/pCDjl6HyR0G3yY6mx+u9vM2Rgur+yP
PbCalv05qZZn/0jgvdURTdPI3SZ2E5OZ5MRo3m6KsV3d7D/EvxVSry2FvDti8Gw5
zj5djF1+UnLdp7PnQug51LOSDsISSEOAmBfZyyk5EQKBgQDKBlIbYZydNZGn+qjT
CtFUm99l1w9dZ4Ki797pBlTbxxeT7cM+aZPRII/oHs4nd/TN39Gtk853jOuvEl4Z
jRZbFa5/7oUpfpra+4RoM+kNc1weTrEY2X+M0xz4ns+Wsdfa4gTJTWKfs2GU30nb
Q2zn99xDApOrkaB+aIn44ZS4mQKBgQC+4taBWtR1e32dA/hzObd2o72m5cwON6L9
Psssg+knEzIPH+wZL92G5l/f+BSs731BankS8FbMPCNNT+2F/glIoiPUeIZQG0Xq
MUN3D7yz8rKD5d2HCjeujiVlhqDz9vRLozQaRHEl72V3kQRY53YfjdU3fN+exAAx
obLIpOw5RQKBgQCisZhp6KAhksRsTNasrqeJxcWQ1DLRe8yevyH681hfbeQ1A2Ma
3hdmcRyzSCupaVdCfJ71dHTvnhMneFlDpnV8gOd4q3OdyRGYjLlC2Zszc9WZqrHA
W1f25wULy4lNyIcXBLHqE2H7TpClPXTTGWeZH+jIJkkaADsJ0dJbFf+Y8QKBgFWS
FLzqPeHomRgBRqhI0GDD6GngDAi3kj5anvP2sldM+JzlH47u8SzfK02YxZP8zDa+
B0ahLRfjQaQP15fAmOLRpKRD31obz8y7htXh3SMCAlq0eglmYC3FFSidIJwdMciS
gfHgiqSJ2/+sp7k/Ean1iAaW2RwwGzIknaPqoubNAoGAUlQB2u5mva1bCAiIr5ek
2jVCjnVj7j0/ry10ztZS6N+k1arvTCELr4CrxYzeF0gVCB25q6tFCNte1snanOzS
exWVNY5R8mVcLGqPyKYFTiqxMApLZgjQFFjb1G80ScUBaTZsHG8EJR0+5nBB8cfX
y1RIA+Fk1nyBUMSmBoPHLHs=
-----END PRIVATE KEY-----
''';
final publicKey = RSAKeyParser().parse(rsaPublicKeyPem) as RSAPublicKey;
final privateKey = RSAKeyParser().parse(rsaPrivateKeyPem) as RSAPrivateKey;
// ============= 2. 前端:生成 AES key,用 RSA 公钥加密后传给后端 =============
final aesKey = Key.fromUtf8(
'my32lengthsupersecretnooneknows!',
); // 32 字节 AES 密钥
final iv = IV.fromLength(16); // 初始化向量
final rsaEncrypter = Encrypter(RSA(publicKey: publicKey));
final encryptedAesKey = rsaEncrypter.encryptBytes(aesKey.bytes);
print('🔐 发送给后端的 被RSA 加密的 Key(Base64):${encryptedAesKey.base64}');
// ============= 3. 前端:用 AES 加密实际数据 =============
final aesEncrypter = Encrypter(AES(aesKey));
final encryptedData = aesEncrypter.encrypt(
'Hello Hybrid Encryption!',
iv: iv,
);
print('📦 发送给后端的密文(Base64):${encryptedData.base64}');
// ============= 4. 后端:用 RSA 私钥解密 AES Key =============
final rsaDecrypter = Encrypter(RSA(privateKey: privateKey));
final decryptedAesKeyBytes = rsaDecrypter.decryptBytes(encryptedAesKey);
// Uint8List.fromList:把 List<int> -> Uint8List
final decryptedAesKey = Key(Uint8List.fromList(decryptedAesKeyBytes));
// ============= 5. 后端:用解密出来的 AES Key 解密数据 =============
final aesDecrypter = Encrypter(AES(decryptedAesKey));
final decryptedText = aesDecrypter.decrypt(encryptedData, iv: iv);
print('✅ 解密得到明文:$decryptedText');
}
执行效果(输出示例)
🔐 发送给后端的 AES Key(Base64):RrUeJ9d2...
📦 发送给后端的密文(Base64):3fQ2J6Gm...
✅ 解密得到明文:Hello Hybrid Encryption!
流程图
前端 (Flutter) 后端 (Server)
────────────────────────────────────────────────────────────────────
生成随机 AES Key ──────┐
│
明文数据 ──(AES 加密)──┼───► 密文数据 ────────────────►
│
RSA 公钥加密 AES Key ──┘ │
│
RSA 私钥解密 AES Key ◄─── 加密后的 AES Key
│
(AES 解密密文数据) ◄─── 密文数据
│
得到明文数据
- AES → 用来加密大数据(快)
- RSA → 只加密 AES Key(保证安全)
- 两者结合就是 混合加密