确定flutter encrypt.dart的属性
Posted
技术标签:
【中文标题】确定flutter encrypt.dart的属性【英文标题】:Determining attributes of flutter encrypt.dart 【发布时间】:2021-09-30 22:33:23 【问题描述】:我正在使用encrypt.dart 对基于 32 位密码(“密码”)的字符串(“文本”)进行 AES 加密,如下所示:
encryptPass(String text, String password)
final key = getKey(password);
final iv = encrypt.IV.fromLength(16);
final encrypter = encrypt.Encrypter(encrypt.AES(key)); //Uses AES/SIC/PKCS7
final e = encrypter.encrypt(text, iv: iv);
String encryptedString = e.base64.toString();
return encryptedString;
getKey(String masterPass)
String keyString = masterPass;
if (keyString.length < 32)
int count = 32 - keyString.length;
for (var i = 0; i < count; i++)
keyString += ".";
final keyReturn = encrypt.Key.fromUtf8(keyString);
return keyReturn;
旁注:这是可行的,但它每次都会为给定的输入字符串生成相同的值,即使我的“iv”和“salt”据说是随机的。这是怎么发生的?
主要问题:我正在尝试使用 kotlin 中的海绵城堡重新创建此过程。问题是我不知道 encrypt.dart AES 函数的某些重要属性。值用于:
盐长: 16, 32, 128, 256?? (encrypted.dart 中的“desiredKeyLength”变量。未在任何地方指定) 迭代次数:(我认为这是 100,但我不确定。) 密钥算法:我假设 PBKDF2WithHmacSHA1 基于 encrypted.dart 的“最终 pbkdf2”。 密钥长度: ?
这是我目前对spongy castle 实现的尝试以供参考:
fun encryptAESBasic(input: String, password: String): String
Security.insertProviderAt(org.spongycastle.jce.provider.BouncyCastleProvider(), 1)
val masterpw = password.toCharArray()
val random = SecureRandom()
val salt = ByteArray(256)
random.nextBytes(salt)
val factory: SecretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1")
val spec: KeySpec = PBEKeySpec(masterpw, salt, 100, 128)
val tmp: SecretKey = factory.generateSecret(spec)
val key: SecretKey = tmp
val cipher = Cipher.getInstance("AES/SIC/PKCS7PADDING", "SC")
val iv = ByteArray(16)
SecureRandom().nextBytes(iv)
cipher.init(Cipher.ENCRYPT_MODE, key, IvParameterSpec(iv))
val cipherText: ByteArray = cipher.doFinal(input.toByteArray())
return cipherText.toString()
【问题讨论】:
【参考方案1】:以下数据(您的问题的答案)取自 https://github.com/leocavalcante/encrypt/blob/5.x/lib/src/encrypted.dart(第 65-72 行):
iterationCount = 100
PBKDF2KeyDerivator(Mac('SHA-1/HMAC')) = PBKDF2WithHmacSHA1
The key length is taken from the key instantiation: final key = Key.fromLength(32);
and the salt length is equal to the key length: salt = SecureRandom(desiredKeyLength).bytes;
https://github.com/leocavalcante/encrypt/blob/5.x/lib/src/algorithms/aes.dart中的算法模式默认为SIC
"AES(this.key, this.mode = AESMode.sic, this.padding = 'PKCS7'"
【讨论】:
“密钥长度取自密钥实例化”究竟是什么意思?我没有在实例化任何东西时指定密钥长度。 您的“getKey”函数会生成一个长度为 32 字节的密钥。它需要一个字符串,如果它不是 32 字节长度,则添加一个“。” (点)到字符串。因此,要在评论中回答您的问题:您将密钥长度定义为“32”。 :-)【参考方案2】:Dart 代码使用零 IV(仅由 0x00
值组成的 IV),这就是为什么总是生成相同的密文。
如您所见,Dart 代码默认应用 SIC 模式和 PKCS7 填充。 SIC 模式是CTR 模式的另一个名称,它是一种流密码模式,因此不需要任何填充。因此不需要在 Dart 代码中使用 PKCS7 填充。
请注意,将 CTR 模式与静态 IV(例如零 IV)结合使用是一个致命错误,通常非常不安全(s.here)。
作为密钥派生,Dart 代码用.
填充密码,直到密钥大小为 32 字节,这是 AES-256 所要求的。这种密钥派生也是非常不安全的。使用密码时,应始终使用可靠的密钥派生函数,如 PBKDF2(如在 Kotlin 代码中)。
因此,在移植到 Kotlin 之前,应修改 Dart 代码并使其更加安全。这需要进行以下更改:
将为每次加密生成一个随机 IV。 应该禁用 PKCS7 填充。 代码不检查密文的真实性/完整性。为此,必须应用额外的身份验证标签 (MAC)。建议从 CTR 切换到 GCM 模式,该模式基于 CTR 模式但除了机密性(认证加密)之外还包括数据真实性/完整性,并隐式生成标签。 必须使用安全密钥派生(例如 PBKDF2,参见 Kotlin 代码)。与此相结合,将为每个密钥派生生成一个随机盐(s.也是other answer)。 Salt 和 IV(均非机密)以及标签将与密文 (@987654329@) 连接。请注意,对于 GCM,许多库会隐式执行密文和标记的连接。当然 - 从技术角度来看 - Dart 代码可以移植到 Kotlin,例如
fun encryptPass(text: String, password: String): String
val secretKeySpec = SecretKeySpec(getKey(password), "AES") // Apply a reliable key derivation function (getKey() is insecure)
val cipher = Cipher.getInstance("AES/CTR/PKCS5PADDING") // Disable padding (CTR doesn't require padding)
val iv = ByteArray(16) // Generate random IV (CTR with static IV is extremely insecure)
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, IvParameterSpec(iv))
val cipherText: ByteArray = cipher.doFinal(text.toByteArray(Charset.forName("UTF-8"))) // Authenticity/integrity missing
return Base64.encodeToString(cipherText, Base64.DEFAULT); // Concatenation of salt, IV, ciphertext and authentication tag missing
fun getKey(masterPass: String): ByteArray
return masterPass.padEnd(32, '.').toByteArray(Charset.forName("UTF-8"))
它给出了与 Dart 代码相同的结果(不需要使用 SpongyCastle),但出于安全原因,不应使用此代码。
【讨论】:
感谢您的详尽回复!会消化并回复您。 这是迄今为止我在堆栈上收到的最好的答案。谢谢。该方法暂时有效。用即将到来的输入重写。 我在 encrypt.dart 库页面上查看了我的原始实现与推荐的方法。该方法似乎是相同的。似乎飞镖库可以使用你的帮助! github.com/leocavalcante/encrypt/issues/225 @metamonkey - encrypt 包只是 PointyCastle 包的一小部分功能的高级包装器。与 PointyCastle 不同,encrypt 并不支持上述所有功能。 PointyCastle 可以实现所有功能。如果您有兴趣,您可以在 SO 上提出相应的问题,与评论相比,这将允许更详细的解释。以上是关于确定flutter encrypt.dart的属性的主要内容,如果未能解决你的问题,请参考以下文章