You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在JavaScript中读取USB Token中的证书及公私钥?

Working with USB Tokens (Smart Cards) in JavaScript: Reading Certificates & Accessing Keys

Hey there! Let's break down how to interact with USB tokens (smart cards) to retrieve certificates and work with their associated public/private keys in JavaScript. The approach varies a lot depending on whether you're building for the browser or Node.js environment, so I'll cover both scenarios below.

Browser Environment

Browsers enforce strict security restrictions that prevent direct access to local hardware, so we need to use PKCS#11 (the global standard for smart card cryptography) via a WebAssembly (WASM) binding. The most reliable library for this is pkcs11js—a WASM port of the PKCS#11 API.

Key Pre-Requisites & Notes:

  • Users must have the official PKCS#11 driver for their USB Token installed locally (most token vendors provide these for free on their websites).
  • Critical: Private keys cannot be exported from the token—they stay locked on the device for security. You can only invoke cryptographic operations (like signing) through the PKCS#11 API, not extract the raw key data.

Example Workflow:

  1. Load the PKCS#11 WASM library

    import * as pkcs11 from 'pkcs11js';
    
    // Initialize with your token's driver path
    // (In browsers, you may need to prompt users to select the driver file manually)
    pkcs11.load('/path/to/token-driver.so'); // Linux/macOS
    // pkcs11.load('C:\\Windows\\System32\\token-driver.dll'); // Windows
    
  2. Connect to the token and log in

    // Get slots with active tokens inserted
    const slots = pkcs11.getSlots(true);
    const targetSlot = slots[0]; // Use the first detected token (adjust if multiple exist)
    const session = targetSlot.open(pkcs11.CKF_RW_SESSION | pkcs11.CKF_SERIAL_SESSION);
    
    // Prompt the user for their token PIN (never hardcode this!)
    const userPin = prompt('Enter your USB Token PIN:');
    session.login(pkcs11.CKU_USER, userPin);
    
  3. Retrieve certificates and extract public keys

    // Find all X.509 certificates on the token
    const certObjects = session.findObjects({
      class: pkcs11.CKO_CERTIFICATE,
      certType: pkcs11.CKC_X_509,
    });
    
    for (const cert of certObjects) {
      // Fetch key certificate attributes
      const certAttrs = session.getAttributeValue(cert, [
        pkcs11.CKA_SUBJECT,
        pkcs11.CKA_VALUE, // Raw DER-encoded certificate data
        pkcs11.CKA_ID, // Unique ID to match the certificate's private key
      ]);
    
      // Parse the DER certificate to get a usable public key via Web Crypto API
      const certDer = new Uint8Array(certAttrs[pkcs11.CKA_VALUE]);
      const publicKey = await window.crypto.subtle.importKey(
        'spki',
        certDer,
        { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' },
        true,
        ['verify']
      );
    
      console.log('Certificate Subject:', certAttrs[pkcs11.CKA_SUBJECT].toString());
      console.log('Public Key (Web Crypto):', publicKey);
    }
    
  4. Sign data using the token's private key

    // Find the private key linked to the certificate (match by ID)
    const privateKeyObjects = session.findObjects({
      class: pkcs11.CKO_PRIVATE_KEY,
      keyType: pkcs11.CKK_RSA,
      id: certAttrs[pkcs11.CKA_ID],
    });
    
    const privateKey = privateKeyObjects[0];
    const dataToSign = new TextEncoder().encode('Data to sign with USB Token');
    
    // Perform signing directly on the token
    const signature = session.sign(
      { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' },
      privateKey,
      dataToSign
    );
    
    console.log('Generated Signature:', new Uint8Array(signature));
    
  5. Clean up resources

    session.logout();
    session.close();
    pkcs11.finalize();
    

Node.js Environment

In Node.js, you have direct access to local system resources, so you can use native PKCS#11 bindings like pkcs11js (same library as above, but optimized for Node.js) or node-pkcs11.

Example Workflow:

  1. Install the library

    npm install pkcs11js
    
  2. Initialize the PKCS#11 driver

    const pkcs11 = require('pkcs11js');
    
    // Load your token's PKCS#11 driver
    pkcs11.load('/path/to/token-driver.so'); // Linux/macOS
    // pkcs11.load('C:\\Windows\\System32\\token-driver.dll'); // Windows
    
  3. Connect and log in

    const slots = pkcs11.getSlots(true);
    const targetSlot = slots[0];
    const session = targetSlot.open(pkcs11.CKF_RW_SESSION | pkcs11.CKF_SERIAL_SESSION);
    
    // Get PIN from environment variable or user input (never hardcode!)
    const userPin = process.env.TOKEN_PIN || prompt('Enter USB Token PIN:');
    session.login(pkcs11.CKU_USER, userPin);
    
  4. Retrieve certificates and public keys

    const certObjects = session.findObjects({
      class: pkcs11.CKO_CERTIFICATE,
      certType: pkcs11.CKC_X_509,
    });
    
    const crypto = require('crypto');
    
    for (const cert of certObjects) {
      const certAttrs = session.getAttributeValue(cert, [
        pkcs11.CKA_SUBJECT,
        pkcs11.CKA_VALUE,
        pkcs11.CKA_ID,
      ]);
    
      // Parse public key using Node.js crypto module
      const publicKey = crypto.createPublicKey({
        key: certAttrs[pkcs11.CKA_VALUE],
        format: 'der',
        type: 'spki',
      });
    
      console.log('Certificate Subject:', certAttrs[pkcs11.CKA_SUBJECT].toString());
      console.log('Public Key (PEM):', publicKey.export({ type: 'spki', format: 'pem' }));
    }
    
  5. Sign data with the private key

    const privateKeyObjects = session.findObjects({
      class: pkcs11.CKO_PRIVATE_KEY,
      keyType: pkcs11.CKK_RSA,
      id: certAttrs[pkcs11.CKA_ID],
    });
    
    const privateKey = privateKeyObjects[0];
    const dataToSign = Buffer.from('Data to sign with USB Token');
    
    const signature = session.sign(
      { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA256' },
      privateKey,
      dataToSign
    );
    
    console.log('Signature (Hex):', Buffer.from(signature).toString('hex'));
    
  6. Clean up

    session.logout();
    session.close();
    pkcs11.finalize();
    

Critical Reminders

  • Private Key Security: USB tokens are designed to never let you export raw private key data—this is a core security feature. All private key operations happen on the device itself.
  • Driver Compatibility: Always use the exact PKCS#11 driver provided by your token vendor—generic drivers won’t work.
  • PIN Handling: Never store or hardcode user PINs. Use secure input methods (native prompts, environment variables) to avoid exposing sensitive data.

内容的提问来源于stack exchange,提问作者Mayur Saini

火山引擎 最新活动