Flutter非对称加密算法插件asymmetric_crypto_primitives的使用

Flutter非对称加密算法插件asymmetric_crypto_primitives的使用

特性

  • 为Android、iOS和MacOS提供了EDDSA加密原语,因为这些平台没有原生支持。
  • 将来会支持在设备上未有原生支持的新一代非对称加密算法。
  • 允许预旋转密钥,因为它默认生成两组密钥对。

开始使用

Android

生成密钥和数据签名需要设备屏幕锁定已启用。可以通过checkIfDeviceSecure方法轻松检查:

isDeviceSecure = await AsymmetricCryptoPrimitives.checkIfDeviceSecure(); // 返回true如果屏幕锁定已设置

不检查屏幕锁定是否设置而直接使用RSA算法可能会导致抛出DeviceNotSecuredException异常。

要开始使用插件,需要初始化signer对象以用于Ed25519或RSA方法:

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  var isDeviceSecure = await AsymmetricCryptoPrimitives.checkIfDeviceSecure();
  if (isDeviceSecure) {
    var signer = await AsymmetricCryptoPrimitives.establishForRSA();
    runApp(MyApp(signer: signer));
  }
}

大多数插件方法都可以通过signer对象访问。

Android版本特性:

  • RSA密钥由KeyStore生成并存储。
  • Ed25519密钥使用Libsodium生成并存储在SharedPreferences中。
  • 存储在SharedPreferences中的密钥使用AES加密。
  • AES密钥由KeyStore生成并存储。
  • 签名消息受本地身份验证保护。
  • writeData()方法将加密后的数据存储在SharedPreferences中。
  • 可以在不进行本地身份验证的情况下签名消息。
iOS

当前仅支持Ed25519算法。RSA可用,但工作仍在进行中,某些功能可能无法正常工作。

默认认证方式是PIN。为了激活FaceID,需要编辑你的app的Info.plist文件并添加以下行:

<key>NSFaceIDUsageDescription</key>
<string>iOS</string>

其余设置与Android类似。

iOS版本特性:

  • Ed25519密钥使用Libsodium生成并存储在NSUserDefaults中。
  • 存储在NSUserDefaults中的密钥使用EC密钥加密。
  • EC密钥由Secure Enclave生成并存储。
  • 签名消息受本地身份验证保护。
  • writeData()方法将加密后的数据存储在NSUserDefaults中。
MacOS

当前仅支持Ed25519算法。RSA尚未可用。由于数据签名受用户密码保护,插件会检查设备上是否可以进行身份验证。因此,如Android一样,需要使用checkIfDeviceSecure()以避免后续错误。

MacOS版本特性:

  • Ed25519密钥使用Libsodium生成并存储在KeyChain中。
  • 由于KeyChain原生使用AES加密数据,无需其他密钥。
  • 签名消息受本地身份验证保护。
  • writeData()方法将数据存储在NSUserDefaults中,未加密,因为没有密钥存储在其中。
Windows

警告:Windows版本的插件仍在开发中。目前密钥存储在公共目录中且无加密!

当前仅支持Ed25519算法。RSA尚未可用。Sodium功能通过Rust插件实现。

Windows版本特性:

  • Ed25519密钥使用Libsodium生成并通过Rust插件存储在Roaming AppData目录下的passFile.txt文件中。
  • 签名消息受本地身份验证保护(通过插件而非原生支持)。
  • writeData()方法将数据存储在Roaming AppData目录中,未加密。

使用

签名数据
String strToSign = 'Sign me!';
signature = await signer.sign(strToSign);
获取密钥
String currentKey = '';
String nextKey = '';
currentKey = await signer.getCurrentPubKey();
nextKey = await signer.getNextPubKey();
旋转密钥
String currentKey = 'current key here!';
String nextKey = 'next key here!';
await signer.rotateForEd25519();
// 查看旋转结果
currentKey = await signer.getCurrentPubKey();
nextKey = await signer.getNextPubKey();

