跳轉到

PAdES B-LTA 數位簽章

PAdES(PDF Advanced Electronic Signatures)是 ETSI EN 319 142-1 標準定義的 PDF 電子簽章格式,為歐盟 eIDAS 法規認可的合法電子簽章形式。NextPDF Pro 提供從 B-B 到 B-LTA 的完整四級支援。


四級簽章等級

graph LR
    BB["B-B\n基礎簽章\n+ 簽署憑證"] --> BT["B-T\n+ RFC 3161\n時間戳記"]
    BT --> BLT["B-LT\n+ 憑證鏈\n+ CRL/OCSP"]
    BLT --> BLTA["B-LTA\n+ 追加時間戳記\n(可週期更新)"]
    style BLTA fill:#D97706,color:#fff
等級 關鍵新增內容 驗證有效期
B-B 基礎簽章值、簽署憑證引用 至憑證到期
B-T RFC 3161 可信時間戳記 至 TSA 憑證到期
B-LT 完整憑證鏈 + 撤銷狀態資料(CRL/OCSP) 至最舊撤銷資料到期
B-LTA 追加時間戳記封存整個驗證資料 無限(依 LTA 輪換週期)

快速開始:B-LTA 簽章

前置準備

use NextPDF\Pro\Signatures\PAdES\PadesSignatureAppender;
use NextPDF\Pro\Signatures\PAdES\PadesSignatureLevel;
use NextPDF\Pro\Signatures\PAdES\PadesSignatureOptions;
use NextPDF\Pro\Signatures\Timestamp\TimestampAuthorityClient;
use NextPDF\Pro\Signatures\Certificate\CertificateChain;

// 載入簽署憑證與私鑰(PEM 或 PKCS#12 格式)
$certificate = CertificateChain::fromPkcs12(
    pkcs12Path: '/secure/signing.p12',
    passphrase: (string) getenv('SIGNING_KEY_PASSPHRASE'),
);

// 設定 RFC 3161 時間戳記服務
$tsa = new TimestampAuthorityClient(
    url: 'https://tsa.example.com/timestamp',
    username: (string) getenv('TSA_USERNAME'),
    password: (string) getenv('TSA_PASSWORD'),
);

執行 B-LTA 簽章

use NextPDF\Pro\Document\ProDocument;
use NextPDF\Pro\Signatures\PAdES\PadesSignatureAppender;
use NextPDF\Pro\Signatures\PAdES\PadesSignatureLevel;
use NextPDF\Pro\Signatures\PAdES\PadesSignatureOptions;

// 生成或載入 PDF 文件
$doc = ProDocument::createStandalone();
$doc->addPage();
$doc->text('Quarterly Financial Report — Q4 2025', x: 20, y: 30);

$pdfBytes = $doc->render();

// 建立簽章選項
$options = PadesSignatureOptions::create(
    level: PadesSignatureLevel::BLta,
    certificate: $certificate,
    timestampAuthority: $tsa,
    reason: 'Authorized signatory — CFO approval',
    location: 'Taipei, Taiwan',
    contactInfo: 'finance@example.com',
    includeOcsp: true,
    includeCrl: true,
);

// 附加 B-LTA 簽章
$appender = new PadesSignatureAppender($options);
$signedPdf = $appender->sign($pdfBytes);

// 儲存已簽署文件
file_put_contents('/output/signed-report.pdf', $signedPdf);

多重簽名(Sequential Signing)

多方連署場景下,後續簽署者使用 PadesSignatureAppender 在現有簽章基礎上附加新簽章,不會使前一個簽章失效:

use NextPDF\Pro\Signatures\PAdES\PadesSignatureAppender;
use NextPDF\Pro\Signatures\CAdES\CadesSignatureParser;

// 解析現有文件中的簽章資訊
$parser = new CadesSignatureParser();
$existingSignatures = $parser->parse($signedPdf);

foreach ($existingSignatures as $sig) {
    echo sprintf(
        "Signer: %s | Level: %s | Valid: %s\n",
        $sig->getSubjectName(),
        $sig->getLevel()->value,
        $sig->isValid() ? 'Yes' : 'No',
    );
}

// 附加第二個簽章(不修改第一個)
$secondSignerOptions = PadesSignatureOptions::create(
    level: PadesSignatureLevel::BLta,
    certificate: $secondCertificate,
    timestampAuthority: $tsa,
    reason: 'Legal counsel review',
    signatureFieldName: 'Signature2', // 使用獨立欄位
);

