node-opcua-pki 6.10.2 → 6.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-opcua-pki",
3
- "version": "6.10.2",
3
+ "version": "6.11.0",
4
4
  "description": "PKI management for node-opcua",
5
5
  "type": "commonjs",
6
6
  "main": "./dist/index.js",
@@ -50,5 +50,5 @@
50
50
  "wget-improved-2": "^3.3.0",
51
51
  "yauzl": "^3.2.1"
52
52
  },
53
- "gitHead": "981178104abfceac923695bf69f4a3f221729191"
53
+ "gitHead": "5536a27b3932c970505bee5fc5152424d6f16ea9"
54
54
  }
package/readme.md CHANGED
@@ -190,182 +190,46 @@ pki demo [--dev] [--silent] [--clean]
190
190
  ## Programmatic Usage
191
191
 
192
192
  ```typescript
193
- import { CertificateManager } from "node-opcua-pki";
194
-
195
- const cm = new CertificateManager({
196
- location: "./my_pki",
197
- keySize: 2048,
198
- });
199
-
200
- await cm.initialize();
201
-
202
- // Create a self-signed certificate
203
- await cm.createSelfSignedCertificate({
204
- applicationUri: "urn:my-server:application",
205
- subject: "/CN=My Server/O=My Organization",
206
- dns: ["localhost"],
207
- startDate: new Date(),
208
- validity: 365,
209
- });
193
+ import { CertificateManager, CertificateAuthority } from "node-opcua-pki";
210
194
  ```
211
195
 
212
- ### CertificateManager API
213
-
214
- #### Certificate Trust
215
-
216
- | Method | Description |
217
- | ----------------------------------------------- | ------------------------------------------------------------------------------------------ |
218
- | `trustCertificate(cert)` | Add a certificate to the trusted store |
219
- | `rejectCertificate(cert)` | Move a certificate to the rejected store |
220
- | `verifyCertificate(cert, options?)` | Full certificate chain validation |
221
- | `removeTrustedCertificate(thumbprint)` | Remove a trusted certificate by SHA-1 thumbprint. Returns the certificate buffer or `null` |
222
- | `addTrustedCertificateFromChain(certChain)` | Validate and trust the leaf certificate from a DER chain |
223
- | `isIssuerInUseByTrustedCertificate(issuerCert)` | Check if any trusted cert was signed by this issuer |
224
- | `reloadCertificates()` | Force a full re-scan of all PKI folders |
225
-
226
- #### Issuer (CA) Certificates
227
-
228
- | Method | Description |
229
- | --------------------------------------------- | ------------------------------------------------------------------------ |
230
- | `addIssuer(cert, validate?, addInTrustList?)` | Add a CA certificate to the issuers store |
231
- | `hasIssuer(thumbprint)` | Check if an issuer exists by SHA-1 thumbprint |
232
- | `removeIssuer(thumbprint)` | Remove an issuer by thumbprint. Returns the certificate buffer or `null` |
233
- | `findIssuerCertificate(cert)` | Find the issuer certificate for a given certificate |
234
-
235
- #### Certificate Revocation Lists (CRLs)
196
+ ### [CertificateManager](./docs/certificate-manager.md)
236
197
 
237
- | Method | Description |
238
- | ----------------------------------------------- | --------------------------------------------------------------------------------------------- |
239
- | `addRevocationList(crl, target?)` | Add a CRL. `target` is `"issuers"` (default) or `"trusted"` |
240
- | `clearRevocationLists(target)` | Remove all CRLs from `"issuers"`, `"trusted"`, or `"all"` |
241
- | `removeRevocationListsForIssuer(cert, target?)` | Remove CRLs issued by a specific CA. `target`: `"issuers"`, `"trusted"`, or `"all"` (default) |
242
- | `isCertificateRevoked(cert, issuerCert?)` | Check if a certificate has been revoked |
243
-
244
- #### Folder Accessors
245
-
246
- | Getter | Path |
247
- | ------------------- | -------------------------- |
248
- | `trustedFolder` | `{location}/trusted/certs` |
249
- | `rejectedFolder` | `{location}/rejected` |
250
- | `crlFolder` | `{location}/trusted/crl` |
251
- | `issuersCertFolder` | `{location}/issuers/certs` |
252
- | `issuersCrlFolder` | `{location}/issuers/crl` |
253
- | `rootDir` | `{location}` |
254
-
255
- ### CertificateAuthority API
256
-
257
- The `CertificateAuthority` class manages an OpenSSL-based CA directory structure for issuing, revoking, and tracking certificates.
198
+ Manages an OPC UA–compliant PKI directory with trust stores, issuer
199
+ stores, file watching, and certificate lifecycle.
258
200
 
