Flutter XML数据加密解密插件xml_crypto的使用

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

Flutter XML数据加密解密插件 xml_crypto 的使用

xml_crypto 是一个用于 Dart 的 XML 数字签名库,它移植自 Node.js 库 xml-crypto。本文将介绍如何在 Flutter 项目中使用该插件进行 XML 数据的加密和解密。

安装

首先,在你的 Flutter 项目的 pubspec.yaml 文件中添加 xml_crypto 依赖:

dependencies:
  xml_crypto: ^latest_version

然后运行以下命令来安装:

dart pub get

支持的算法

规范化和转换算法

  • Canonicalization: http://www.w3.org/TR/2001/REC-xml-c14n-20010315
  • Canonicalization with comments: http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments
  • Exclusive Canonicalization: http://www.w3.org/2001/10/xml-exc-c14n#
  • Exclusive Canonicalization with comments: http://www.w3.org/2001/10/xml-exc-c14n#WithComments
  • Enveloped Signature transform: http://www.w3.org/2000/09/xmldsig#enveloped-signature

哈希算法

  • SHA1 digests: http://www.w3.org/2000/09/xmldsig#sha1
  • SHA256 digests: http://www.w3.org/2001/04/xmlenc#sha256
  • SHA512 digests: http://www.w3.org/2001/04/xmlenc#sha512

签名算法

  • RSA-SHA1: http://www.w3.org/2000/09/xmldsig#rsa-sha1
  • RSA-SHA256: http://www.w3.org/2001/04/xmldsig-more#rsa-sha256
  • RSA-SHA512: http://www.w3.org/2001/04/xmldsig-more#rsa-sha512

HMAC-SHA1 也是可用的,但默认情况下是禁用的:

import 'package:xml_crypto/xml_crypto.dart';

SignedXml.enableHMAC();

启用 HMAC 后会禁用数字签名算法。

签名 XML 文档

签名 XML 文档时,可以指定以下属性来自定义签名过程:

  • sign.signingKey: 必需,包含私钥的 Uint8List
  • sign.keyInfoProvider: 可选,包含证书的 key info provider 实例
  • sign.signatureAlgorithm: 可选,支持的签名算法之一
  • sign.canonicalizationAlgorithm: 可选,支持的规范化算法之一

示例代码:

import 'dart:io';
import 'package:xml_crypto/xml_crypto.dart';

final xml = "<library>"
            "<book>"
              "<name>Harry Potter</name>"
            "</book>"
          "</library>";

final sig = SignedXml()
  ..addReference("//*[local-name()='book']")
  ..signingKey = File("client.pem").readAsBytesSync()
  ..computeSignature(xml);
File("signed.xml").writeAsStringSync(sig.signedXml);

结果将会是一个带有签名的 XML 文档:

<library>
  <book Id="_0">
    <name>Harry Potter</name>
  </book>
  <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
    <SignedInfo>
      <CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
      <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
      <Reference URI="#_0">
        <Transforms>
          <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
        </Transforms>
        <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
        <DigestValue>cdiS43aFDQMnb3X8yaIUej3+z9Q=</DigestValue>
      </Reference>
    </SignedInfo>
    <SignatureValue>vhWzpQyIYuncHUZV9W...[long base64 removed]...</SignatureValue>
  </Signature>
</library>

验证 XML 文档

验证 XML 文档时,必须指定以下属性:

  • sign.keyInfoProvider: 必需,包含证书的 key info provider 实例

示例代码:

import 'dart:io';
import 'package:xml/xml.dart' as xml;
import 'package:xml_crypto/xml_crypto.dart';

final signedXml = File("signed.xml").readAsStringSync();
var doc = xml.XmlDocument.parse(signedXml);

var signature = doc.findAllElements('Signature').first;
final sig = SignedXml()
  ..keyInfoProvider = FileKeyInfo("client_public.pem")
  ..loadSignature(signature);
final res = sig.checkSignature(signedXml);
if (!res) print(sig.validationErrors);

如果验证失败,sig.validationErrors 将包含错误信息。

为了防止某些攻击,我们还需要检查内容是否是我们要验证的内容:

final elem = doc.findAllElements('book').first;
final uri = sig.references.first.uri; // might not be 0 - depending on the document you verify
final id = (uri.startsWith('#')) ? uri.substring(1) : uri;
if (elem.getAttribute('ID') != id && elem.getAttribute('Id') != id && elem.getAttribute('id') != id)
  throw Exception('the interesting element was not the one verified by the signature');

