You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular/NodeJS应用对接智能卡/USB令牌实现PDF签名回调咨询

对接智能卡/USB令牌实现PDF数字签名方案

核心前提说明

如果智能卡/USB令牌是用户本地设备,后端(Node.js)无法直接访问用户本地硬件,必须通过前端(Angular)对接;仅当智能卡插在服务器上时,后端方案才适用。以下针对用户场景(本地智能卡)给出可行方案:


前端对接方案(Angular + Web Crypto API + PKCS#11兼容库)

浏览器本身不原生支持PKCS#11,但可以通过WebAssembly封装的PKCS#11库结合Web Crypto API实现本地智能卡访问,流程如下:

1. 依赖准备

安装前端PKCS#11库(以pkcs11js的WASM版本为例):

npm install pkcs11js

2. 前端签名流程代码示例

import * as pkcs11 from 'pkcs11js';
import { HttpClient } from '@angular/common/http';

export class PdfSignComponent {
  constructor(private http: HttpClient) {}

  async signPdfBeforeSave(pdfBlob: Blob) {
    try {
      // 加载PKCS#11模块(路径需根据用户系统调整:Windows用cryptoki.dll,Linux用libpkcs11.so,macOS用libpkcs11.dylib)
      const lib = pkcs11.Module.load('/path/to/pkcs11/library');
      lib.initialize();

      // 获取已插入智能卡的插槽
      const slots = lib.getSlots(true); // true仅返回有卡的插槽
      if (slots.length === 0) {
        alert('未检测到智能卡/USB令牌');
        return;
      }
      const slot = slots[0];
      const session = slot.open(pkcs11.SessionFlag.SERIAL_SESSION | pkcs11.SessionFlag.RW_SESSION);

      // 提示用户输入PIN(本地处理,绝不传给后端)
      const pin = prompt('请输入智能卡PIN码:');
      if (!pin) {
        session.close();
        lib.finalize();
        return;
      }
      session.login('user', pin);

      // 查找智能卡中的签名私钥
      const privateKeys = session.find({
        class: pkcs11.ObjectClass.PRIVATE_KEY,
        keyType: pkcs11.KeyType.RSA,
        sign: true
      });
      if (privateKeys.length === 0) {
        alert('智能卡中未找到可用签名密钥');
        session.logout();
        session.close();
        lib.finalize();
        return;
      }
      const privateKey = privateKeys[0];

      // 计算PDF文件的SHA-256哈希(智能卡仅签哈希,避免大文件传输)
      const pdfArrayBuffer = await pdfBlob.arrayBuffer();
      const pdfHash = await crypto.subtle.digest('SHA-256', pdfArrayBuffer);

      // 调用智能卡进行签名
      const signature = session.sign(
        { mechanism: pkcs11.Mechanism.SHA256_RSA_PKCS },
        privateKey,
        new Uint8Array(pdfHash)
      );

      // 清理资源
      session.logout();
      session.close();
      lib.finalize();

      // 将原始PDF和签名传给后端,由后端嵌入签名并保存
      const pdfBase64 = btoa(String.fromCharCode(...new Uint8Array(pdfArrayBuffer)));
      const signatureBase64 = btoa(String.fromCharCode(...signature));

      this.http.post('/api/save-signed-pdf', { pdf: pdfBase64, signature: signatureBase64 })
        .subscribe({
          next: () => alert('PDF签名并保存成功'),
          error: (err) => console.error('保存失败:', err)
        });
    } catch (err) {
      console.error('签名失败:', err);
      alert('签名过程出错,请检查智能卡驱动和PIN码');
    }
  }
}

3. 后端嵌入签名并保存(Node.js)

使用node-signpdf(遵循PAdES规范)和pdf-lib处理PDF签名嵌入:

const express = require('express');
const app = express();
app.use(express.json({ limit: '10mb' }));
const { PDFDocument } = require('pdf-lib');
const { sign } = require('node-signpdf');
const fs = require('fs');

app.post('/api/save-signed-pdf', async (req, res) => {
  try {
    const { pdf: pdfBase64, signature: signatureBase64 } = req.body;
    const pdfBytes = Buffer.from(pdfBase64, 'base64');
    const signatureBytes = Buffer.from(signatureBase64, 'base64');

    // 加载原始PDF,添加签名占位符并嵌入签名(遵循PAdES规范)
    const signedPdf = await sign(pdfBytes, signatureBytes, {
      reason: '用户电子签名',
      contactInfo: 'contact@example.com',
      location: '线上签署'
    });

    // 保存到服务器指定目录
    const savePath = `./signed-documents/${Date.now()}.pdf`;
    fs.writeFileSync(savePath, signedPdf);
    res.status(200).send({ message: '签名PDF已保存', filePath: savePath });
  } catch (err) {
    console.error('后端处理失败:', err);
    res.status(500).send({ error: '保存签名PDF失败' });
  }
});

app.listen(3000, () => console.log('后端服务启动'));

关键注意事项

  • 驱动依赖:用户必须安装智能卡对应的PKCS#11驱动(如银行U盾驱动、通用PC/SC驱动),否则前端无法识别设备。
  • 安全性:PIN码必须在前端本地输入和处理,绝对不能传输到后端;仅将PDF哈希传给智能卡签名,避免原始PDF泄露。
  • PDF规范:签名必须遵循PKCS#7/PAdES标准,否则PDF阅读器可能不认可签名有效性,推荐使用node-signpdf等专业库处理。
  • 浏览器兼容:Web Crypto API和WASM版PKCS#11库在Chrome、Firefox、Edge中支持良好,Safari可能存在兼容性限制。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 03:09:56