|
|
@@ -0,0 +1,185 @@
|
|
|
+
|
|
|
+import 'dart:convert';
|
|
|
+
|
|
|
+import 'dart:math';
|
|
|
+import 'package:pointycastle/pointycastle.dart';
|
|
|
+import 'package:pointycastle/key_derivators/pbkdf2.dart';
|
|
|
+import 'package:pointycastle/digests/sha256.dart';
|
|
|
+import 'package:pointycastle/macs/hmac.dart';
|
|
|
+
|
|
|
+
|
|
|
+// 随机字符串
|
|
|
+String randomString(int len) {
|
|
|
+ if (len <= 0) {
|
|
|
+ len = 16;
|
|
|
+ }
|
|
|
+ // 安全字符集(去掉Base64中的特殊字符 + / =)
|
|
|
+ const charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-';
|
|
|
+ final random = Random.secure();
|
|
|
+ final result = StringBuffer();
|
|
|
+
|
|
|
+ for (int i = 0; i < len; i++) {
|
|
|
+ final index = random.nextInt(charset.length);
|
|
|
+ result.write(charset[index]);
|
|
|
+ }
|
|
|
+
|
|
|
+ return result.toString();
|
|
|
+}
|
|
|
+
|
|
|
+String randomStringEx(int len, {
|
|
|
+ final bool includeLowercase = true,
|
|
|
+ final bool includeUppercase = true,
|
|
|
+ final bool includeDigits = true,
|
|
|
+ final bool includeSpecialChars = true,
|
|
|
+ final String? customCharset,
|
|
|
+}) {
|
|
|
+ if (len <= 0) {
|
|
|
+ len = 16;
|
|
|
+ }
|
|
|
+
|
|
|
+ String? charset;
|
|
|
+ if (customCharset != null) {
|
|
|
+ charset = customCharset;
|
|
|
+ } else {
|
|
|
+ final buffer = StringBuffer();
|
|
|
+ if (includeLowercase) buffer.write('abcdefghijklmnopqrstuvwxyz');
|
|
|
+ if (includeUppercase) buffer.write('ABCDEFGHIJKLMNOPQRSTUVWXYZ');
|
|
|
+ if (includeDigits) buffer.write('0123456789');
|
|
|
+ if (includeSpecialChars) buffer.write('_-');
|
|
|
+ charset = buffer.toString();
|
|
|
+ }
|
|
|
+ if (charset.isEmpty) {
|
|
|
+ throw StateError('empty charset');
|
|
|
+ }
|
|
|
+
|
|
|
+ final random = Random.secure();
|
|
|
+ final result = StringBuffer();
|
|
|
+
|
|
|
+ for (int i = 0; i < len; i++) {
|
|
|
+ final index = random.nextInt(charset.length);
|
|
|
+ result.write(charset[index]);
|
|
|
+ }
|
|
|
+
|
|
|
+ return result.toString();
|
|
|
+}
|
|
|
+
|
|
|
+/// password to PBKDF2 password
|
|
|
+String passwordWithPBKDF2({
|
|
|
+ required String password,
|
|
|
+ required String salt,
|
|
|
+ int iterations = 100000,
|
|
|
+ int keyLength = 32,
|
|
|
+}) {
|
|
|
+ final seed = utf8.encode(password);
|
|
|
+ final saltBytes = utf8.encode(salt);
|
|
|
+
|
|
|
+ // 1. 初始化 PBKDF2 派生器(HMAC-SHA256 作为伪随机函数)
|
|
|
+ final pbkdf2 = PBKDF2KeyDerivator(
|
|
|
+ HMac(SHA256Digest(), 64), // HMAC-SHA256,64字节块大小
|
|
|
+ );
|
|
|
+
|
|
|
+ // 2. 设置参数:盐值 + 迭代次数 + 密钥长度(AES-256 需要 32 字节)
|
|
|
+ final params = Pbkdf2Parameters(
|
|
|
+ saltBytes,
|
|
|
+ iterations, // 迭代次数(越高越安全,建议 10万+)
|
|
|
+ keyLength, // 输出密钥长度(32字节 = 256位,适配 AES-256)
|
|
|
+ );
|
|
|
+ pbkdf2.init(params);
|
|
|
+
|
|
|
+ // 3. 派生密钥(返回 32 字节的 AES-256 密钥)
|
|
|
+ final keyBytes = pbkdf2.process(seed);
|
|
|
+ return base64Encode(keyBytes);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+//
|
|
|
+// /// ---------- 使用 HMAC-SHA256 实现 PBKDF2 ----------
|
|
|
+// ///
|
|
|
+// /// - [password]: 用户密码(UTF-8 编码后的字节)
|
|
|
+// /// - [salt]: 盐(建议随机生成)
|
|
|
+// /// - [iterations]: 迭代次数(如 10000)
|
|
|
+// /// - [keyLength]: 派生密钥长度(字节,如 32 = 256 位)
|
|
|
+//
|
|
|
+// String generatePasswordWithPBKDF2({
|
|
|
+// required String password,
|
|
|
+// required String salt,
|
|
|
+// int iterations = 10000,
|
|
|
+// int keyLength = 32,
|
|
|
+// }) {
|
|
|
+// final key = base64.encode(pbkdf2HmacSha256(
|
|
|
+// password: utf8.encode(password),
|
|
|
+// salt: utf8.encode(salt),
|
|
|
+// iterations: iterations,
|
|
|
+// keyLength: keyLength,
|
|
|
+// ));
|
|
|
+// return key;
|
|
|
+// }
|
|
|
+//
|
|
|
+// Uint8List pbkdf2HmacSha256({
|
|
|
+// required Uint8List password,
|
|
|
+// required Uint8List salt,
|
|
|
+// int iterations = 100000,
|
|
|
+// int keyLength = 32,
|
|
|
+// }) {
|
|
|
+// if (iterations < 1) throw ArgumentError('Iterations must be >= 1');
|
|
|
+// if (keyLength <= 0) throw ArgumentError('Key length must be > 0');
|
|
|
+//
|
|
|
+// final List<int> derivedKey = [];
|
|
|
+// final int hLen = 32; // SHA-256 输出长度(字节)
|
|
|
+// final int l = (keyLength + hLen - 1) ~/ hLen; // 块数
|
|
|
+//
|
|
|
+// for (int i = 1; i <= l; i++) {
|
|
|
+// // T_i = F(Password, Salt, Iterations, i)
|
|
|
+// final blockSalt = _concat(salt, _encodeInt(i));
|
|
|
+// final Uint8List u = hmacSha256(password, blockSalt);
|
|
|
+// Uint8List t = u;
|
|
|
+//
|
|
|
+// for (int j = 1; j < iterations; j++) {
|
|
|
+// final Uint8List uNext = hmacSha256(password, u);
|
|
|
+// t = _xor(t, uNext);
|
|
|
+// u.setAll(0, uNext); // 更新 u 为 uNext
|
|
|
+// }
|
|
|
+//
|
|
|
+// derivedKey.addAll(t);
|
|
|
+// }
|
|
|
+//
|
|
|
+// return Uint8List.fromList(derivedKey.sublist(0, keyLength));
|
|
|
+// }
|
|
|
+//
|
|
|
+// // HMAC-SHA256 辅助函数
|
|
|
+// Uint8List hmacSha256(Uint8List key, Uint8List data) {
|
|
|
+// final hmac = Hmac(sha256, key);
|
|
|
+// final digest = hmac.convert(data);
|
|
|
+// return Uint8List.fromList(digest.bytes);
|
|
|
+// }
|
|
|
+//
|
|
|
+// // 将整数编码为大端 4 字节(符合 RFC 2898)
|
|
|
+// Uint8List _encodeInt(int i) {
|
|
|
+// return Uint8List.fromList([
|
|
|
+// (i >> 24) & 0xFF,
|
|
|
+// (i >> 16) & 0xFF,
|
|
|
+// (i >> 8) & 0xFF,
|
|
|
+// i & 0xFF,
|
|
|
+// ]);
|
|
|
+// }
|
|
|
+//
|
|
|
+// // 拼接两个 Uint8List
|
|
|
+// Uint8List _concat(Uint8List a, Uint8List b) {
|
|
|
+// final result = Uint8List(a.length + b.length);
|
|
|
+// result.setAll(0, a);
|
|
|
+// result.setAll(a.length, b);
|
|
|
+// return result;
|
|
|
+// }
|
|
|
+//
|
|
|
+// // 按字节异或两个等长 Uint8List
|
|
|
+// Uint8List _xor(Uint8List a, Uint8List b) {
|
|
|
+// if (a.length != b.length) {
|
|
|
+// throw ArgumentError('XOR requires equal-length inputs');
|
|
|
+// }
|
|
|
+// final result = Uint8List(a.length);
|
|
|
+// for (int i = 0; i < a.length; i++) {
|
|
|
+// result[i] = a[i] ^ b[i];
|
|
|
+// }
|
|
|
+// return result;
|
|
|
+// }
|
|
|
+
|