自定义算法

你可以通过实现接口来自定义算法,例如 KeyInfoProvider, HashAlgorithm, SignatureAlgorithm, 和 CanonicalizationAlgorithm

示例:自定义签名算法

class MySignatureAlgorithm implements SignatureAlgorithm {
  @override
  String getSignature(String xml, Uint8List signingKey, [CalculateSignatureCallback? callback]) {
    final rsa = RSAPrivateKey.fromPEM(utf8.decode(signingKey));
    final res = rsa.signSsaPkcs1v15ToBase64(utf8.encode(xml), hasher: EmsaHasher.sha1);
    callback?.call(null, res);
    return '';
  }

  @override
  bool verifySignature(String xml, Uint8List key, String signatureValue, [ValidateSignatureCallback? callback]) => true;

  @override
  String get algorithmName => 'http://mySigningAlgorithm';
}

SignedXml.signatureAlgorithms["http://mySigningAlgorithm"] = MySignatureAlgorithm();

异步签名和验证

如果你需要使用异步回调来进行签名或验证,可以创建自定义签名算法:

class AsyncSignatureAlgorithm implements SignatureAlgorithm {
  @override
  String getSignature(String xml, Uint8List signingKey, [CalculateSignatureCallback? callback]) {
    final rsa = RSAPrivateKey.fromPEM(utf8.decode(signingKey));
    final res = rsa.signSsaPkcs1v15ToBase64(utf8.encode(xml), hasher: EmsaHasher.sha1);
    callback?.call(null, res);
    return '';
  }

  @override
  bool verifySignature(String xml, Uint8List key, String signatureValue, [ValidateSignatureCallback? callback]) => true;

  @override
  String get algorithmName => 'http://www.w3.org/2000/09/xmldsig#rsa-sha1';
}

SignedXml.signatureAlgorithms["http://asyncSignatureAlgorithm"] = AsyncSignatureAlgorithm();
final sig = SignedXml();
sig.signatureAlgorithm = "http://asyncSignatureAlgorithm";
sig.computeSignature(xml, opts: opts, callback: (err, _) {
  final signedResponse = sig.signedXml;
});

完整示例 Demo

以下是一个完整的示例,展示了如何签名和验证 XML 文档:

import 'dart:io';
import 'package:xml/xml.dart' as xml;
import 'package:xml_crypto/xml_crypto.dart';

void main() {
  final xmlContent = "<library>"
      "<book>"
      "<name>Harry Potter</name>"
      "</book>"
      "</library>";

  // 签名 XML 文档
  signXml(xmlContent, "//*[local-name()='book']", 'client.pem', 'result.xml');

  print('XML 已成功签名');

  final signedXmlContent = File('result.xml').readAsStringSync();
  print('正在验证签名...');

  // 验证 XML 文档
  if (validateXml(signedXmlContent, 'client_public.pem')) {
    print('签名有效');
  } else {
    print('签名无效');
  }
}

void signXml(String xml, String xpath, String keyPath, String destPath) {
  final sig = SignedXml()
    ..signingKey = File(keyPath).readAsBytesSync()
    ..addReference(xpath)
    ..computeSignature(xml);
  File(destPath).writeAsStringSync(sig.signedXml);
}

bool validateXml(String xml, String publicKeyPath) {
  final doc = xml.XmlDocument.parse(xml);
  final signature = doc.findAllElements('Signature').first;
  final sig = SignedXml()
    ..keyInfoProvider = FileKeyInfo(publicKeyPath)
    ..loadSignature(signature);
  final res = sig.checkSignature(xml);
  if (!res) print(sig.validationErrors);
  return res;
}

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

1 回复

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


当然,我可以为你提供一个关于如何在Flutter中使用xml_crypto插件进行XML数据加密和解密的代码示例。xml_crypto插件通常用于处理XML数字签名和加密。以下是一个简单的示例,展示如何使用这个插件对XML数据进行加密和解密。

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

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

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

接下来,我们编写一个Flutter应用,展示如何使用xml_crypto进行XML加密和解密。

