Flutter数据加密插件crypt的使用

发布于 1周前 作者 eggper 来自 Flutter

Flutter数据加密插件crypt的使用

描述

crypt包实现了Unix crypt格式中指定的SHA-256和SHA-512哈希算法,这些哈希算法常用于Unix和POSIX系统以及LDAP条目中作为身份验证凭据。该包可以生成类似于以下格式的字符串:

$5$xYWYo0raYwLSchAd$na8cL1H.ESWtof6DNwraE6p8WI9DYObZ3irMe01Guk6

或者带有自定义轮数(rounds)的:

$5$rounds=10000$saltstringsaltst$3xv.VbSHBb41AL9AvLeujZkZRBAwqFMz2.opqey6IcA

字符串格式以$符号分隔字段,依次为算法指示符、轮数(可选)、盐值(salt)和哈希值。

对于SHA-256哈希,字符串以$5$开头;对于SHA-512哈希,字符串以$6$开头。默认情况下,当使用SHA-256或SHA-512时,轮数设置为5000。

注意事项

不同的系统可能对crypt格式字符串有不同的使用方式。例如,在LDAP posixAccount条目的userPassword属性中,需要在crypt格式字符串前加上{crypt}

使用方法

验证密码是否匹配给定的crypt格式哈希

要测试输入的密码是否与创建crypt格式哈希时使用的密码相匹配,可以从crypt格式哈希创建一个Crypt对象,并调用其match方法。

bool isValid(String cryptFormatHash, String enteredPassword) =>
  Crypt(cryptFormatHash).match(enteredPassword);

生成crypt格式哈希

要生成一个crypt格式哈希,可以使用Crypt.sha256Crypt.sha512构造函数,并将其转换为字符串。

import 'package:crypt/crypt.dart';

void main() {
  // 创建crypt字符串
  
  // 默认轮数和随机生成的盐
  final c1 = Crypt.sha256('p@ssw0rd');

  // 随机生成的盐
  final c2 = Crypt.sha256('p@ssw0rd', rounds: 10000);

  // 默认轮数
  final c3 = Crypt.sha256('p@ssw0rd', salt: 'abcdefghijklmnop');

  // 不使用默认值
  final c4 = Crypt.sha256('p@ssw0rd', rounds: 10000, salt: 'abcdefghijklmnop');

  // SHA-512
  final d1 = Crypt.sha512('p@ssw0rd');

  print(c1);
  print(c2);
  print(c3);
  print(c4);
  print(d1);

  // 比较输入值与crypt哈希

  for (final hashString in [
    r'$5$zQUCjEzs9jnrRdCK$dbo1i9WjQjbUwOC4JCRAZHpfd31Dh676vI0L6w0dZw1',
    c1.toString(),
    c2.toString(),
    c3.toString(),
    c4.toString(),
    d1.toString(),
  ]) {
    // 解析crypt字符串:这会提取类型、轮数和盐
    final h = Crypt(hashString);

    const correctValue = 'p@ssw0rd';
    const wrongValue = '123456';

    if (!h.match(correctValue)) {
      print('Error: unexpected non-match: $correctValue');
    }

    if (h.match(wrongValue)) {
      print('Error: unexpected match: $wrongValue');
    }
  }
}

以上示例代码将输出如下内容:

$5$jYq8PvB6hI3cLREQ$FGBjCL5NO1qSwync3LOlCWTnIBJCjVsFtst9jNnnBx9
$5$rounds=10000$wJiiNy1TwwaWhGFN$t2JsIqOgfXh/3LLQF.YA9XDlJmtpLYmSe4i9TZl7cM.
$5$abcdefghijklmnop$gUWLu9sDI2Qvs112Xb8jmgD3ySIRE5ek63jk6ybSs7D
$5$rounds=10000$abcdefghijklmnop$51muKIziT9VAyDZ2ZueAYvAwgIYx0cLxUCIAlPoWaHD
$6$LJgzW1oI9UZ5w8HO$pTL3hmFg2zBkQPqRhcej6CmY2Az0WLDVlnMGTg//71D3hDEvKCB7XqwtinHEM1rlD/YAlEjhy2Lb3LJQsNvXx.

请注意,具有随机生成盐的crypt字符串每次程序运行时都会产生不同的值,而使用固定盐的crypt字符串将始终产生相同的值。

特性和问题

随机数生成器

盐生成使用的是加密安全的随机数生成器(如果可用)。如果没有可用的加密安全随机数生成器,则会回退到使用加密不安全的随机数生成器。