警告:旋转当前不适用于RSA算法。正在进行中。

获取signer的唯一UUID
String uuid = '';
uuid = await signer.getUuid();
获取之前使用的signer对象
void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  var signer = await AsymmetricCryptoPrimitives.getEd25519SignerFromUuid('ecd886f1-1af6-4e62-a6b2-825e2b15ebd2');
  // 或者 getRSASignerFromUuid()
  runApp(MyApp(signer: signer));
}

此方法如果找不到与输入UUID关联的密钥,将抛出IncorrectUuidException异常。

清理
await AsymmetricCryptoPrimitives.cleanUp(signer);

移除与该signer对象关联的所有密钥。

数据存储函数

写入数据
String _data = 'Data';
String _key = 'Key';
var result = await AsymmetricCryptoPrimitives.writeData(_key, _data); // 如果一切顺利返回true。可能会抛出SharedPreferencesException或DeviceNotSecuredException
读取数据
String _key = 'Key';
var result = await AsymmetricCryptoPrimitives.readData(_key); // 如果一切顺利返回写入的数据。可能会抛出InvalidSignatureException、DeviceNotSecuredException或NoKeyInStorageException
删除数据
String _key = 'Key';
var result = await AsymmetricCryptoPrimitives.deleteData(_key); // 如果一切顺利返回true。可能会抛出SharedPreferencesException或DeviceNotSecuredException
编辑数据
String _data = 'Data';
String _key = 'Key';
var result = await AsymmetricCryptoPrimitives.editData(_key, _data); // 如果一切顺利返回true。可能会抛出SharedPreferencesException或DeviceNotSecuredException

完整示例代码

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

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  var signer = await AsymmetricCryptoPrimitives.establishForEd25519();
  runApp(MyApp(
    signer: signer,
  ));
}

class MyApp extends StatefulWidget {
  final signer;
  const MyApp({Key? key, this.signer}) : super(key: key);

  [@override](/user/override)
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  TextEditingController writeKeyController = TextEditingController();
  TextEditingController writeDataController = TextEditingController();
  TextEditingController readKeyController = TextEditingController();
  TextEditingController deleteKeyController = TextEditingController();
  TextEditingController editKeyController = TextEditingController();
  TextEditingController editDataController = TextEditingController();
  TextEditingController signDataController = TextEditingController();
  String writeResult = '';
  String readResult = '';
  String deleteResult = '';
  String editResult = '';
  String currentKey = '';
  String nextKey = '';
  String signature = '';
  late var signer;

