优化配置,修复 demo bug
1,network 框架完善 2,websocket 机制完善 3,设计文档整理到架构文档 4,脚本,配置完善
This commit is contained in:
@@ -29,23 +29,25 @@ class EncryptionFlutterService {
|
||||
// Get secure random
|
||||
final secureRandom = FortunaRandom();
|
||||
secureRandom.seed(KeyParameter(_generateSecureRandomBytes(32)));
|
||||
|
||||
|
||||
// Create RSA key generator
|
||||
final keyGen = RSAKeyGenerator();
|
||||
keyGen.init(ParametersWithRandom(
|
||||
RSAKeyGeneratorParameters(BigInt.parse('65537'), keySize, 64),
|
||||
secureRandom,
|
||||
));
|
||||
|
||||
keyGen.init(
|
||||
ParametersWithRandom(
|
||||
RSAKeyGeneratorParameters(BigInt.parse('65537'), keySize, 64),
|
||||
secureRandom,
|
||||
),
|
||||
);
|
||||
|
||||
// Generate key pair
|
||||
final keyPair = keyGen.generateKeyPair();
|
||||
final rsaPublicKey = keyPair.publicKey as RSAPublicKey;
|
||||
final rsaPrivateKey = keyPair.privateKey as RSAPrivateKey;
|
||||
|
||||
final rsaPublicKey = keyPair.publicKey;
|
||||
final rsaPrivateKey = keyPair.privateKey;
|
||||
|
||||
// Export to PEM format
|
||||
final publicKeyPem = _encodeRSAPublicKey(rsaPublicKey);
|
||||
final privateKeyPem = _encodeRSAPrivateKey(rsaPrivateKey);
|
||||
|
||||
|
||||
return RsaKeyPairResult(
|
||||
publicKey: publicKeyPem,
|
||||
privateKey: privateKeyPem,
|
||||
@@ -59,18 +61,18 @@ class EncryptionFlutterService {
|
||||
String _encodeRSAPublicKey(RSAPublicKey publicKey) {
|
||||
// Build RSAPublicKeyInfo structure
|
||||
final topSeq = ASN1Sequence();
|
||||
|
||||
|
||||
// AlgorithmIdentifier: OID 1.2.840.113549.1.1.1 + NULL
|
||||
final algoSeq = ASN1Sequence();
|
||||
algoSeq.add(ASN1ObjectIdentifier([1, 2, 840, 113549, 1, 1, 1])); // RSA
|
||||
algoSeq.add(ASN1Null());
|
||||
topSeq.add(algoSeq);
|
||||
|
||||
|
||||
// RSAPublicKey: modulus + publicExponent
|
||||
final keySeq = ASN1Sequence();
|
||||
keySeq.add(ASN1Integer(publicKey.n!));
|
||||
keySeq.add(ASN1Integer(publicKey.exponent!));
|
||||
|
||||
|
||||
// BitString wrapping the key (with 0 unused bits prefix)
|
||||
final keyBytes = keySeq.encodedBytes;
|
||||
final keyList = List<int>.from(keyBytes);
|
||||
@@ -86,27 +88,27 @@ class EncryptionFlutterService {
|
||||
String _encodeRSAPrivateKey(RSAPrivateKey privateKey) {
|
||||
// Build RSAPrivateKey structure (PKCS#8 format)
|
||||
final topSeq = ASN1Sequence();
|
||||
|
||||
|
||||
// Version (0)
|
||||
topSeq.add(ASN1Integer(BigInt.zero));
|
||||
|
||||
|
||||
// Modulus
|
||||
topSeq.add(ASN1Integer(privateKey.n!));
|
||||
|
||||
|
||||
// Public Exponent
|
||||
topSeq.add(ASN1Integer(privateKey.exponent!));
|
||||
|
||||
|
||||
// Private Exponent
|
||||
topSeq.add(ASN1Integer(privateKey.privateExponent!));
|
||||
|
||||
|
||||
// Prime P
|
||||
topSeq.add(ASN1Integer(privateKey.p!));
|
||||
|
||||
|
||||
// Prime Q
|
||||
topSeq.add(ASN1Integer(privateKey.q!));
|
||||
|
||||
|
||||
// (Optional CRT params omitted for simplicity)
|
||||
|
||||
|
||||
final derBytes = topSeq.encodedBytes;
|
||||
final base64 = base64Encode(derBytes.toList());
|
||||
return '-----BEGIN PRIVATE KEY-----\n$base64\n-----END PRIVATE KEY-----';
|
||||
@@ -122,24 +124,24 @@ class EncryptionFlutterService {
|
||||
try {
|
||||
// Generate AES key from MD5(password)
|
||||
final aesKey = _md5Hash(password);
|
||||
|
||||
|
||||
// Generate random IV (16 bytes)
|
||||
final iv = _generateSecureRandomBytes(16);
|
||||
|
||||
|
||||
// AES encrypt using encrypt package
|
||||
final secretKey = encrypt_pkg.Key(aesKey);
|
||||
final encryptor = encrypt_pkg.Encrypter(
|
||||
encrypt_pkg.AES(secretKey, mode: encrypt_pkg.AESMode.cbc),
|
||||
);
|
||||
|
||||
|
||||
final encrypted = encryptor.encrypt(privateKey, iv: encrypt_pkg.IV(iv));
|
||||
final encryptedBytes = encrypted.bytes;
|
||||
|
||||
|
||||
// Combine IV + encrypted data
|
||||
final combined = Uint8List(iv.length + encryptedBytes.length);
|
||||
combined.setAll(0, iv);
|
||||
combined.setAll(iv.length, encryptedBytes);
|
||||
|
||||
|
||||
return base64Encode(combined);
|
||||
} catch (e) {
|
||||
throw Exception('Failed to encrypt private key: $e');
|
||||
@@ -154,25 +156,25 @@ class EncryptionFlutterService {
|
||||
try {
|
||||
// Generate AES key from MD5(password)
|
||||
final aesKey = _md5Hash(password);
|
||||
|
||||
|
||||
// Decode Base64
|
||||
final combined = base64Decode(encryptedPrivateKey);
|
||||
|
||||
|
||||
// Extract IV and encrypted data
|
||||
final iv = combined.sublist(0, 16);
|
||||
final encBytes = combined.sublist(16);
|
||||
|
||||
|
||||
// AES decrypt
|
||||
final secretKey = encrypt_pkg.Key(aesKey);
|
||||
final encryptor = encrypt_pkg.Encrypter(
|
||||
encrypt_pkg.AES(secretKey, mode: encrypt_pkg.AESMode.cbc),
|
||||
);
|
||||
|
||||
|
||||
final decrypted = encryptor.decrypt(
|
||||
encrypt_pkg.Encrypted(encBytes),
|
||||
iv: encrypt_pkg.IV(iv),
|
||||
);
|
||||
|
||||
|
||||
return decrypted;
|
||||
} catch (e) {
|
||||
throw Exception('Failed to decrypt private key: $e');
|
||||
@@ -185,11 +187,8 @@ class EncryptionFlutterService {
|
||||
SessionKeyResult generateSessionKey({int initialRound = 1}) {
|
||||
final keyBytes = _generateSecureRandomBytes(sessionKeySize);
|
||||
final key = base64Encode(keyBytes);
|
||||
|
||||
return SessionKeyResult(
|
||||
key: key,
|
||||
round: initialRound,
|
||||
);
|
||||
|
||||
return SessionKeyResult(key: key, round: initialRound);
|
||||
}
|
||||
|
||||
/// Encrypt session key with RSA public key
|
||||
@@ -200,11 +199,11 @@ class EncryptionFlutterService {
|
||||
try {
|
||||
// Parse RSA public key
|
||||
final rsaPublicKey = _parsePublicKey(publicKey);
|
||||
|
||||
|
||||
// RSA encrypt using PKCS1 padding (like native implementations)
|
||||
final cipher = PKCS1Encoding(RSAEngine());
|
||||
cipher.init(true, PublicKeyParameter<RSAPublicKey>(rsaPublicKey));
|
||||
|
||||
|
||||
final encryptedBytes = cipher.process(utf8.encode(sessionKey));
|
||||
return base64Encode(encryptedBytes);
|
||||
} catch (e) {
|
||||
@@ -220,11 +219,11 @@ class EncryptionFlutterService {
|
||||
try {
|
||||
// Parse RSA private key
|
||||
final rsaPrivateKey = _parsePrivateKey(privateKey);
|
||||
|
||||
|
||||
// RSA decrypt using PKCS1 padding (like native implementations)
|
||||
final cipher = PKCS1Encoding(RSAEngine());
|
||||
cipher.init(false, PrivateKeyParameter<RSAPrivateKey>(rsaPrivateKey));
|
||||
|
||||
|
||||
final decryptedBytes = cipher.process(base64Decode(encryptedSessionKey));
|
||||
return utf8.decode(decryptedBytes);
|
||||
} catch (e) {
|
||||
@@ -243,30 +242,27 @@ class EncryptionFlutterService {
|
||||
try {
|
||||
// Derive key for round
|
||||
final actualKey = _deriveKeyForRound(sessionKey, round);
|
||||
|
||||
|
||||
// Generate random IV (16 bytes for CTR)
|
||||
final iv = _generateSecureRandomBytes(16);
|
||||
|
||||
|
||||
// AES-CTR encrypt
|
||||
final secretKey = encrypt_pkg.Key(actualKey);
|
||||
final encryptor = encrypt_pkg.Encrypter(
|
||||
encrypt_pkg.AES(secretKey, mode: encrypt_pkg.AESMode.ctr),
|
||||
);
|
||||
|
||||
|
||||
final encrypted = encryptor.encrypt(plaintext, iv: encrypt_pkg.IV(iv));
|
||||
final encryptedBytes = encrypted.bytes;
|
||||
|
||||
|
||||
// Combine IV + encrypted data
|
||||
final combined = Uint8List(iv.length + encryptedBytes.length);
|
||||
combined.setAll(0, iv);
|
||||
combined.setAll(iv.length, encryptedBytes);
|
||||
|
||||
|
||||
final data = base64Encode(combined);
|
||||
|
||||
return EncryptedMessageResult(
|
||||
round: round,
|
||||
data: data,
|
||||
);
|
||||
|
||||
return EncryptedMessageResult(round: round, data: data);
|
||||
} catch (e) {
|
||||
throw Exception('Failed to encrypt message: $e');
|
||||
}
|
||||
@@ -281,25 +277,25 @@ class EncryptionFlutterService {
|
||||
try {
|
||||
// Derive key for round
|
||||
final actualKey = _deriveKeyForRound(sessionKey, round);
|
||||
|
||||
|
||||
// Decode Base64
|
||||
final combined = base64Decode(encryptedData);
|
||||
|
||||
|
||||
// Extract IV and encrypted data
|
||||
final iv = combined.sublist(0, 16);
|
||||
final encBytes = combined.sublist(16);
|
||||
|
||||
|
||||
// AES-CTR decrypt
|
||||
final secretKey = encrypt_pkg.Key(actualKey);
|
||||
final encryptor = encrypt_pkg.Encrypter(
|
||||
encrypt_pkg.AES(secretKey, mode: encrypt_pkg.AESMode.ctr),
|
||||
);
|
||||
|
||||
|
||||
final decrypted = encryptor.decrypt(
|
||||
encrypt_pkg.Encrypted(encBytes),
|
||||
iv: encrypt_pkg.IV(iv),
|
||||
);
|
||||
|
||||
|
||||
return decrypted;
|
||||
} catch (e) {
|
||||
throw Exception('Failed to decrypt message: $e');
|
||||
@@ -316,36 +312,34 @@ class EncryptionFlutterService {
|
||||
String? _aesSecret;
|
||||
|
||||
/// Decrypt push notification (AES-GCM)
|
||||
String decryptPushNotification({
|
||||
required String encryptedData,
|
||||
}) {
|
||||
String decryptPushNotification({required String encryptedData}) {
|
||||
try {
|
||||
final secret = _aesSecret;
|
||||
if (secret == null) {
|
||||
throw Exception('AES_SECRET not set');
|
||||
}
|
||||
|
||||
|
||||
// Convert hex string to bytes
|
||||
final secretBytes = _hexStringToBytes(secret);
|
||||
|
||||
|
||||
// Decode Base64
|
||||
final combined = base64Decode(encryptedData);
|
||||
|
||||
|
||||
// Extract IV and encrypted data
|
||||
final iv = combined.sublist(0, gcmIvLength);
|
||||
final encBytes = combined.sublist(gcmIvLength);
|
||||
|
||||
|
||||
// AES-GCM decrypt
|
||||
final secretKey = encrypt_pkg.Key(secretBytes);
|
||||
final encryptor = encrypt_pkg.Encrypter(
|
||||
encrypt_pkg.AES(secretKey, mode: encrypt_pkg.AESMode.gcm),
|
||||
);
|
||||
|
||||
|
||||
final decrypted = encryptor.decrypt(
|
||||
encrypt_pkg.Encrypted(encBytes),
|
||||
iv: encrypt_pkg.IV(iv),
|
||||
);
|
||||
|
||||
|
||||
return decrypted;
|
||||
} catch (e) {
|
||||
throw Exception('Failed to decrypt push notification: $e');
|
||||
@@ -375,10 +369,10 @@ class EncryptionFlutterService {
|
||||
Uint8List _deriveKeyForRound(String sessionKey, int targetRound) {
|
||||
// Base64 decode session key
|
||||
final keyBytes = base64Decode(sessionKey);
|
||||
|
||||
|
||||
// Apply MD5 for the round (simplified version)
|
||||
final hash = md5.convert(keyBytes).bytes as Uint8List;
|
||||
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
@@ -390,19 +384,19 @@ class EncryptionFlutterService {
|
||||
.replaceAll('\n', '')
|
||||
.trim();
|
||||
final bytes = base64Decode(base64);
|
||||
|
||||
|
||||
// Parse ASN.1 DER format
|
||||
final asn1Parser = ASN1Parser(Uint8List.fromList(bytes));
|
||||
final topLevelSeq = asn1Parser.nextObject() as ASN1Sequence;
|
||||
|
||||
|
||||
final subjectPublicKeyInfo = topLevelSeq.elements[1] as ASN1BitString;
|
||||
final keyBytes = subjectPublicKeyInfo.contentBytes();
|
||||
final keyParser = ASN1Parser(Uint8List.fromList(keyBytes));
|
||||
final keySeq = keyParser.nextObject() as ASN1Sequence;
|
||||
|
||||
|
||||
final modulus = keySeq.elements[0] as ASN1Integer;
|
||||
final publicExponent = keySeq.elements[1] as ASN1Integer;
|
||||
|
||||
|
||||
return RSAPublicKey(
|
||||
modulus.valueAsBigInteger,
|
||||
publicExponent.valueAsBigInteger,
|
||||
@@ -417,11 +411,11 @@ class EncryptionFlutterService {
|
||||
.replaceAll('\n', '')
|
||||
.trim();
|
||||
final bytes = base64Decode(base64);
|
||||
|
||||
|
||||
// Parse ASN.1 DER format
|
||||
final asn1Parser = ASN1Parser(Uint8List.fromList(bytes));
|
||||
final keySeq = asn1Parser.nextObject() as ASN1Sequence;
|
||||
|
||||
|
||||
final modulus = keySeq.elements[1] as ASN1Integer;
|
||||
final privateExponent = keySeq.elements[3] as ASN1Integer;
|
||||
final p = keySeq.elements[4] as ASN1Integer;
|
||||
@@ -440,7 +434,9 @@ class EncryptionFlutterService {
|
||||
final len = hex.length;
|
||||
final data = Uint8List(len ~/ 2);
|
||||
for (var i = 0; i < len; i += 2) {
|
||||
data[i ~/ 2] = (int.parse(hex[i], radix: 16) << 4) + int.parse(hex[i + 1], radix: 16);
|
||||
data[i ~/ 2] =
|
||||
(int.parse(hex[i], radix: 16) << 4) +
|
||||
int.parse(hex[i + 1], radix: 16);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
@@ -468,4 +464,3 @@ class EncryptedMessageResult {
|
||||
|
||||
EncryptedMessageResult({required this.round, required this.data});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user