Skip to content

Commit 2e7ee0f

Browse files
authored
fix(signatures): ensure crypto engine is always installed before use (#36)
1 parent 28fc1aa commit 2e7ee0f

File tree

6 files changed

+36
-23
lines changed

6 files changed

+36
-23
lines changed

src/integration/signatures/signing.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,8 @@ describe("signing integration", () => {
459459

460460
const pdfStr = new TextDecoder().decode(bytes);
461461
expect(pdfStr).toContain("/Type /Sig");
462+
463+
await saveTestOutput("signatures/signed-aes128.pdf", bytes);
462464
});
463465

464466
it("signs with Triple DES P12", async () => {
@@ -473,6 +475,8 @@ describe("signing integration", () => {
473475

474476
const pdfStr = new TextDecoder().decode(bytes);
475477
expect(pdfStr).toContain("/Type /Sig");
478+
479+
await saveTestOutput("signatures/signed-3des.pdf", bytes);
476480
});
477481

478482
it("signs with legacy RC2 P12", async () => {
@@ -487,6 +491,8 @@ describe("signing integration", () => {
487491

488492
const pdfStr = new TextDecoder().decode(bytes);
489493
expect(pdfStr).toContain("/Type /Sig");
494+
495+
await saveTestOutput("signatures/signed-rc2.pdf", bytes);
490496
});
491497
});
492498

src/signatures/crypto/crypto-engine.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -245,12 +245,18 @@ export function getCryptoEngine(): CryptoEngine {
245245
}
246246

247247
/**
248-
* Install the legacy crypto engine globally in pkijs.
249-
* Call this once at startup to enable legacy P12 support.
248+
* Get the pkijs Crypto object, ensuring our custom engine is installed.
249+
*
250+
* Handles cases where pkijs engine might not be set yet, or where another
251+
* engine is already installed.
250252
*/
251-
export function installCryptoEngine(): void {
253+
export const getCrypto = () => {
252254
const engine = getCryptoEngine();
253255

254-
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
255-
pkijs.setEngine(engine.name, engine as unknown as pkijs.ICryptoEngine);
256-
}
256+
if (!pkijs.engine || pkijs.engine.name !== engine.name) {
257+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
258+
pkijs.setEngine(engine.name, engine as unknown as pkijs.ICryptoEngine);
259+
}
260+
261+
return pkijs.getCrypto(true);
262+
};

src/signatures/crypto/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export {
1010
CryptoEngine,
1111
decryptLegacyPbe,
1212
getCryptoEngine,
13-
installCryptoEngine,
13+
getCrypto,
1414
isLegacyPbeOid,
1515
LEGACY_PBE_OIDS,
1616
} from "./crypto-engine";

src/signatures/revocation.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { fromBER, ObjectIdentifier, OctetString, Sequence } from "asn1js";
1212
import * as pkijs from "pkijs";
1313

1414
import { toArrayBuffer } from "../helpers/buffer";
15+
import { getCrypto } from "./crypto";
1516
import {
1617
OID_AD_OCSP,
1718
OID_AUTHORITY_INFO_ACCESS,
@@ -245,7 +246,7 @@ export class DefaultRevocationProvider implements RevocationProvider {
245246
cert: pkijs.Certificate,
246247
issuer: pkijs.Certificate,
247248
): Promise<Uint8Array> {
248-
const crypto = pkijs.getCrypto(true);
249+
const crypto = getCrypto();
249250

250251
// Use SHA-1 for OCSP certID hashes.
251252
// While SHA-1 is deprecated for signatures, it's widely required for OCSP

src/signatures/signers/crypto-key.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,11 @@
44
* Signs using a CryptoKey directly.
55
*/
66

7-
import * as pkijs from "pkijs";
87
import { createCMSECDSASignature } from "pkijs";
98

9+
import { getCrypto } from "../crypto";
1010
import type { DigestAlgorithm, KeyType, SignatureAlgorithm, Signer } from "../types";
1111

12-
const cryptoEngine = pkijs.getCrypto(true);
13-
1412
/**
1513
* Signer that uses a Web Crypto CryptoKey directly.
1614
*
@@ -71,6 +69,8 @@ export class CryptoKeySigner implements Signer {
7169
* @returns Raw signature bytes
7270
*/
7371
async sign(data: Uint8Array, algorithm: DigestAlgorithm): Promise<Uint8Array> {
72+
const crypto = getCrypto();
73+
7474
let signAlgorithm: { name: string; saltLength?: number; hash?: { name: string } };
7575

7676
switch (this.signatureAlgorithm) {
@@ -87,7 +87,7 @@ export class CryptoKeySigner implements Signer {
8787
break;
8888
}
8989

90-
const signature = await cryptoEngine.sign(signAlgorithm, this.privateKey, new Uint8Array(data));
90+
const signature = await crypto.sign(signAlgorithm, this.privateKey, new Uint8Array(data));
9191

9292
// WebCrypto ECDSA returns P1363 format (r || s), but CMS requires DER format
9393
if (this.signatureAlgorithm === "ECDSA") {

src/signatures/signers/p12.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { createCMSECDSASignature } from "pkijs";
1010

1111
import { toArrayBuffer } from "../../helpers/buffer";
1212
import { buildCertificateChain } from "../aia";
13-
import { decryptLegacyPbe, installCryptoEngine, isLegacyPbeOid, PKCS12KDF } from "../crypto";
13+
import { decryptLegacyPbe, getCrypto, isLegacyPbeOid, PKCS12KDF } from "../crypto";
1414
import {
1515
OID_CERT_BAG,
1616
OID_EC_PUBLIC_KEY,
@@ -24,12 +24,6 @@ import {
2424
import type { DigestAlgorithm, KeyType, SignatureAlgorithm, Signer } from "../types";
2525
import { CertificateChainError, SignerError } from "../types";
2626

27-
// Install our legacy crypto engine to handle 3DES/RC2 encrypted P12 files
28-
installCryptoEngine();
29-
30-
// Get the crypto engine (now with legacy support)
31-
const cryptoEngine = pkijs.getCrypto(true);
32-
3327
/**
3428
* Options for creating a P12Signer.
3529
*/
@@ -252,6 +246,8 @@ export class P12Signer implements Signer {
252246
password: string,
253247
passwordBuffer: ArrayBuffer,
254248
): Promise<CryptoKey> {
249+
const crypto = getCrypto();
250+
255251
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
256252
const keyBag = safeBag.bagValue as pkijs.PKCS8ShroudedKeyBag;
257253
const algorithmId = keyBag.encryptionAlgorithm.algorithmId;
@@ -300,7 +296,7 @@ export class P12Signer implements Signer {
300296
decryptedKey = toArrayBuffer(decrypted);
301297
} else {
302298
// Use pkijs/Web Crypto
303-
decryptedKey = await cryptoEngine.decryptEncryptedContentInfo({
299+
decryptedKey = await crypto.decryptEncryptedContentInfo({
304300
encryptedContentInfo: new pkijs.EncryptedContentInfo({
305301
contentEncryptionAlgorithm: keyBag.encryptionAlgorithm,
306302
encryptedContent: keyBag.encryptedData,
@@ -325,11 +321,13 @@ export class P12Signer implements Signer {
325321
* Import a PrivateKeyInfo into WebCrypto.
326322
*/
327323
private static async importPrivateKey(privateKeyInfo: pkijs.PrivateKeyInfo): Promise<CryptoKey> {
324+
const crypto = getCrypto();
325+
328326
const algorithmOid = privateKeyInfo.privateKeyAlgorithm.algorithmId;
329327

330328
// RSA
331329
if (algorithmOid === OID_RSA_ENCRYPTION) {
332-
return cryptoEngine.importKey(
330+
return crypto.importKey(
333331
"pkcs8",
334332
privateKeyInfo.toSchema().toBER(false),
335333
{ name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
@@ -355,7 +353,7 @@ export class P12Signer implements Signer {
355353
}
356354
}
357355

358-
return cryptoEngine.importKey(
356+
return crypto.importKey(
359357
"pkcs8",
360358
privateKeyInfo.toSchema().toBER(false),
361359
{ name: "ECDSA", namedCurve },
@@ -413,6 +411,8 @@ export class P12Signer implements Signer {
413411
* @returns The signature bytes
414412
*/
415413
async sign(data: Uint8Array, algorithm: DigestAlgorithm): Promise<Uint8Array> {
414+
const crypto = getCrypto();
415+
416416
let signAlgorithm: { name: string; saltLength?: number; hash?: { name: string } };
417417

418418
switch (this.signatureAlgorithm) {
@@ -429,7 +429,7 @@ export class P12Signer implements Signer {
429429
break;
430430
}
431431

432-
const signature = await cryptoEngine.sign(signAlgorithm, this.privateKey, new Uint8Array(data));
432+
const signature = await crypto.sign(signAlgorithm, this.privateKey, new Uint8Array(data));
433433

434434
// WebCrypto ECDSA returns P1363 format (r || s), but CMS requires DER format
435435
if (this.signatureAlgorithm === "ECDSA") {

0 commit comments

Comments
 (0)