  [@override](/user/override)
  void initState() {
    signer = widget.signer;
    super.initState();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('storage example app'),
        ),
        body: SingleChildScrollView(
          child: Center(
              child: Column(
            children: [
              const Text(
                'Signer uuid:',
                style: TextStyle(fontWeight: FontWeight.bold, fontSize: 25),
              ),
              Text(signer.uuid),
              const Divider(),
              const Text(
                'Current keys:',
                style: TextStyle(fontWeight: FontWeight.bold, fontSize: 25),
              ),
              RawMaterialButton(
                onPressed: () async {
                  currentKey = await signer.getCurrentPubKey();
                  nextKey = await signer.getNextPubKey();
                  setState(() {});
                },
                child: const Text('Get keys!'),
              ),
              Text(currentKey),
              Text(nextKey),
              const Divider(),
              const Text(
                'Rotate keys:',
                style: TextStyle(fontWeight: FontWeight.bold, fontSize: 25),
              ),
              RawMaterialButton(
                onPressed: () async {
                  await signer.rotateForEd25519();
                  currentKey = await signer.getCurrentPubKey();
                  nextKey = await signer.getNextPubKey();
                  setState(() {});
                },
                child: const Text('Rotate!'),
              ),
              const Divider(),
              const Text(
                'Sign data:',
                style: TextStyle(fontWeight: FontWeight.bold, fontSize: 25),
              ),
              TextFormField(
                controller: signDataController,
                decoration: const InputDecoration(hintText: "data"),
              ),
              RawMaterialButton(
                onPressed: () async {
                  signature = await signer.sign(signDataController.text);
                  setState(() {});
                },
                child: const Text('Sign!'),
              ),
              Text(signature),
              const Divider(),
              const Text(
                'Clean up:',
                style: TextStyle(fontWeight: FontWeight.bold, fontSize: 25),
              ),
              RawMaterialButton(
                onPressed: () async {
                  await AsymmetricCryptoPrimitives.cleanUp(signer);
                  setState(() {
                    currentKey = '';
                    nextKey = '';
                    signature = '';
                  });
                },
                child: const Text('Clean up!'),
              ),
              const Divider(
                thickness: 5,
              ),
              const Text(
                '1. Write',
                style: TextStyle(fontWeight: FontWeight.bold, fontSize: 25),
              ),
              TextFormField(
                controller: writeKeyController,
                decoration: const InputDecoration(hintText: "key"),
              ),
              TextFormField(
                controller: writeDataController,
                decoration: const InputDecoration(hintText: "data"),
              ),
              RawMaterialButton(
                onPressed: () async {
                  if (writeDataController.text.isNotEmpty && writeKeyController.text.isNotEmpty) {
                    var result = await AsymmetricCryptoPrimitives.writeData(
                        writeKeyController.text, writeDataController.text);
                    if (result == true) {
                      setState(() {
                        writeResult = 'Success!';
                      });
                    } else {
                      setState(() {
                        writeResult = 'Failure!';
                      });
                    }
                  }
                },
                child: const Text('Write!'),
              ),
              Text(writeResult),
              const Divider(),
              const Text(
                '2. Read',
                style: TextStyle(fontWeight: FontWeight.bold, fontSize: 25),
              ),
              TextFormField(
                controller: readKeyController,
                decoration: const InputDecoration(hintText: "key"),
              ),
              RawMaterialButton(
                onPressed: () async {
                  if (readKeyController.text.isNotEmpty) {
                    var result = await AsymmetricCryptoPrimitives.readData(
                        readKeyController.text);
                    if (result != false) {
                      setState(() {
                        readResult = result;
                      });
                    } else {
                      setState(() {
                        readResult = 'Failure!';
                      });
                    }
                  }
                },
                child: const Text('Read!'),
              ),
              Text(readResult),
              const Divider(),
              const Text(
                '3. Delete',
                style: TextStyle(fontWeight: FontWeight.bold, fontSize: 25),
              ),
              TextFormField(
                controller: deleteKeyController,
                decoration: const InputDecoration(hintText: "key"),
              ),
              RawMaterialButton(
                onPressed: () async {
                  if (deleteKeyController.text.isNotEmpty) {
                    var result = await AsymmetricCryptoPrimitives.deleteData(
                        deleteKeyController.text);
                    if (result == true) {
                      setState(() {
                        deleteResult = "Deleted!";
                      });
                    } else {
                      setState(() {
                        deleteResult = "Failure!";
                      });
                    }
                  }
                },
                child: const Text('Delete!'),
              ),
              Text(deleteResult),
              const Divider(),
              const Text(
                '4. Edit',
                style: TextStyle(fontWeight: FontWeight.bold, fontSize: 25),
              ),
              TextFormField(
                controller: editKeyController,
                decoration: const InputDecoration(hintText: "key"),
              ),
              TextFormField(
                controller: editDataController,
                decoration: const InputDecoration(hintText: "data"),
              ),
              RawMaterialButton(
                onPressed: () async {
                  if (editDataController.text.isNotEmpty && editKeyController.text.isNotEmpty) {
                    var result = await AsymmetricCryptoPrimitives.editData(
                        editKeyController.text, editDataController.text);
                    if (result == true) {
                      setState(() {
                        editResult = 'Success!';
                      });
                    } else {
                      setState(() {
                        editResult = 'Failure!';
                      });
                    }
                  }
                },
                child: const Text('Edit!'),
              ),
              Text(editResult),
              const Divider(),
            ],
          )),
        ),
      ),
    );
  }
}

