确定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的属性的主要内容,如果未能解决你的问题,请参考以下文章

Flutter Container尺寸的确定

flutter useRootNavigator属性的作用

Flutter 无法确定任务 ':app:processDebugResources' 的依赖关系

flutter 使用ListView等列表组件错误记录

Flutter布局模型之水平垂直 - 石头的博客

Flutter 2:无法确定捆绑的 Java 版本