如何在 Flutter/Dart 中手动验证 x509 证书上的签名

How to manually verify signatures on x509 certificates in Flutter/Dart

我有两个 .pem 格式的 X509Certificate 对象 x1(root) 和 x2。

我想验证 x2 是否由 x1 手动签名。

如何在 Flutter/Dart 中做到这一点?

我找到了这个 article,但是那里的所有操作都是在终端中用 openssl 完成的,在某些阶段需要将 .pem 转换为 .der 以进行提取来自证书的签名。

我也知道 C# 有这样的 method 用于验证,我在 flutter/dart

中找不到任何模拟

我正在使用来自 pub.dev 的 x509b 包来执行此操作。原来的 x509 包似乎不再更新,使用的是过时版本的 asn1lib。在我的版本中,我同时使用 x509 和 asn1lib 本身。很确定有 cleaner/more 高效的方法,但这对我有用。

我还直接使用从我的根证书中提取的 public 密钥,而不是使用整个证书,但我相信您也可以使用这些库执行该步骤。

x509 库中的 tbsCertificate 属性 似乎有误,因此我使用 ASN1Parser 手动提取了 TBS 证书部分。

decodePEM 函数取自 asn1lib 存储库中的示例。

import 'dart:convert';
import 'dart:typed_data';
import 'package:x509b/x509.dart' as x509;
import 'package:asn1lib/asn1lib.dart';

bool verifyCertificate() {
    var strX1PublicKeyInfo = "-----BEGIN PUBLIC KEY-----\nSOME PUBLIC KEY\n-----END PUBLIC KEY-----";
    var strX2Certificate = "-----BEGIN CERTIFICATE-----\nSOME CERTIFICATE\n-----END CERTIFICATE-----";

    var x1PublicKey = (x509.parsePem(strX1PublicKeyInfo).single as x509.SubjectPublicKeyInfo).subjectPublicKey as x509.RsaPublicKey;
    var x2Certificate = x509.parsePem(strX2Certificate).single as x509.X509Certificate;
    var x2CertificateDER = decodePEM(strX2Certificate);

    var asn1Parser = ASN1Parser(x2CertificateDER);
    var seq = asn1Parser.nextObject() as ASN1Sequence;
    var tbsSequence = seq.elements[0] as ASN1Sequence;

    var signature = x509.Signature(Uint8List.fromList(x2Certificate.signatureValue!));
    var verifier = x1PublicKey.createVerifier(x509.algorithms.signing.rsa.sha256);

    return verifier.verify(tbsSequence.encodedBytes, signature)
}

Uint8List decodePEM(pem) {
  var startsWith = [
    '-----BEGIN PUBLIC KEY-----',
    '-----BEGIN PRIVATE KEY-----',
    '-----BEGIN CERTIFICATE-----',
  ];
  var endsWith = [
    '-----END PUBLIC KEY-----',
    '-----END PRIVATE KEY-----',
    '-----END CERTIFICATE-----'
  ];

  //HACK
  for (var s in startsWith) {
    if (pem.startsWith(s)) pem = pem.substring(s.length);
  }

  for (var s in endsWith) {
    if (pem.endsWith(s)) pem = pem.substring(0, pem.length - s.length);
  }

  //Dart base64 decoder does not support line breaks
  pem = pem.replaceAll('\n', '');
  pem = pem.replaceAll('\r', '');
  return Uint8List.fromList(base64.decode(pem));
}