$appender = new PadesSignatureAppender($secondSignerOptions);
$doubleSignedPdf = $appender->sign($signedPdf); // 傳入已有第一個簽章的 PDF

CadesSignatureParser 解析器

CadesSignatureParser 負責讀取 PDF 文件中嵌入的 CMS(Cryptographic Message Syntax)簽章結構:

use NextPDF\Pro\Signatures\CAdES\CadesSignatureParser;
use NextPDF\Pro\Signatures\CAdES\ParsedSignature;

$parser = new CadesSignatureParser();

/** @var list<ParsedSignature> $signatures */
$signatures = $parser->parse($pdfBytes);

foreach ($signatures as $signature) {
    // 簽章基本資訊
    $signature->getSubjectName();           // string: 簽署者 DN
    $signature->getSigningTime();           // DateTimeImmutable
    $signature->getLevel();                 // PadesSignatureLevel enum
    $signature->isValid();                  // bool: 密碼學驗證結果

    // 時間戳記資訊(B-T 以上才有)
    $signature->getTimestamp()?->getTime();        // ?DateTimeImmutable
    $signature->getTimestamp()?->getTsaName();     // ?string

    // 長期驗證資料(B-LT 以上才有)
    $signature->getLtvData()?->hasCrl();           // ?bool
    $signature->getLtvData()?->hasOcsp();          // ?bool
    $signature->getLtvData()?->getCertificateChain(); // ?list<X509Certificate>
}

LTA 時間戳記輪換

B-LTA 的長期效力依賴週期性的時間戳記更新。當最外層時間戳記的 TSA 憑證即將到期前,必須在現有時間戳記上再追加一個新的封存時間戳記:

use NextPDF\Pro\Signatures\PAdES\LtaTimestampRenewer;

$renewer = new LtaTimestampRenewer(
    timestampAuthority: $tsa,
    renewBeforeExpiryDays: 180, // 到期前 180 天觸發
);

// 檢查是否需要輪換
if ($renewer->needsRenewal($signedPdfBytes)) {
    $renewedPdf = $renewer->renew($signedPdfBytes);
    // 儲存更新後的文件,原有所有簽章不受影響
    file_put_contents('/archive/renewed-report.pdf', $renewedPdf);
}

簽章外觀設計

use NextPDF\Pro\Signatures\PAdES\PadesSignatureAppearance;
use NextPDF\Pro\Signatures\PAdES\SignatureAppearanceRect;

$appearance = PadesSignatureAppearance::create(
    rect: SignatureAppearanceRect::fromPoints(x: 400, y: 700, width: 180, height: 60),
    backgroundImage: '/assets/company-seal.png',
    textTemplate: "Signed by: {{subjectName}}\nDate: {{signingTime:Y-m-d}}\nReason: {{reason}}",
    fontSize: 8.0,
    fontColor: '#1E3A8A',
);

$options = PadesSignatureOptions::create(
    level: PadesSignatureLevel::BLta,
    certificate: $certificate,
    timestampAuthority: $tsa,
    appearance: $appearance,
);

驗證簽章

簽署完成後應立即驗證,確認簽章結構正確:

use NextPDF\Pro\Signatures\PAdES\PadesSignatureValidator;
use NextPDF\Pro\Signatures\PAdES\ValidationResult;

$validator = new PadesSignatureValidator(
    trustedRoots: ['/certs/root-ca.pem', '/certs/intermediate-ca.pem'],
    checkRevocation: true,
    verifyTimestamps: true,
);

/** @var ValidationResult $result */
$result = $validator->validate($signedPdfBytes);

if (!$result->isValid()) {
    foreach ($result->getErrors() as $error) {
        throw new \RuntimeException(sprintf('Signature validation failed: %s', $error->getMessage()));
    }
}

echo 'Signature valid. Level: ' . $result->getHighestLevel()->value;

錯誤處理

例外類別 觸發條件 建議處理
CertificateExpiredException 簽署憑證已過期 替換憑證或改用 HSM
TimestampAuthorityException TSA 服務無回應 重試邏輯 + 備援 TSA
OcspRevocationException 憑證已被撤銷 拒絕簽章,通知管理員
PdfCorruptedException 輸入 PDF 結構損毀 驗證輸入來源
SignatureConflictException 嘗試修改已簽署區域 改用 append-only 模式

相關資源