259
201
  ```typescript
260
- import { CertificateAuthority } from "node-opcua-pki";
261
-
262
- const ca = new CertificateAuthority({
263
- location: "./my_ca",
264
- keySize: 2048,
265
- });
266
- await ca.initialize();
202
+ const cm = new CertificateManager({ location: "./my_pki" });
203
+ await cm.initialize();
267
204
  ```
268
205
 
269
- #### Buffer Accessors
270
-
271
- | Method | Returns | Description |
272
- | ------------------------ | -------- | ---------------------------------------- |
273
- | `getCACertificateDER()` | `Buffer` | CA certificate as DER |
274
- | `getCACertificatePEM()` | `string` | CA certificate as PEM |
275
- | `getCRLDER()` | `Buffer` | Current CRL as DER (empty if none) |
276
- | `getCRLPEM()` | `string` | Current CRL as PEM |
206
+ ### [CertificateAuthority](./docs/certificate-authority.md)
277
207
 
278
- #### Buffer Operations
279
-
280
- | Method | Returns | Description |
281
- | --- | --- | --- |
282
- | `signCertificateRequestFromDER(csrDer, options?)` | `Promise<Buffer>` | Sign a DER-encoded CSR, return signed cert as DER. Handles temp files internally. |
283
- | `revokeCertificateDER(certDer, reason?)` | `Promise<void>` | Revoke a DER-encoded certificate. Looks up the stored cert by serial number. |
208
+ OpenSSL-based CA for issuing, revoking, and tracking certificates.
209
+ Supports root CAs, intermediate CAs with manual 3-step workflow,
210
+ proactive certificate renewal, and full chain output per OPC UA
211
+ Part 6 §6.2.6.
284
212
 
