Dpkcs11: Dart PKCS11 Wrapper

The Dpkcs11 library provides a Dart FFI wrapper for the PKCS11 (Cryptographic Token Interface) standard. This library allows Dart applications to interface with PKCS11-compatible hardware security modules (HSMs) and smart cards for cryptographic operations like hashing, signing, and verification.

Features

  • **PKCS11 Standard Support:**Access to core PKCS11 functions: signing, verification and hashing/digest operations.
  • Dart FFI Integration: Seamless integration with native PKCS11 libraries (e.g., dynamic link libraries on Windows, shared objects on Linux/macOS).
  • Secure Operations: Enables applications to utilize the security features of hardware tokens for sensitive cryptographic tasks.

Getting Started

Prerequisites

To use this library, you must have:

  1. A PKCS11 compatible library (e.g., libpkcs11.so, pkcs11.dll) installed on your system.

Installation

Add dpkcs11 to your project's pubspec.yaml file:

dependencies:
  dpkcs11: ^1.0.0 # Use the latest version

Then, run:

dart pub get

Initialization

You need to initialize the library by pointing it to the native PKCS11 implementation file.

import 'package:dpkcs11/dpkcs11.dart';

// Replace 'path/to/your/pkcs11/library' with the actual path
  String libraryPath = 'path/to/your/pkcs11/library';

  Dpkcs11 dpkcs11 = Dpkcs11(libraryPath: libraryPath);
  
  //initialize, lists available slots
  dpkcs11.init();
  
  try {
    PKCS11Slot slot = dpkcs11.selectFirstSlot(); // get a slot;
    PKCS11Session session = slot.openSession(); // open slot to the session
    try {
      var pin = '123456';
      session.login(pin);
      try {
      //some actions here
      } finally {
        session.logout(); 
      }
    } finally {
      session.close();
    }
  } finally {
     dpkcs11.close();
  }

⚠️ Critical Resource Management Note: PKCS11 resources (sessions and library connections) must be released. Always pair init() and openSession() calls with close() and session.close() in a finally block to prevent resource leaks and potential security issues.

Key Type Support: RSA and ECDSA

The Dpkcs11 library simplifies the signing process by automatically selecting the correct PKCS11 signing mechanism based on the properties of the retrieved key object from the HSM/Smartcard.

Specifically:

  • No Manual Mechanism Selection: You do not need to manually specify the low-level PKCS11 mechanisms (e.g., CKM_RSA_PKCS or CKM_ECDSA) when calling keyPair.sign().
  • Automatic Determination: The library examines the Key Type (e.g., CKO_RSA_PUBLIC_KEY, CKO_ECDSA_PUBLIC_KEY) and the relevant Digest Algorithm used (if the data is hashed internally by the token) to determine the necessary mechanism for the underlying PKCS11 call.

This design ensures that signing works seamlessly for both RSA and ECDSA key types stored on the hardware token, provided the key attributes allow the signature operation (CKA_SIGN is set to TRUE).

Examples

Signing

  var datatosign = "Hello world";

  //Keys are searched based on their PKCS11 Object Attributes 
  //(e.g., CKA_SIGN, CKA_VERIFY) stored on the token.
  PKCS11Keypair? kp = session.getKeyPairByAttribute(
          PKCS11Attribute.SIGN,
          true,
  );

 if (kp == null) {
      throw Exception("No key found for signing");
  }

  List<int> signature = kp.sign(datatosign);
  print("Signature: $signature"); 

Verification

  var datatosign = "Hello world";
  List<int> = signature = [];

  //get a keypair with signing capability
  PKCS11Keypair? kp = session.getKeyPairByAttribute(
          PKCS11Attribute.VERIFY,
          true,
  );

 if (kp == null) {
      throw Exception("No key found for verify");
  }

  bool signatureResult = kp.verify(datatosign, signature);
  print("Verify signature: $signatureResult");

Digest

  List<int> hash = session.digest(DigestMethod.SHA_1, "welcome1");
  print("Digest: $hash");

🔑 Supported Digest Methods

The Dpkcs11 library supports the following common cryptographic hash algorithms, ensuring standard PKCS11 compatibility for digest operations:

  • MD5
  • SHA_1
  • SHA224
  • SHA256
  • SHA384
  • SHA512

🧩 Advanced Usage: Integrating with XML Digital Signatures

While Dpkcs11 focuses on direct PKCS11 interactions (signing, verification), it can be seamlessly used with other Dart libraries to perform XML Digital Signatures (XMLDSig).

A great library for this purpose is xml_crypto, which handles the complexities of XML canonicalization and signature structure. You can find the package details on pub.flutter-io.cn.

PKCS11 devices are commonly used to store private keys for securing XML documents, especially in regulated environments.

How it Works:

  1. Key Access: Dpkcs11 is used to securely access the private key from the HSM/Token (as shown in the Signing example).
  2. Signing Engine: The xml_crypto library handles the complex process of:
    • Canonicalizing the XML data.
    • Calculating the digest of the data.
    • Generating the full XML signature structure (e.g., <Signature>, <SignedInfo>).
  3. Signature Generation: The xml_crypto library passes the final hash/data to be signed to the Dpkcs11 key object (kp.sign(data)), which then returns the hardware-generated signature bytes.

This integration allows Dart applications to leverage the hardware-level security of your PKCS11 device for creating legally compliant and verifiable XML signatures.

💡 XML Signature Example

This pseudo-code demonstrates how to connect the signing capability of a Dpkcs11 key with a dedicated XML signature library like xml_crypto.

import 'package:dpkcs11/dpkcs11.dart';
import 'package:xml_crypto/xml_crypto.dart'; // Assume this is the XML signing library

// 1. Define the Dpkcs11 Signing Handler class
// This class acts as the bridge for xml_crypto
class MyPKCS11Signer implements SignatureAlgorithm {
  
  final String algorithmUri;
  final PKCS11Keypair pkcs11Keypair; 

  MyPKCS11Signer({required pkcs11Keypair, required this.algorithmUri});

  @override
  String get algorithmName => algorithmUri;

  @override
  String getSignature(String xml,Uint8List signingKey, [CalculateSignatureCallback? callback]) {        
    return base64.encode(pkcs11Keypair.sign(xml));
  }
}

// 3. Integrate the Handler with xml_crypto (Conceptual)
void performXmlSignature() {
    String xml = '<Document id="data">...</Document>';

    String signAlgo = "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha384"; //ecdsa-sha384

    var config = {
      "signatureAlgorithm": signAlgo, 
      "canonicalizationAlgorithm": "http://www.w3.org/2001/10/xml-exc-c14n#", // exclusive
      "idAttribute": ["Id", "id"],
    };

// 2. Dpkcs11 Initialization and Key Retrieval
// ... (Initialization, Session opening, Login, etc.) ...
  // get pkcs11Keypair from session.

   //bind PKCS11Signer HERE
   SignedXml.signatureAlgorithms[signAlgo] = MyPKCS11Signer(pkcs11Keypair: kp ,algorithmUri: signAlgo);
      
    final sig = SignedXml("",config)
      ..addReference(/*...*/)
      ..addReference(/*...*/)
      ..signingKey = Uint8List(0)  // just pass an empty list
      ..computeSignature(xml.trim());

    return sig.signedXml;
}

// ... (Logout, Close Session, Close Dpkcs11, etc. in finally blocks) ...

Contributing

Contributions are welcome! Please check the issue tracker for open tasks or submit a pull request with your improvements.

License

This library is distributed under the BSD 3-Clause License. See the LICENSE file for more information.

Libraries

dpkcs11