import 'package:flutter/material.dart';
import 'package:xml/xml.dart' as xml;
import 'package:xml_crypto/xml_crypto.dart';
import 'dart:convert';

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

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  String encryptedXml = '';
  String decryptedXml = '';

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('XML Crypto Example'),
        ),
        body: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Text('Original XML:'),
              SizedBox(height: 10),
              Text('<?xml version="1.0" encoding="UTF-8"?><data>Hello, World!</data>',
                  style: TextStyle(fontSize: 16)),
              SizedBox(height: 20),
              ElevatedButton(
                onPressed: () {
                  encryptXml();
                },
                child: Text('Encrypt XML'),
              ),
              SizedBox(height: 20),
              if (encryptedXml.isNotEmpty)
                Text('Encrypted XML:', style: TextStyle(fontSize: 16)),
              if (encryptedXml.isNotEmpty)
                SizedBox(height: 10),
              if (encryptedXml.isNotEmpty)
                Text(encryptedXml, style: TextStyle(fontSize: 14)),
              SizedBox(height: 20),
              ElevatedButton(
                onPressed: () {
                  decryptXml();
                },
                child: Text('Decrypt XML'),
                enabled: encryptedXml.isNotEmpty,
              ),
              SizedBox(height: 20),
              if (decryptedXml.isNotEmpty)
                Text('Decrypted XML:', style: TextStyle(fontSize: 16)),
              if (decryptedXml.isNotEmpty)
                SizedBox(height: 10),
              if (decryptedXml.isNotEmpty)
                Text(decryptedXml, style: TextStyle(fontSize: 14)),
            ],
          ),
        ),
      ),
    );
  }

  void encryptXml() {
    String publicKeyPem = '''
      -----BEGIN PUBLIC KEY-----
      ... (你的公钥内容) ...
      -----END PUBLIC KEY-----
    ''';

    String privateKeyPem = '''
      -----BEGIN PRIVATE KEY-----
      ... (你的私钥内容) ...
      -----END PRIVATE KEY-----
    ''';

    xml.XmlDocument xmlDoc = xml.parse('<?xml version="1.0" encoding="UTF-8"?><data>Hello, World!</data>');
    XmlEncryption xmlEncryption = XmlEncryption(xmlDoc.outerXml);
    String encryptedData = xmlEncryption.encrypt(publicKeyPem, privateKeyPem);

    setState(() {
      encryptedXml = encryptedData;
      decryptedXml = '';
    });
  }

  void decryptXml() {
    String privateKeyPem = '''
      -----BEGIN PRIVATE KEY-----
      ... (你的私钥内容) ...
      -----END PRIVATE KEY-----
    ''';

    XmlEncryption xmlEncryption = XmlEncryption(encryptedXml);
    String decryptedData = xmlEncryption.decrypt(privateKeyPem);

    setState(() {
      encryptedXml = ''; // 可选:清空加密后的XML内容
      decryptedXml = decryptedData;
    });
  }
}

class XmlEncryption {
  String xmlData;

  XmlEncryption(this.xmlData);

  String encrypt(String publicKeyPem, String privateKeyPem) {
    // 注意:这里需要实际的加密逻辑,使用xml_crypto或其他库实现
    // 由于xml_crypto库主要处理签名,加密部分可能需要自定义或使用其他库
    // 以下仅为示例,实际加密过程需要更复杂的实现
    // 假设加密过程返回base64编码的加密字符串
    String encrypted = base64Encode(utf8.encode(xmlData)); // 仅为示例,不是实际加密
    return encrypted;
  }

  String decrypt(String privateKeyPem) {
    // 注意:这里需要实际的解密逻辑,使用xml_crypto或其他库实现
    // 由于xml_crypto库主要处理签名,解密部分可能需要自定义或使用其他库
    // 以下仅为示例,实际解密过程需要更复杂的实现
    // 假设解密过程将base64编码的字符串解码回原始XML
    String decrypted = utf8.decode(base64Decode(encryptedXml)); // 仅为示例,不是实际解密
    return decrypted;
  }
}

注意

  1. xml_crypto库主要用于处理XML数字签名,而不是加密和解密。加密和解密通常需要其他库,如pointycastle
  2. 上述代码中的encryptdecrypt方法仅为示例,并没有实际执行加密和解密操作。你需要使用适当的库和方法来实现这些功能。
  3. 替换publicKeyPemprivateKeyPem字符串为你自己的公钥和私钥内容。
  4. 示例中的XmlEncryption类是为了演示目的而创建的,并不是xml_crypto库的一部分。实际使用时,你需要根据所选的加密库来实现加密和解密逻辑。

希望这个示例能帮助你理解如何在Flutter中使用XML加密和解密的基本流程。如果你有具体的加密和解密需求,可能需要进一步查阅相关库和文档。

回到顶部