285
213
  ```typescript
286
- // Sign a CSR from a DER buffer
287
- const certDer = await ca.signCertificateRequestFromDER(csrDer, {
288
- validity: 365,
214
+ // Root CA
215
+ const rootCA = new CertificateAuthority({
216
+ keySize: 2048,
217
+ location: "./my_root_ca",
218
+ subject: "/CN=My Root CA",
289
219
  });
220
+ await rootCA.initialize();
290
221
 
291
- // Revoke a certificate from its DER buffer
292
- await ca.revokeCertificateDER(certDer, "keyCompromise");
293
- ```
294
-
295
- #### Certificate Database
296
-
297
- These methods parse the OpenSSL `index.txt` database to query issued certificate status. Certificate files are read from the CA's `certs/` directory.
298
-
299
- | Method | Returns | Description |
300
- | --- | --- | --- |
301
- | `getIssuedCertificates()` | `IssuedCertificateRecord[]` | All records from `index.txt` |
302
- | `getIssuedCertificateCount()` | `number` | Total number of issued certificates |
303
- | `getCertificateStatus(serial)` | `string \| undefined` | `"valid"`, `"revoked"`, or `"expired"` |
304
- | `getCertificateBySerial(serial)` | `Buffer \| undefined` | DER buffer from `certs/<serial>.pem` |
305
-
306
- ```typescript
307
- // List all issued certificates
308
- const records = ca.getIssuedCertificates();
309
- for (const r of records) {
310
- console.log(`${r.serial}: ${r.status} — ${r.subject}`);
311
- }
312
-
313
- // Check if a specific certificate is revoked
314
- const status = ca.getCertificateStatus("1000");
315
- if (status === "revoked") {
316
- console.log("Certificate 1000 has been revoked");
317
- }
318
-
319
- // Read a certificate by serial number
320
- const der = ca.getCertificateBySerial("1000");
321
- ```
322
-
323
- **`IssuedCertificateRecord`** fields:
324
-
325
- | Field | Type | Description |
326
- | --- | --- | --- |
327
- | `serial` | `string` | Hex serial (e.g. `"1000"`) |
328
- | `status` | `"valid" \| "revoked" \| "expired"` | Certificate status |
329
- | `subject` | `string` | X.500 subject (slash-delimited) |
330
- | `expiryDate` | `string` | ISO-8601 expiry date |
331
- | `revocationDate` | `string?` | ISO-8601 revocation date (if revoked) |
332
-
333
- ### File Watching
334
-
335
- `CertificateManager` uses [chokidar](https://github.com/paulmillr/chokidar) to watch the PKI folders for changes. By default, it uses **native OS events** (inotify, FSEvents, ReadDirectoryChangesW) for near-real-time detection.
336
-
337
- #### Environment Variables
338
-
339
- | Variable | Description | Default |
340
- | --- | --- | --- |
341
- | `OPCUA_PKI_USE_POLLING` | Set to `"true"` to use polling instead of native FS events. Required for NFS, CIFS, Docker volumes, or other remote/virtual file systems. | `false` |
342
- | `OPCUA_PKI_POLLING_INTERVAL` | Polling interval in milliseconds (only effective when polling is enabled). Clamped to [100, 600 000]. | `5000` |
343
-
344
- ```bash
345
- # Example: enable polling with a 2-second interval
346
- OPCUA_PKI_USE_POLLING=true OPCUA_PKI_POLLING_INTERVAL=2000 node my_server.js
347
- ```
348
-
349
- > **Note:** If external processes modify the PKI folders directly (e.g., CLI tools, OPC UA `WriteTrustList`), call `reloadCertificates()` to force an immediate re-scan of the folder state.
350
-
351
- #### Events
352
-
353
- After `initialize()`, the `CertificateManager` emits events when its file-system watchers detect live changes. Events are **not** emitted during `initialize()` or `reloadCertificates()` to avoid noise.
354
-
355
- | Event | Payload | Description |
356
- | --- | --- | --- |
357
- | `certificateAdded` | `{ store, certificate, fingerprint, filename }` | A certificate file was added to a store |
358
- | `certificateRemoved` | `{ store, fingerprint, filename }` | A certificate file was removed from a store |
359
- | `certificateChange` | `{ store, certificate, fingerprint, filename }` | A certificate file was modified in a store |
360
- | `crlAdded` | `{ store, filename }` | A CRL file was added |
361
- | `crlRemoved` | `{ store, filename }` | A CRL file was removed |
362
-
363
- `store` is one of `"trusted"`, `"rejected"`, `"issuersCerts"` (for certificate events) or `"crl"`, `"issuersCrl"` (for CRL events).
364
-
365
- ```typescript
366
- cm.on("certificateAdded", ({ store, fingerprint, filename }) => {
367
- console.log(`New certificate in ${store}: ${fingerprint}`);
222
+ // Intermediate CA (3-step workflow)
223
+ const intCA = new CertificateAuthority({
224
+ keySize: 2048,
225
+ location: "./my_intermediate_ca",
226
+ subject: "/CN=My Intermediate CA",
368
227
  });
228
+ const result = await intCA.initializeCSR(); // Step 1
229
+ await rootCA.signCACertificateRequest( // Step 2
230
+ certFile, result.csrPath, { validity: 3650 }
231
+ );
232
+ await intCA.installCACertificate(certFile); // Step 3
369
233
  ```
370
234
 
371
235
  ## References