Flutter加密解密插件sodium的使用

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

Flutter加密解密插件sodium的使用

sodium 是一个为Dart提供的libsodium绑定库,支持VM和JS环境,无需Flutter依赖。它提供了简单的API来访问libsodium的主要功能,并尽可能地与原始C API保持一致。本文将详细介绍如何在Flutter项目中使用sodium进行加密解密操作。

目录

特性

sodium 提供了以下特性:

  • 简单易用的Dart API,适用于VM和JS环境。
  • 提供所有主要的libsodium API(详见API状态)。
  • 支持本地API以实现更紧密的集成。

API状态

libsodium API VM JS Sumo 文档
padding ✔️ ✔️ 链接
memory ✔️ 链接
randombytes ✔️ ✔️ 链接
crypto_secretbox ✔️ ✔️ 链接

安装

pubspec.yaml 文件中添加 sodium 依赖并运行 flutter pub get

dependencies:
  sodium: ^latest_version

对于Flutter用户,建议使用 sodium_libs,它可以为每个Flutter平台提供嵌入式的二进制文件。

使用

Sodium vs SodiumSumo

如果你只使用原生版本(即你的应用程序不会被转译成JS),你可以总是使用sumo变体。如果你想同时支持原生和Web,则需要检查你需要的API是否在标准版或sumo版中可用。

加载libsodium

VM - 加载动态库

在Dart VM中,使用 dart:ffi 加载libsodium动态库:

import 'dart:ffi';
import 'package:sodium/sodium.dart';

DynamicLibrary loadLibsodium() {
  return DynamicLibrary.open('/path/to/libsodium.XXX'); // 或者 DynamicLibrary.process()
}

final sodium = await SodiumInit.init2(loadLibsodium);

转译JavaScript - 加载JavaScript代码

对于浏览器环境,可以使用 sodium.js

import 'package:sodium/sodium.dart';

dynamic loadSodiumJS() {
  return ...; // 例如通过<script>标签加载
}

final sodium = await SodiumInit.init2(loadSodiumJS);
通过Dart加载sodium.js到浏览器
@JS()
library interop;

import 'package:js/js.dart';
import 'package:sodium/sodium.dart';

@JS()
@anonymous
class SodiumBrowserInit {
  external void Function(dynamic sodium) get onload;
  external factory SodiumBrowserInit({void Function(dynamic sodium) onload});
}

Future<dynamic> loadSodiumInBrowser() async {
  final completer = Completer<dynamic>();

  setProperty(window, 'sodium', SodiumBrowserInit(
    onload: allowInterop(completer.complete),
  ));

  final script = ScriptElement();
  script
    ..type = 'text/javascript'
    ..async = true
    ..src = 'sodium.js'; // 使用你服务器上的路径
  document.head!.append(script);

  return completer.future;
}

final sodium = await SodiumInit.init2(loadSodiumInBrowser);

使用API

获取 Sodium 实例后,使用API非常简单。以下是加密示例:

final sodium = // 加载libsodium

final String message = 'my very secret message';
final Uint8List messageBytes = Uint8List.fromList(message.codeUnits);

final nonce = sodium.randombytes.buf(sodium.crypto.secretBox.nonceBytes);
final SecureKey key = sodium.crypto.secretBox.keygen();

final encryptedData = sodium.crypto.secretBox.easy(
  message: messageBytes,
  nonce: nonce,
  key: key,
);

print(encryptedData);

key.dispose();

在单独的隔离区运行计算

对于耗时较长的操作(如密码哈希),可以在隔离区中运行以避免阻塞UI:

final subkeyId = BigInt.from(42);
final masterKey = sodium.crypto.kdf.keygen();
final derivedKey = await sodium.runIsolated(
  secureKeys: [masterKey],
  (sodium, secureKeys, keyPairs) {
    final [masterKey] = secureKeys;
    return sodium.crypto.kdf.deriveFromKey(
      masterKey: masterKey,
      context: 'computed',
      subkeyId: subkeyId,
      subkeyLen: 64,
    );
  }
);

使用自定义隔离区

对于更复杂的场景,可以使用低级隔离区API:

Future<SecureKey> deriveKey() {
  final subkeyId = BigInt.from(42);
  final masterKey = sodium.crypto.kdf.keygen();

  final sodiumFactory = sodium.isolateFactory;
  final transferrableMasterKey = sodium.createTransferrableSecureKey(masterKey);

  final result = compute(_deriveKey, (sodiumFactory, transferrableMasterKey, subkeyId));

  return sodium.materializeTransferrableSecureKey(result);
}

