下面是dart Aes加密逻辑的代码
static String encryptAESGCM(String plaintext, String key) {
try {
final keyBytes = Uint8List.fromList(utf8.encode(key));
// SecureRandom secureRandom = getSecureRandom();
// Uint8List data = secureRandom.nextBytes(12);
Uint8List data = generateSecureRandomData(12);
// Uint8List data = generateNonSecureRandomData(12);
final cipher = GCMBlockCipher(AESEngine());
final params = AEADParameters(KeyParameter(keyBytes), 128, data, Uint8List(0));
cipher.init(true, params); // Initialize for encryption
final plaintextBytes = utf8.encode(plaintext);
final ciphertextBytes = cipher.process(Uint8List.fromList(plaintextBytes)); // Process all data at once
return base64.encode(ciphertextBytes); // Base64 encode for transmission
} catch (error) {
// Handle encryption errors gracefully
print("Encryption error: $error");
return ""; // Or return a suitable error message
}
}
static Uint8List generateSecureRandomData(size) {
return Uint8List.fromList(List<int>.generate(size, (_) => Random.secure().nextInt(256))); // cryptographically secure
}
在此代码中,我尝试传递我的用户名和密码纯文本并将其返回为加密的 但是当我在 api 调用上使用这些加密文本时,它显示无效凭证,可能是我的加密逻辑不正确 下面我粘贴我的服务器端 C# 代码
public string UserNamePasswordEncryption(stringplaintext)
{
var key = _configuration.GetSection("keypath").GetValue<string>("key");
using var aes = new AesCcm(Encoding.UTF8.GetBytes(key));
var nonce = new byte[AesGcm.NonceByteSizes.MaxSize];
RandomNumberGenerator.Fill(nonce);
var plaintextBytes = Encoding.UTF8.GetBytes(plaintext);
var ciphertextBytes = new byte[plaintextBytes.Length];
var tag = new byte[AesGcm.TagByteSizes.MaxSize];
aes.Encrypt(nonce, plaintextBytes, ciphertextBytes, tag);
return new AesGcmCiphertext(nonce, tag, ciphertextBytes).ToString();
}
剩余的C#代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
namespace model.Models
{
public class AesGcmCiphertext
{
public byte[] Nonce { get; }
public byte[] Tag { get; }
public byte[] CiphertextBytes { get; }
public static AesGcmCiphertext FromBase64String(string data)
{
var dataBytes = Convert.FromBase64String(data);
return new AesGcmCiphertext(
dataBytes.Take(AesGcm.NonceByteSizes.MaxSize).ToArray(),
dataBytes[^AesGcm.TagByteSizes.MaxSize..],
dataBytes[AesGcm.NonceByteSizes.MaxSize..^AesGcm.TagByteSizes.MaxSize]
);
}
public AesGcmCiphertext(byte[] nonce, byte[] tag, byte[] ciphertextBytes)
{
Nonce = nonce;
Tag = tag;
CiphertextBytes = ciphertextBytes;
}
public override string ToString()
{
return Convert.ToBase64String(Nonce.Concat(CiphertextBytes).Concat(Tag).ToArray());
}
}
}
我不知道我的 dart 是这个 C# 的替代代码
这两个代码有以下几点不同:
为了使两种实现兼容,并且由于 C# 代码是参考代码,因此必须相应地调整 Dart 代码,例如:
import 'dart:convert';
import 'dart:math';
import 'dart:typed_data';
import 'package:pointycastle/api.dart';
import 'package:pointycastle/block/aes.dart';
import 'package:pointycastle/block/modes/ccm.dart';
import 'package:pointycastle/random/fortuna_random.dart';
...
final keyBytes = Uint8List.fromList(utf8.encode(key));
final nonce = getSecureRandom().nextBytes(12);
final plaintextBytes = utf8.encode(plaintext);
final cipher = CCMBlockCipher(AESEngine()) // apply CCM
..init(true, AEADParameters(KeyParameter(keyBytes), 128, nonce, Uint8List(0)));
final ciphertextBytes = cipher.process(Uint8List.fromList(plaintextBytes)); // returns ciphertext | tag
return base64.encode(nonce + ciphertextBytes); // create nonce | ciphertext | tag
...
SecureRandom getSecureRandom() {
List<int> seed = List<int>.generate(32, (_) => Random.secure().nextInt(256));
return FortunaRandom()..seed(KeyParameter(Uint8List.fromList(seed)));
}
备注:
关于C#代码:在C#代码中,使用CCM进行加密,但有时会应用GCM类。这是一种糟糕的风格,因为它具有误导性(正如你所经历的那样)。相反,应使用相应的 CCM 类(或在用户定义类的情况下重命名),并且如果常量不同(例如
AesGcm.NonceByteSizes.MaxSize
= 12 和 AesCcm.NonceByteSizes.MaxSize
= 13),则应明确定义这些。
SecureRandom
类,PointyCastle 提供了一个 CSPRNG,其中有各种算法的实现,例如Fortuna 算法,在 FortunaRandom
类中实现。由于由于加密,您已经在使用 PointyCastle(因此这并不意味着额外的依赖),因此也可以使用 PointyCastle 的 CSPRNG。这有更好的记录,因此比指定不明确的Random.secure()
(通常仅用作使用SecureRandom
/FortunaRandom
时生成种子的熵源)更透明。
关于密钥:一般来说,UTF-8编码意味着密码或密码短语,它通常比随机字节序列具有更小的熵。在这种情况下,不应将密码直接用作密钥,而应应用密钥派生函数(例如 Argon2 或至少 PBKDF2)来从密码中派生出actual密钥,并结合每个密钥的随机盐加密。盐不是秘密的,它与密文(通常是串联的)一起提供给解密方。
关于算法的选择:您可能有理由使用 CCM 而不是 GCM(遗留应用程序等),但通常(更现代的)GCM 被认为优于 CCM,因此应该应用,请参见例如这里。