Flutter加密解密插件cryptography_flutter的使用

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

Flutter加密解密插件cryptography_flutter的使用

概述

cryptography_flutter 是一个Flutter插件,它允许在Android、iOS和Mac OS X上使用原生API进行加密操作。该插件由gohilla.com维护,并且遵循Apache License 2.0开源协议。

使用理由

  1. 安全性:操作系统API从安全角度来看是更优的选择。
  2. 性能优越:与纯Dart实现相比,操作系统API可以快至100倍。
  3. 跨平台支持:当操作系统API不可用时,会回退到package:cryptography的实现。

通用行为

  • 包含了两种类型的类:
    • FlutterChacha20,在Android/iOS/Mac OS X上使用操作系统API;如果操作系统不支持,则使用背景实现(如BackgroundChacha20)或纯Dart实现(如DartChacha20)。
    • 类似BackgroundChacha20,通过Flutter SDK中的compute函数将长时间计算移动到后台隔离区。
  • 对于非常小或过大的输入,避免跨隔离区传递消息开销,直接在同一隔离区内完成计算。
  • 设有队列机制防止并发请求过多导致内存耗尽。

快速开始

在项目的pubspec.yaml文件中添加依赖:

dependencies:
  cryptography: ^2.7.0
  cryptography_flutter: ^2.3.2

更多API文档请参阅pub.dev/packages/cryptography

算法行为

AES-GCM

  • FlutterAesGcm用于Android、iOS和Mac OS X,性能比纯Dart实现高约50倍。
  • BackgroundAesGcm用于Windows和Linux处理足够大的输入。

ChaCha20-Poly1305-AEAD

  • FlutterChacha20适用于Android和Apple系统,性能提升显著。
  • BackgroundChacha20为Windows和Linux准备。

NIST ECDH / ECDSA

  • FlutterEcdhFlutterEcdsa仅限Apple系统。

Ed25519 和 X25519

  • 分别有FlutterEd25519/BackgroundEd25519FlutterX25519/BackgroundX25519,均针对Apple系统。

HMAC 和 PBKDF2

  • FlutterHmacFlutterPbkdf2分别适用于Android,而BackgroundPbkdf2则覆盖Apple、Windows和Linux。

示例代码

下面是一个简单的示例应用,演示如何使用AES-GCM算法进行加密解密操作:

import 'dart:convert';
import 'package:cryptography/cryptography.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(const MaterialApp(
    title: 'Cryptography demo',
    home: CipherPage(),
  ));
}

class CipherPage extends StatefulWidget {
  const CipherPage({Key? key}) : super(key: key);

  @override
  State<CipherPage> createState() => _CipherPageState();
}

class _CipherPageState extends State<CipherPage> {
  static final _aesGcm128 = AesGcm.with128bits();

  Cipher _cipher = _aesGcm128;
  final _secretKeyController = TextEditingController();
  final _nonceController = TextEditingController();