Future<TransferrableSecureKey> _deriveKey((SodiumFactory, TransferrableSecureKey, BigInt) message) async {
  final (sodiumFactory, transferrableMasterKey, subkeyId) = message;
  final sodium = await sodiumFactory();
  final masterKey = sodium.materializeTransferrableSecureKey(transferrableMasterKey);
  final derivedKey = sodium.crypto.kdf.deriveFromKey(
    masterKey: masterKey,
    context: 'computed',
    subkeyId: subkeyId,
    subkeyLen: 64,
  );
  return sodium.createTransferrableSecureKey(derivedKey);
}

文档

更多详细信息请参考 官方文档示例代码

希望以上内容能帮助你在Flutter项目中顺利使用sodium进行加密解密操作!如果有任何问题,请随时提问。


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

1 回复

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


当然,下面是一个关于如何在Flutter项目中使用sodium插件进行加密和解密的示例代码。sodium是基于libsodium库的加密库,提供了多种加密、解密、签名等功能。在Flutter中,你可以通过sodium插件来使用这些功能。

首先,确保你已经在你的pubspec.yaml文件中添加了sodium插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  sodium: ^x.y.z  # 请使用最新版本号

然后,运行flutter pub get来获取依赖。

以下是一个完整的示例代码,展示了如何使用sodium插件进行对称加密和解密:

import 'package:flutter/material.dart';
import 'package:sodium/sodium.dart';

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

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

class EncryptDecryptExample extends StatefulWidget {
  @override
  _EncryptDecryptExampleState createState() => _EncryptDecryptExampleState();
}

class _EncryptDecryptExampleState extends State<EncryptDecryptExample> {
  String? plainText;
  String? encryptedText;
  String? decryptedText;

  @override
  void initState() {
    super.initState();
    // 初始化Sodium库
    Sodium.init().then((_) {
      setState(() {
        // 示例明文
        plainText = "Hello, Flutter!";
        
        // 生成随机密钥
        var key = Sodium.randombytes_buf(Sodium.crypto_secretbox_KEYBYTES);
        
        // 加密明文
        var nonce = Sodium.randombytes_buf(Sodium.crypto_secretbox_NONCEBYTES);
        var cipherText = Sodium.crypto_secretbox_easy(
          plainText!.codeUnits,
          nonce,
          key
        );
        
        // 将加密后的数据转换为Base64字符串以便显示
        encryptedText = Sodium.bin_to_hex(cipherText);
        
        // 解密数据
        var decryptedBytes = Sodium.crypto_secretbox_open_easy(
          cipherText,
          nonce,
          key
        );
        
        // 将解密后的数据转换为字符串
        decryptedText = String.fromCharCodes(decryptedBytes!);
        
        // 更新UI
        setState(() {});
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Text("Plain Text: ${plainText ?? 'Loading...'}"),
        SizedBox(height: 20),
        Text("Encrypted Text: ${encryptedText ?? 'Loading...'}"),
        SizedBox(height: 20),
        Text("Decrypted Text: ${decryptedText ?? 'Loading...'}"),
      ],
    );
  }
}

解释

  1. 初始化Sodium库: 在initState方法中,使用Sodium.init()来初始化库。这是一个异步操作,因此使用then来处理完成后的逻辑。

  2. 生成密钥和随机数: 使用Sodium.randombytes_buf生成密钥和随机数(nonce)。密钥用于加密和解密,随机数用于确保每次加密的结果都不同,即使明文和密钥相同。

  3. 加密: 使用Sodium.crypto_secretbox_easy方法进行加密。该方法接受明文、随机数和密钥,返回加密后的数据。

  4. 解密: 使用Sodium.crypto_secretbox_open_easy方法进行解密。该方法接受加密后的数据、随机数和密钥,返回解密后的明文。

  5. 显示结果: 将明文、加密后的数据(转换为Base64字符串以便显示)和解密后的数据显示在UI上。

注意事项

  • 在实际使用中,请确保密钥和随机数的安全存储和传输。
  • 加密和解密操作通常应该在后台线程中进行,以避免阻塞UI线程。
  • 示例代码中的加密和解密操作是同步的,但在实际应用中,你可能需要使用异步操作来处理较大的数据或进行更复杂的操作。

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

回到顶部