更多关于Flutter非对称加密算法插件asymmetric_crypto_primitives的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

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


asymmetric_crypto_primitives 是一个 Flutter 插件,用于实现非对称加密算法的基本操作。它支持诸如 RSA、ECC(椭圆曲线加密)等非对称加密算法。使用该插件,你可以在 Flutter 应用中实现加密、解密、签名和验证签名等操作。

安装插件

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

dependencies:
  flutter:
    sdk: flutter
  asymmetric_crypto_primitives: ^0.1.0 # 请使用最新的版本号

然后运行 flutter pub get 来安装插件。

使用插件

1. 生成密钥对

import 'package:asymmetric_crypto_primitives/asymmetric_crypto_primitives.dart';

void generateKeyPair() async {
  final keyPair = await AsymmetricCryptoPrimitives.generateRSAKeyPair();
  print('Public Key: ${keyPair.publicKey}');
  print('Private Key: ${keyPair.privateKey}');
}

2. 加密和解密

void encryptDecrypt() async {
  final keyPair = await AsymmetricCryptoPrimitives.generateRSAKeyPair();
  final publicKey = keyPair.publicKey;
  final privateKey = keyPair.privateKey;

  final plainText = 'Hello, World!';
  final encrypted = await AsymmetricCryptoPrimitives.encryptRSA(publicKey, plainText);
  print('Encrypted: $encrypted');

  final decrypted = await AsymmetricCryptoPrimitives.decryptRSA(privateKey, encrypted);
  print('Decrypted: $decrypted');
}

3. 签名和验证签名

void signVerify() async {
  final keyPair = await AsymmetricCryptoPrimitives.generateRSAKeyPair();
  final privateKey = keyPair.privateKey;
  final publicKey = keyPair.publicKey;

  final message = 'Hello, World!';
  final signature = await AsymmetricCryptoPrimitives.signRSA(privateKey, message);
  print('Signature: $signature');

  final isValid = await AsymmetricCryptoPrimitives.verifyRSA(publicKey, message, signature);
  print('Is Signature Valid: $isValid');
}

常见操作总结

  • 生成密钥对: generateRSAKeyPair
  • 加密: encryptRSA
  • 解密: decryptRSA
  • 签名: signRSA
  • 验证签名: verifyRSA

注意事项

  1. 性能: 非对称加密算法(如 RSA)通常比对称加密算法(如 AES)慢,因此在处理大量数据时,建议结合使用对称和非对称加密。
  2. 密钥管理: 私钥必须妥善保管,避免泄露。
  3. 兼容性: 确保你的应用与不同的加密标准和协议兼容。

示例代码

以下是一个完整的示例,展示了如何使用 asymmetric_crypto_primitives 插件进行密钥生成、加密、解密、签名和验证签名操作。

import 'package:asymmetric_crypto_primitives/asymmetric_crypto_primitives.dart';

void main() async {
  // 生成密钥对
  final keyPair = await AsymmetricCryptoPrimitives.generateRSAKeyPair();
  final publicKey = keyPair.publicKey;
  final privateKey = keyPair.privateKey;

  print('Public Key: $publicKey');
  print('Private Key: $privateKey');

  // 加密和解密
  final plainText = 'Hello, World!';
  final encrypted = await AsymmetricCryptoPrimitives.encryptRSA(publicKey, plainText);
  print('Encrypted: $encrypted');

  final decrypted = await AsymmetricCryptoPrimitives.decryptRSA(privateKey, encrypted);
  print('Decrypted: $decrypted');

  // 签名和验证签名
  final message = 'Hello, World!';
  final signature = await AsymmetricCryptoPrimitives.signRSA(privateKey, message);
  print('Signature: $signature');

  final isValid = await AsymmetricCryptoPrimitives.verifyRSA(publicKey, message, signature);
  print('Is Signature Valid: $isValid');
}
回到顶部