Skip to content

Infisical/infisical-pkcs-11

Repository files navigation

infisical infisical

Infisical PKCS#11 Module: Sign code and artifacts using keys managed in Infisical — private keys never leave Infisical.

Introduction

The Infisical PKCS#11 Module is a shared library (.so, .dylib, .dll) that implements PKCS#11 v2.40. It acts as a bridge between standard signing tools and the Infisical API — your tool loads the library, and all cryptographic operations are performed by Infisical.

Each Signer in your Infisical project appears as a PKCS#11 slot, exposing a private key object (for signing) and a certificate object (for verification and chain building).

Features

  • Remote Signing: Private keys never leave Infisical. All signing operations are performed by Infisical.
  • Universal Tool Compatibility: Works with jarsigner, osslsigncode, signtool, pkcs11-tool, GnuTLS, OpenSSL, and any PKCS#11 consumer.
  • RSA and ECDSA Support: SHA-256/384/512 with PKCS#1 v1.5, PSS, and ECDSA. Supports P-256, P-384, and P-521 curves.
  • Approval Workflows: Require human review before signing, bounded by a signature count and/or a time window per approval.
  • Audit Logging: Every signing operation is recorded with actor, timestamp, and client metadata.
  • Cross-Platform: Pre-built binaries for Linux (x86_64, ARM64), macOS (x86_64, ARM64), and Windows (x86_64).