可以通过设置Crypt.cryptographicallySecureSaltstrue来防止使用加密不安全的随机数生成器。如果尝试在不支持加密安全随机数生成器的平台上生成盐,则会抛出异常。

显式地将其设置为false可以在未来版本中允许这种回退行为。当前默认设置为false,以保持向后兼容性。但未来版本可能会将默认值设置为true以提高安全性。

依赖于crypto

当前版本依赖于Dart的crypto包版本3.0.0,它支持SHA-512。如果您需要使用更早版本的crypto包,请使用本包的2.0.0版本,但请注意该版本不支持SHA-512 crypt字符串且不是空安全的。

如果有任何功能请求或问题,请在GitHub issue tracker上提交。


更多关于Flutter数据加密插件crypt的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter数据加密插件crypt的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何在Flutter中使用crypt插件进行数据加密和解密的示例代码。请注意,crypt插件通常指的是pointycastle或其他类似的库,这些库提供了加密和解密功能。由于crypt这个名称可能不够具体,我会使用pointycastle作为示例,这是Flutter中常用的加密库。

首先,你需要在你的pubspec.yaml文件中添加pointycastle依赖:

dependencies:
  flutter:
    sdk: flutter
  pointycastle: ^3.0.1  # 请检查最新版本号

然后,运行flutter pub get来安装依赖。

接下来,是一个简单的示例,演示如何使用pointycastle进行AES加密和解密:

import 'package:flutter/material.dart';
import 'package:pointycastle/export.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter Encryption Example'),
        ),
        body: Center(
          child: EncryptionExample(),
        ),
      ),
    );
  }
}

class EncryptionExample extends StatefulWidget {
  @override
  _EncryptionExampleState createState() => _EncryptionExampleState();
}

class _EncryptionExampleState extends State<EncryptionExample> {
  String? originalText = "Hello, Flutter!";
  String? encryptedText;
  String? decryptedText;

  void _encrypt() {
    final key = Uint8List.fromList(List.generate(32, (i) => (i % 256).toByte()));
    final plainText = Uint8List.fromList(originalText!.codeUnits);

    final iv = Uint8List.fromList(List.generate(16, (i) => (i % 256).toByte()));
    final aes = AESFastEngine();
    final cipher = CBCBlockCipher(aes);
    final params = ParametersWithIV<KeyParameter>(KeyParameter(key), iv);

    final padder = PaddedBufferedBlockCipher(cipher);
    padder.init(true, params);

    final encrypted = padder.processBytes(plainText, true);
    final encryptedBytes = List<int>.from(encrypted);

    setState(() {
      encryptedText = encryptedBytes.map((e) => e.toRadixString(16).padStart(2, '0')).join();
    });
  }

  void _decrypt() {
    if (encryptedText == null) return;

    final key = Uint8List.fromList(List.generate(32, (i) => (i % 256).toByte()));
    final encryptedBytes = Uint8List.fromList(
        encryptedText!.matches('.{2}').map((hex) => int.parse(hex, radix: 16)).toList()
    );

    final iv = Uint8List.fromList(List.generate(16, (i) => (i % 256).toByte()));
    final aes = AESFastEngine();
    final cipher = CBCBlockCipher(aes);
    final params = ParametersWithIV<KeyParameter>(KeyParameter(key), iv);

    final padder = PaddedBufferedBlockCipher(cipher);
    padder.init(false, params);

    final decrypted = padder.processBytes(encryptedBytes, true);
    final decryptedBytes = List<int>.from(decrypted);

    setState(() {
      decryptedText = String.fromCharCodes(decryptedBytes);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        Text('Original Text: $originalText'),
        ElevatedButton(onPressed: _encrypt, child: Text('Encrypt')),
        if (encryptedText != null)
          Text('Encrypted Text: $encryptedText'),
        if (decryptedText != null)
          Text('Decrypted Text: $decryptedText'),
        ElevatedButton(onPressed: _decrypt, child: Text('Decrypt')),
      ],
    );
  }
}

这个示例展示了如何使用AES加密和解密一个简单的字符串。注意,这里的密钥和初始化向量(IV)是随机生成的,仅用于演示目的。在实际应用中,你应该使用安全的密钥管理和IV生成方法,并确保密钥和IV的保密性。

此外,加密和解密过程中涉及的数据处理(如编码和解码)也需要仔细处理,以确保数据的完整性和安全性。这个示例使用了十六进制编码来显示加密后的数据,但在实际应用中,你可能会选择其他更适合的编码方式。

回到顶部