  List<int> _clearText = [];
  final _cipherTextController = TextEditingController();
  final _macController = TextEditingController();
  Object? _error;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Center(
          child: Container(
            constraints: const BoxConstraints(maxWidth: 500),
            padding: const EdgeInsets.all(20),
            child: ListView(
              children: [
                // Cipher选择器
                InputDecorator(
                  decoration: const InputDecoration(labelText: 'Cipher'),
                  child: DropdownButton<Cipher>(
                    value: _cipher,
                    onChanged: (newValue) {
                      setState(() {
                        _cipher = newValue ?? _aesGcm128;
                        _encrypt();
                      });
                    },
                    items: [
                      DropdownMenuItem(
                        value: _aesGcm128,
                        child: const Text('AES-GCM (128-bits)'),
                      ),
                      // 其他Cipher选项...
                    ],
                  ),
                ),
                const SizedBox(height: 10),
                Text('Class: ${_cipher.runtimeType}'),
                const SizedBox(height: 10),
                // Secret Key输入框
                Row(children: [
                  Expanded(
                    child: TextField(
                      controller: _secretKeyController,
                      onChanged: (value) {
                        _encrypt();
                      },
                      minLines: 1,
                      maxLines: 16,
                      enableInteractiveSelection: true,
                      decoration: InputDecoration(
                          labelText: 'Secret key (${_cipher.secretKeyLength} bytes)'),
                    ),
                  ),
                  ElevatedButton(
                    onPressed: () async {
                      final secretKey = await _cipher.newSecretKey();
                      final bytes = await secretKey.extractBytes();
                      _secretKeyController.text = _toHex(bytes);
                      await _encrypt();
                    },
                    child: const Text('Generate'),
                  ),
                ]),
                const SizedBox(height: 10),
                // Nonce输入框
                Row(children: [
                  Expanded(
                    child: TextField(
                      controller: _nonceController,
                      onChanged: (value) {
                        _encrypt();
                      },
                      minLines: 1,
                      maxLines: 16,
                      enableInteractiveSelection: true,
                      decoration: InputDecoration(
                          labelText: 'Nonce (${_cipher.nonceLength} bytes)'),
                    ),
                  ),
                  ElevatedButton(
                    onPressed: () async {
                      _nonceController.text = _toHex(_cipher.newNonce());
                      await _encrypt();
                    },
                    child: const Text('Generate'),
                  ),
                ]),
                const SizedBox(height: 30),
                const Text('Encrypt'),
                TextField(
                  onChanged: (newValue) {
                    try {
                      _clearText = utf8.encode(newValue);
                      _encrypt();
                    } catch (error) {
                      setState(() {
                        _error = error;
                      });
                    }
                  },
                  minLines: 1,
                  maxLines: 16,
                  enableInteractiveSelection: true,
                  decoration: const InputDecoration(labelText: 'Cleartext (text)'),
                ),
                const SizedBox(height: 10),
                TextField(
                  controller: _cipherTextController,
                  minLines: 1,
                  maxLines: 16,
                  enableInteractiveSelection: true,
                  decoration: const InputDecoration(labelText: 'Ciphertext (hex)'),
                ),
                const SizedBox(height: 10),
                TextField(
                  controller: _macController,
                  minLines: 1,
                  maxLines: 16,
                  enableInteractiveSelection: true,
                  decoration: const InputDecoration(
                      labelText: 'Message Authentication Code (MAC)'),
                ),
                const SizedBox(height: 10),
                if (_error != null) Text(_error.toString()),
              ],
            ),
          ),
        ),
      ),
    );
  }

  Future<void> _encrypt() async {
    try {
      final cipher = _cipher;
      final secretBox = await cipher.encrypt(
        _clearText,
        secretKey: SecretKeyData(
          _fromHex(_secretKeyController.text),
        ),
        nonce: _fromHex(_nonceController.text),
      );
      _cipherTextController.text = _toHex(secretBox.cipherText);
      _macController.text = _toHex(secretBox.mac.bytes);
      setState(() {
        _error = null;
      });
    } catch (error, stackTrace) {
      setState(() {
        _error = '$error\n\n$stackTrace';
        _cipherTextController.text = '';
        _macController.text = '';
      });
      return;
    }
  }

  String _toHex(List<int> bytes) {
    return bytes.map((e) => e.toRadixString(16).padLeft(2, '0')).join(' ');
  }

  List<int> _fromHex(String s) {
    s = s.replaceAll(' ', '').replaceAll('\n', '');
    return List<int>.generate(s.length ~/ 2, (i) {
      var byteInHex = s.substring(2 * i, 2 * i + 2);
      if (byteInHex.startsWith('0')) {
        byteInHex = byteInHex.substring(1);
      }
      final result = int.tryParse(byteInHex, radix: 16);
      if (result == null) {
        throw StateError('Not valid hexadecimal bytes: $s');
      }
      return result;
    });
  }
}