Prerequisites

  • An Infisical instance with the Cert Manager product enabled
  • At least one Signer created (Cert Manager > Code Signing > Signers)
  • The Signer must be backed by an Internal CA, AWS Private CA, or Azure AD CS
  • A way to authenticate as a member of the Signer with the Administrator or Operator role (the signer's Members tab): either a Machine Identity with Universal Auth, or an Infisical access token (a user's or a machine identity's). See Authentication.
  • If using approval policies: an approved sign request for the signer before signing

Quick Start

1. Install

Download the binary for your platform from the releases page:

Platform File
Linux x86_64 libinfisical-pkcs11-linux-amd64.so
Linux ARM64 libinfisical-pkcs11-linux-arm64.so
macOS x86_64 libinfisical-pkcs11-darwin-amd64.dylib
macOS ARM64 libinfisical-pkcs11-darwin-arm64.dylib
Windows x86_64 libinfisical-pkcs11-windows-amd64.dll

Or build from source (requires Go 1.21+ and a C compiler):

make build

2. Configure

Create /etc/infisical/pkcs11.conf (or set INFISICAL_CONFIG to a custom path):

{
  "server_url": "https://app.infisical.com"
}

Set credentials via environment variables (recommended):

export INFISICAL_UNIVERSAL_AUTH_CLIENT_ID="your-client-id"
export INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET="your-client-secret"

3. Verify

# List available signers (slots)
pkcs11-tool --module ./libinfisical-pkcs11.so --list-slots

# List objects in a slot
pkcs11-tool --module ./libinfisical-pkcs11.so --list-objects --slot 0

4. Sign

pkcs11-tool --module ./libinfisical-pkcs11.so --sign \
  --slot 0 --mechanism SHA256-RSA-PKCS \
  --input-file document.bin --output-file document.sig

Configuration

The module reads a JSON config file and environment variables. Environment variables take precedence over config file values for credentials.

Environment Variables

Variable Description
INFISICAL_UNIVERSAL_AUTH_CLIENT_ID Machine Identity client ID (Universal Auth)
INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET Machine Identity client secret (Universal Auth)
INFISICAL_TOKEN An Infisical access token (a user or machine identity token). Selects token auth; used instead of Universal Auth credentials
INFISICAL_CONFIG Path to config file (default: /etc/infisical/pkcs11.conf)
INFISICAL_SERVER_URL The Infisical instance URL. Sets server_url (and overrides the config file)

Config File

Field Required Default Description
server_url Yes Infisical server URL
auth.method No inferred Authentication method: universal-auth or token. Inferred from the credentials when unset (a token means token, otherwise universal-auth)
auth.client_id No (none) Machine Identity client ID, for universal-auth (prefer env var)
auth.client_secret No (none) Machine Identity client secret, for universal-auth (prefer env var)
auth.token No (none) Infisical access token, for token auth (prefer the env var)
tls.ca_cert_path No Custom CA certificate for self-hosted instances
tls.skip_verify No false Skip TLS verification (development only)
cache.token_ttl_seconds No 300 Auth token cache duration
cache.cert_ttl_seconds No 3600 Certificate data cache duration
cache.signer_ttl_seconds No 300 Signer list cache duration
approval.signing_duration No Auto-request approval with this time window (e.g. "8h", "30m", "2d"). Range: 1m–30d
approval.signing_count No Auto-request approval for this many signings
log_level No info Log verbosity: trace, debug, info, warn, error
log_file No stderr Path to log file
Full config example
{
  "server_url": "https://app.infisical.com",
  "auth": {
    "client_id": "your-client-id",
    "client_secret": "your-client-secret"
  },
  "tls": {
    "ca_cert_path": "/path/to/custom-ca.pem",
    "skip_verify": false
  },
  "cache": {
    "token_ttl_seconds": 300,
    "cert_ttl_seconds": 3600,
    "signer_ttl_seconds": 300
  },
  "approval": {
    "signing_duration": "8h",
    "signing_count": 10
  },
  "log_level": "info",
  "log_file": "/var/log/infisical-pkcs11.log"
}

Authentication

The module supports two ways to authenticate with Infisical.

Universal Auth (Machine Identity) is the default. The module exchanges the client ID/secret for an access token and refreshes it automatically. Provide the credentials three ways (in order of precedence):

  1. Environment variables (recommended for CI/CD and production):

    export INFISICAL_UNIVERSAL_AUTH_CLIENT_ID="your-client-id"
    export INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET="your-client-secret"
  2. Config file (convenient for development):

    {
      "auth": {
        "client_id": "your-client-id",
        "client_secret": "your-client-secret"
      }
    }
  3. PIN at login time — tools that call C_Login can pass credentials as the PIN in the format clientId:clientSecret.

When credentials are available, the module auto-authenticates during initialization — no explicit C_Login is needed from tools.

Token auth uses an Infisical access token directly, either a user's token or a machine identity's. Provide the token three ways (in order of precedence):

  1. Environment variable (selects token auth automatically):

    export INFISICAL_TOKEN="your-access-token"
  2. Config file: set auth.token (token auth is selected automatically).

  3. PIN at login time: tools that call C_Login can pass the token as the PIN. The module detects the PIN type automatically: a clientId:clientSecret PIN selects universal-auth, anything else is treated as an access token.

We recommend the environment or config. The C_Login PIN is an alternative that works only with tools that hand it over before they list keys. Many tools list and open a key (C_GetSlotList, C_OpenSession) first and call C_Login afterwards, so for those use the environment or config instead.

Token auth is temporary. The module uses the token as-is and does not refresh it. When the token expires, signing fails until you set a new token.

Environment variables take precedence over the config file, and INFISICAL_TOKEN takes precedence over Universal Auth credentials.

Tool Integration Guides

Java jarsigner

jarsigner is the standard tool for signing JAR files, included with the JDK.

Step 1: Create a SunPKCS11 provider config (infisical-pkcs11.cfg):

name = Infisical
library = /path/to/libinfisical-pkcs11.so

On Windows, use the DLL path:

name = Infisical
library = C:\path\to\libinfisical-pkcs11-windows-amd64.dll

Step 2: Sign:

# Java 9+ (recommended)
jarsigner -keystore NONE -storetype PKCS11 \
  -addprovider SunPKCS11 \
  -providerArg infisical-pkcs11.cfg \
  -sigalg SHA256withRSA \
  myapp.jar "your-signer-name"
Java 8
jarsigner -keystore NONE -storetype PKCS11 \
  -providerClass sun.security.pkcs11.SunPKCS11 \
  -providerArg infisical-pkcs11.cfg \
  -sigalg SHA256withRSA \
  myapp.jar "your-signer-name"
Windows (PowerShell)
$env:INFISICAL_CONFIG = "C:\path\to\pkcs11.conf"
$env:INFISICAL_UNIVERSAL_AUTH_CLIENT_ID = "your-client-id"
$env:INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET = "your-client-secret"

jarsigner -keystore NONE -storetype PKCS11 `
  -addprovider SunPKCS11 `
  -providerArg infisical-pkcs11.cfg `
  -sigalg SHA256withRSA `
  myapp.jar "your-signer-name"

Step 3: Verify:

jarsigner -verify -verbose myapp.jar

Note: The alias ("your-signer-name") must match the Signer name in Infisical exactly.

osslsigncode (Authenticode on Linux/macOS)

osslsigncode signs Windows executables (.exe, .dll, .msi) from Linux or macOS.

osslsigncode sign \
  -pkcs11module /path/to/libinfisical-pkcs11.so \
  -pkcs11cert "pkcs11:object=your-signer-name;type=cert" \
  -key "pkcs11:object=your-signer-name;type=private" \
  -h sha256 \
  -in MyApp.exe -out MyApp-signed.exe

signtool (Windows Authenticode)

$env:INFISICAL_CONFIG = "C:\path\to\pkcs11.conf"
$env:INFISICAL_UNIVERSAL_AUTH_CLIENT_ID = "your-client-id"
$env:INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET = "your-client-secret"

signtool sign /fd SHA256 /f cert.cer `
  /csp "Infisical PKCS#11" `
  /kc "your-signer-name" `
  MyApp.exe

Note: Windows integration typically requires registering the PKCS#11 module as a CSP/KSP or using a wrapper like pkcs11-csp.

pkcs11-tool (OpenSC)

pkcs11-tool is useful for testing and exploring the module.

# List available slots (one per signer)
pkcs11-tool --module ./libinfisical-pkcs11.so --list-slots

# List objects in a slot
pkcs11-tool --module ./libinfisical-pkcs11.so --list-objects --slot 0

# List supported mechanisms
pkcs11-tool --module ./libinfisical-pkcs11.so --list-mechanisms --slot 0

# Sign a file
pkcs11-tool --module ./libinfisical-pkcs11.so --sign \
  --slot 0 --mechanism SHA256-RSA-PKCS \
  --input-file document.bin --output-file document.sig

GnuPG (via gnupg-pkcs11-scd)

# Install: macOS: brew install gnupg-pkcs11-scd | Ubuntu: apt install gnupg-pkcs11-scd

Add to ~/.gnupg/gpg-agent.conf:

scdaemon-program /usr/bin/gnupg-pkcs11-scd

Add to ~/.gnupg/gnupg-pkcs11-scd.conf:

providers infisical
provider-infisical-library /path/to/libinfisical-pkcs11.so

Restart and verify:

gpgconf --kill gpg-agent
gpg --card-status

Supported Mechanisms

RSA

Mechanism PKCS#11 Constant Key Sizes
SHA256-RSA-PKCS CKM_SHA256_RSA_PKCS 2048, 3072, 4096
SHA384-RSA-PKCS CKM_SHA384_RSA_PKCS 2048, 3072, 4096
SHA512-RSA-PKCS CKM_SHA512_RSA_PKCS 2048, 3072, 4096
SHA256-RSA-PKCS-PSS CKM_SHA256_RSA_PKCS_PSS 2048, 3072, 4096
SHA384-RSA-PKCS-PSS CKM_SHA384_RSA_PKCS_PSS 2048, 3072, 4096
SHA512-RSA-PKCS-PSS CKM_SHA512_RSA_PKCS_PSS 2048, 3072, 4096
RSA-PKCS (raw DigestInfo) CKM_RSA_PKCS 2048, 3072, 4096

ECDSA

Mechanism PKCS#11 Constant Curves
ECDSA-SHA256 CKM_ECDSA_SHA256 P-256, P-384, P-521
ECDSA-SHA384 CKM_ECDSA_SHA384 P-256, P-384, P-521
ECDSA-SHA512 CKM_ECDSA_SHA512 P-256, P-384, P-521
ECDSA (raw pre-hashed) CKM_ECDSA P-256, P-384, P-521

Approval Workflow

If a signer has an approval policy, you need an approved sign request before signing. Without it, sign requests will return CKR_GENERAL_ERROR (HTTP 403).

Automatic Approval Requests

When approval.signing_duration and/or approval.signing_count are configured, the module automatically creates an approval request when signing is denied because no approved sign request exists. The sign operation still fails (an approver must approve the request first), but the request is created for you — no manual API call needed.

{
  "approval": {
    "signing_duration": "8h",
    "signing_count": 10
  }
}

Once an approver approves the request (via the Infisical UI at Cert Manager > Code Signing > Signers > <signer> > Approvals tab), retrying the sign operation will succeed.

Requesting approval via API
# Authenticate
TOKEN=$(curl -s https://app.infisical.com/api/v1/auth/universal-auth/login \
  -H "Content-Type: application/json" \
  -d '{"clientId":"...","clientSecret":"..."}' | jq -r '.accessToken')

# Request access for an 8-hour window, capped at 10 signatures
# macOS / BSD:
START=$(date -u -v+1M +"%Y-%m-%dT%H:%M:%SZ")
END=$(date -u -v+8H +"%Y-%m-%dT%H:%M:%SZ")
# Linux / GNU coreutils (uncomment if your `date` is GNU):
# START=$(date -u -d '+1 minute' +"%Y-%m-%dT%H:%M:%SZ")
# END=$(date -u -d '+8 hours' +"%Y-%m-%dT%H:%M:%SZ")

curl -s https://app.infisical.com/api/v1/cert-manager/signers/your-signer-id/requests \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d "{
    \"justification\": \"CI/CD release build\",
    \"requestedSignings\": 10,
    \"requestedWindowStart\": \"$START\",
    \"requestedWindowEnd\": \"$END\"
  }"

An approver must approve the request via the Infisical UI (Cert Manager > Code Signing > Signers > <signer> > Approvals tab). Once approved, signing works for the granted window.

Request Shape

Each approval request can be bounded by a signature count, a time window, or both. The Signer's policy sets the ceiling for each (Signatures per approval, Signing window) — a request that exceeds the policy is rejected with a 400. justification is the only required field; omitting a bound falls back to the policy ceiling.

Field Description
justification Required. Free-text reason for the request (1–2048 chars), shown to approvers.
requestedSignings How many sign operations the approval permits. Leave empty to fall back to the policy ceiling.
requestedWindowStart ISO 8601 timestamp the access window opens. Defaults to "now".
requestedWindowEnd ISO 8601 timestamp the access window closes. Leave empty to fall back to the policy ceiling.

Admin endpoints

Administrators of a Signer can also pre-approve or revoke requests on behalf of other members:

  • POST /api/v1/cert-manager/signers/{signerId}/requests/pre-approve — body accepts granteeUserId or granteeIdentityId plus the same justification / requestedSignings / requestedWindowStart / requestedWindowEnd fields. Creates a request that is already approved.
  • POST /api/v1/cert-manager/signers/{signerId}/requests/{requestId}/revoke — revokes a pending or active request. No body.

Troubleshooting

Enable debug logging by adding to your config file:

{
  "log_level": "debug",
  "log_file": "/tmp/infisical-pkcs11.log"
}

Then monitor: tail -f /tmp/infisical-pkcs11.log

Common Errors

Error Cause Fix
CKR_GENERAL_ERROR on init Config file not found or invalid Check INFISICAL_CONFIG path and JSON syntax
CKR_GENERAL_ERROR on sign Approval required or permission denied Request approval, or confirm the Machine Identity is a Signer member with the Administrator or Operator role (Auditors cannot sign)
CKR_USER_NOT_LOGGED_IN No credentials or token expired Set INFISICAL_UNIVERSAL_AUTH_CLIENT_ID and CLIENT_SECRET
CKR_PIN_INCORRECT Invalid credentials in PIN For universal-auth use the format clientId:clientSecret; for token auth pass the access token as the PIN
CKR_SLOT_ID_INVALID No signers visible to this Machine Identity (none exist, or the identity isn't a member of any signer) Create a signer in Cert Manager > Code Signing, or add this identity as a member on the signer's Members tab
CKR_DEVICE_ERROR Server unreachable Check server_url and network connectivity

Building from Source

Requires Go 1.21+ and a C compiler (GCC or Clang).

# Build for current platform
make build

# Run tests
make test

# Cross-compile (requires appropriate cross-compilers)
make build-linux-amd64
make build-linux-arm64
make build-darwin-amd64
make build-darwin-arm64
make build-windows-amd64    # requires MinGW

Security

  • Private keys never leave Infisical. All signing happens inside Infisical.
  • Use environment variables for credentials — avoid committing secrets to config files.
  • If using config-file credentials, restrict permissions: chmod 600 /etc/infisical/pkcs11.conf
  • Auth tokens are cached in memory only — never written to disk.
  • Enable approval policies on signers to require human review before signing.
  • Every signing operation is recorded in Infisical audit logs with actor, timestamp, and client metadata.

Please do not file GitHub issues or post on public forums for security vulnerabilities. If you believe you have uncovered a vulnerability, contact security@infisical.com.

Contributing

Whether it's big or small, we love contributions. Check out our contributing guide to get started.

Not sure where to get started? Join our Slack and ask us any questions.

About

No description, website, or topics provided.

Resources

License

Stars

1 star

Watchers

0 watching

Forks

Packages

 
 
 

Contributors