felectronic_certificates

Flutter plugin for managing device-stored certificates. Import, sign with, list, and delete PKCS#12 certificates using the Android KeyStore or iOS Keychain.

style: very good analysis License: MIT

Features

Feature Description
Import Import PKCS#12 (.p12/.pfx) files into the device keystore
Sign Sign arbitrary data with a certificate's private key
List Retrieve all installed certificates with metadata
Select Open a native certificate picker or set default by serial
Delete Remove certificates by serial number or default selection
Session Builder pattern for repeated operations on one certificate
Extensions Expiry checks, usage labels, display helpers

Platform Support

Android iOS
API 28+ iOS 13+

Installation

dependencies:
  felectronic_certificates: ^1.0.0

Android Setup

This plugin depends on a bundled AAR (certificatesigner-release.aar) for certificate operations. Add the certificate-signer module to your app's settings.gradle.kts:

// android/settings.gradle.kts
include(":certificate-signer")
project(":certificate-signer").projectDir =
    file("../path-to/felectronic_certificates_android/android/certificate-signer")

The plugin's build.gradle.kts will automatically link the AAR when the :certificate-signer project is found.

iOS Setup

No additional setup required. The plugin uses the iOS Keychain APIs directly.

Usage

import 'package:felectronic_certificates/felectronic_certificates.dart';

Import a PKCS#12 Certificate

import 'dart:io';

final bytes = await File('certificate.p12').readAsBytes();
await importCertificate(
  bytes,
  password: 'cert-password',
  alias: 'My Work Certificate',
);

List All Certificates

final certs = await getAllCertificates();
for (final cert in certs) {
  print('${cert.displayName} - ${cert.expiryStatus}');
  print('  Serial: ${cert.serialNumber}');
  print('  Issuer: ${cert.issuerName}');
  print('  Usages: ${cert.usageSummary}');
  print('  Expired: ${cert.isExpired}');
}

Select a Default Certificate

// Open the native picker
final selected = await selectDefaultCertificate();
if (selected != null) {
  print('Selected: ${selected.holderName}');
}

// Or set by serial number directly
await setDefaultCertificateBySerialNumber('AB12CD34');

// Get the current default
final current = await getDefaultCertificate();

// Clear the default selection
await clearDefaultCertificate();

Sign Data

import 'dart:convert';
import 'dart:typed_data';

final data = Uint8List.fromList(utf8.encode('Data to sign'));

// Sign with the default certificate (SHA-256 with RSA)
final signature = await signWithDefaultCertificate(data);

// Sign with a different algorithm
final ecSignature = await signWithDefaultCertificate(
  data,
  algorithm: CertSignAlgorithm.sha256ec,
);

Delete a Certificate

// Delete the current default
await deleteDefaultCertificate();

// Delete by serial number
await deleteCertificateBySerialNumber('AB12CD34');

CertificateSession

CertificateSession wraps a selected certificate for repeated operations without re-selecting each time:

// From the native picker
final session = await CertificateSession.select();
if (session != null) {
  print('Using: ${session.certificate.holderName}');

  final signature = await session.sign(myData);

  // Clear default selection (does not delete the cert)
  await session.clear();

  // Or delete the certificate entirely
  await session.delete();
}

// From the existing default
final session = await CertificateSession.fromDefault();

Signing Algorithms

Enum Algorithm
CertSignAlgorithm.sha256rsa SHA-256 with RSA (default)
CertSignAlgorithm.sha384rsa SHA-384 with RSA
CertSignAlgorithm.sha512rsa SHA-512 with RSA
CertSignAlgorithm.sha256ec SHA-256 with ECDSA
CertSignAlgorithm.sha384ec SHA-384 with ECDSA
CertSignAlgorithm.sha512ec SHA-512 with ECDSA

Key Usage Types

Enum Description
CertKeyUsage.signing Digital signatures
CertKeyUsage.authentication Identity authentication
CertKeyUsage.encryption Data encryption

Parse from strings:

final usage = CertKeyUsage.tryParse('SIGNING');
final usages = CertKeyUsage.parseUsages('SIGNING;AUTHENTICATION');

DeviceCertificate Extensions

The DeviceCertificateX extension adds convenience properties:

cert.isExpired;       // Whether the certificate has expired
cert.daysUntilExpiry; // Days remaining (negative if expired)
cert.isExpiringSoon;  // Expires within 30 days
cert.canSign;         // Has CertKeyUsage.signing
cert.canAuthenticate; // Has CertKeyUsage.authentication
cert.canEncrypt;      // Has CertKeyUsage.encryption
cert.usageSummary;    // "Signing, Authentication"
cert.displayName;     // Alias if set, otherwise holderName
cert.expiryStatus;    // "Expires in 45 days", "Expired", etc.

Error Handling

All errors extend the sealed CertificateError class:

Error Description
CertNotSelectedError No default certificate is selected
CertImportCancelledError User cancelled the import
CertIncorrectPasswordError Wrong password for the PKCS#12 file
CertAlreadyExistsError Certificate already exists in the keychain
CertSigningError Signing operation failed
CertNotFoundError Certificate not found
CertUnknownError Unexpected error with a message
try {
  await signWithDefaultCertificate(data);
} on CertNotSelectedError {
  print('Please select a certificate first');
} on CertSigningError {
  print('Signing failed');
} on CertificateError catch (e) {
  print('Certificate error: ${e.message}');
}

Architecture

This is a federated Flutter plugin:

Package Description
felectronic_certificates App-facing API
felectronic_certificates_platform_interface Platform interface + models
felectronic_certificates_android Android implementation (KeyStore + AAR)
felectronic_certificates_ios iOS implementation (Keychain)

Platform communication uses Pigeon for type-safe bindings.