此代码片段展示了如何创建一个简单的Flutter应用程序界面,用户可以选择不同的加密算法并输入密钥、随机数(nonce),然后输入明文进行加密。加密后的密文和MAC会被显示出来。同时提供了生成随机密钥和随机数的功能按钮。


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

1 回复

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


当然,以下是如何在Flutter项目中使用cryptography_flutter插件进行加密和解密的示例代码。这个插件提供了对称加密(如AES)和非对称加密(如RSA)的功能。

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

dependencies:
  flutter:
    sdk: flutter
  cryptography_flutter: ^x.y.z  # 请替换为最新版本号

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

以下是一个简单的示例,展示如何使用AES进行加密和解密:

import 'package:flutter/material.dart';
import 'package:cryptography_flutter/cryptography_flutter.dart';
import 'dart:typed_data';
import 'dart:convert';

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

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

class EncryptDecryptDemo extends StatefulWidget {
  @override
  _EncryptDecryptDemoState createState() => _EncryptDecryptDemoState();
}

class _EncryptDecryptDemoState extends State<EncryptDecryptDemo> {
  final TextEditingController _textController = TextEditingController();
  late Uint8List _key;
  late Uint8List _nonce;
  String? _encryptedText;
  String? _decryptedText;

  @override
  void initState() {
    super.initState();
    // 初始化密钥和nonce(nonce在AES-GCM模式下需要)
    _key = Uint8List.fromList(List.generate(32, (i) => (i % 256).toByte()));
    _nonce = Uint8List.fromList(List.generate(12, (i) => (i % 256).toByte()));
  }

  void _encrypt() async {
    String? text = _textController.text;
    if (text == null || text.isEmpty) return;

    Uint8List plaintext = Uint8List.fromList(text.codeUnits);
    AesGcm aesGcm = AesGcm(_key);
    Ciphertext ciphertext = await aesGcm.encrypt(plaintext, _nonce);

    setState(() {
      _encryptedText = base64.encode(ciphertext.bytes);
      _decryptedText = null;
    });
  }

  void _decrypt() async {
    if (_encryptedText == null) return;

    Uint8List encryptedBytes = base64.decode(_encryptedText!);
    Ciphertext ciphertext = Ciphertext.fromBytes(encryptedBytes);
    AesGcm aesGcm = AesGcm(_key);
    Uint8List plaintext = await aesGcm.decrypt(ciphertext, _nonce);

    setState(() {
      _decryptedText = String.fromCharCodes(plaintext);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        TextField(
          controller: _textController,
          decoration: InputDecoration(labelText: 'Enter text to encrypt'),
          maxLines: 5,
        ),
        SizedBox(height: 20),
        ElevatedButton(
          onPressed: _encrypt,
          child: Text('Encrypt'),
        ),
        SizedBox(height: 20),
        if (_encryptedText != null)
          Text(
            'Encrypted Text (Base64): $_encryptedText',
            style: TextStyle(color: Colors.grey),
          ),
        SizedBox(height: 20),
        ElevatedButton(
          onPressed: _decrypt,
          child: Text('Decrypt'),
        ),
        SizedBox(height: 20),
        if (_decryptedText != null)
          Text(
            'Decrypted Text: $_decryptedText',
            style: TextStyle(color: Colors.green),
          ),
      ],
    );
  }
}

在这个示例中:

  1. 我们初始化了一个AES密钥(32字节)和一个nonce(12字节),这些是在AES-GCM模式下加密必需的。
  2. _encrypt函数将用户输入的文本加密,并将结果以Base64编码的形式显示出来。
  3. _decrypt函数将Base64编码的加密文本解密,并显示解密后的文本。

注意:

  • 在实际应用中,密钥和nonce的管理非常重要,不要将它们硬编码在代码中。
  • 使用AES-GCM模式时,nonce必须是唯一的,并且不能重复使用,以确保加密的安全性。

希望这个示例能够帮助你理解如何在Flutter项目中使用cryptography_flutter插件进行加密和解密。

回到顶部