@certchip/signer 0.1.11

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/README.md ADDED
@@ -0,0 +1,587 @@
1
+ # @certchip/signer
2
+
3
+ Cross-platform code and document signing CLI tool with SSH key authentication.
4
+
5
+ ## Features
6
+
7
+ - **Cross-platform** - Windows, Linux, macOS (x64, arm64)
8
+ - **SSH Key Authentication** - Ed25519, ECDSA, RSA support
9
+ - **Code Signing** - PE executables (EXE, DLL, SYS, OCX), MSI, MSP, CAB
10
+ - **Document Signing** - PDF with visual signature (watermark, box, barcode, QR code)
11
+ - **Script Signing** - PowerShell, VBScript with Authenticode
12
+ - **Text/Source Signing** - JS, Python, Go, Rust, and more
13
+ - **Hash-based Signing** - Default mode: only hash sent to server, not the file
14
+ - **Windows KSP** - Native Windows crypto integration (Windows only)
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ # Global installation (recommended)
20
+ npm install -g @certchip/signer
21
+
22
+ # Or local installation
23
+ npm install @certchip/signer
24
+ ```
25
+
26
+ ## Quick Start
27
+
28
+ ### signercli (Cross-platform: Windows, Linux, macOS)
29
+
30
+ ```bash
31
+ # Login with SSH key authentication
32
+ signercli -login https://signer.example.com username
33
+
34
+ # Sign a file
35
+ signercli myapp.exe
36
+
37
+ # Verify signature
38
+ signercli -verify myapp.exe
39
+
40
+ # Logout
41
+ signercli -logout
42
+ ```
43
+
44
+ ### signer (Windows only - KSP integration)
45
+
46
+ ```bash
47
+ # Login (certificate is installed to Windows certificate store)
48
+ signer -login https://signer.example.com username
49
+
50
+ # Sign using Windows signtool.exe
51
+ signtool sign /n "Your Certificate CN" /fd sha256 /tr http://timestamp.digicert.com /td sha256 myapp.exe
52
+
53
+ # Or sign directly with signer
54
+ signer myapp.exe
55
+
56
+ # Logout (removes certificate from store)
57
+ signer -logout
58
+ ```
59
+
60
+ ### With local installation (npx)
61
+
62
+ ```bash
63
+ # signercli (Cross-platform)
64
+ npx signercli -login https://signer.example.com username
65
+ npx signercli myapp.exe
66
+
67
+ # signer (Windows only)
68
+ npx signer -login https://signer.example.com username
69
+ npx signer myapp.exe
70
+ ```
71
+
72
+ ## CLI Commands
73
+
74
+ This package provides two CLI tools with different purposes:
75
+
76
+ | | signercli | signer |
77
+ |---|-----------|--------|
78
+ | **Purpose** | Direct file signing | Windows signtool integration |
79
+ | **Platform** | Windows, Linux, macOS | Windows only |
80
+ | **How it works** | Signs files directly via server API | Provides certificates to Windows crypto system |
81
+ | **Best for** | CI/CD, cross-platform, simple signing | Windows developers using signtool.exe |
82
+
83
+ ### When to use which tool?
84
+
85
+ | Scenario | Recommended |
86
+ |----------|-------------|
87
+ | CI/CD pipeline (any platform) | signercli |
88
+ | Linux/macOS development | signercli |
89
+ | Simple file signing | signercli |
90
+ | Using Windows signtool.exe | signer |
91
+ | Windows certificate store integration | signer |
92
+ | Visual Studio post-build signing | signercli or signer |
93
+
94
+ ---
95
+
96
+ ### signercli (Cross-platform)
97
+
98
+ The main CLI tool for code and document signing. Signs files directly by communicating with the signing server. Works on all platforms without any additional setup.
99
+
100
+ #### Authentication
101
+
102
+ ```bash
103
+ # Auto-detect authentication (SSH key if exists, otherwise password prompt)
104
+ signercli -login <url> [username]
105
+
106
+ # SSH Key Authentication (explicit)
107
+ signercli -login <url> -key ~/.ssh/id_ed25519
108
+ signercli -login <url> username -key ~/.ssh/id_rsa
109
+
110
+ # Password Authentication
111
+ signercli -login <url> -user <userid> # Password prompted interactively
112
+ signercli -login <url> -user <userid> -pw <password> # Password on command line
113
+
114
+ # Login Options
115
+ -profile <name> Use specific config profile
116
+ -expires <time> Token expiration (e.g., 24h, 7d, 1w)
117
+ -cert-id <id> Pre-specify certificate ID
118
+ -cert-serial <sn> Pre-specify certificate serial number
119
+ -include-chain Include certificate chain in token
120
+
121
+ # Logout
122
+ signercli -logout [url]
123
+ ```
124
+
125
+ #### File Signing
126
+
127
+ ```bash
128
+ # Basic signing (default: hash-only mode)
129
+ signercli <file>
130
+
131
+ # Signing options
132
+ signercli <file> -o <output> # Specify output file
133
+ signercli <file> -save-signed # Save with _signed suffix (preserve original)
134
+ signercli <file> -hash-only # Hash-based signing (default)
135
+ signercli <file> -file-upload # Upload entire file to server
136
+ signercli <file> -hash-algorithm <alg> # sha256, sha384, sha512
137
+ signercli <file> -timestamp-url <url> # Timestamp server URL
138
+ signercli <file> -profile <name> # Use config profile
139
+ ```
140
+
141
+ > **Note:** Hash-only signing is the default mode. Only the file hash is sent to the server, not the entire file.
142
+
143
+ #### Signature Verification
144
+
145
+ ```bash
146
+ signercli -verify <file>
147
+ signercli -verify <file> -signature-id <id> # Verify specific signature
148
+ signercli -verify <file> -profile <name>
149
+ ```
150
+
151
+ #### Certificate Management
152
+
153
+ ```bash
154
+ # List available certificates
155
+ signercli -codesign-list
156
+
157
+ # Get/Set certificate ID
158
+ signercli -codesign-id # Show current certificate ID
159
+ signercli -codesign-id <id> # Set certificate ID
160
+
161
+ # Get certificate PEM
162
+ signercli -codesign-cert # Fetch current certificate
163
+ signercli -codesign-cert -id <id> # Fetch specific certificate
164
+ signercli -codesign-cert -o cert.pem # Save to file
165
+
166
+ # Set private key password (for password-protected keys on server)
167
+ signercli -codesign-set <password>
168
+ ```
169
+
170
+ #### Configuration
171
+
172
+ Profiles store connection settings. The `default` profile is used when no profile is specified. Other profiles inherit missing settings from `default`.
173
+
174
+ ```bash
175
+ # View configuration
176
+ signercli -config # Show config file
177
+ signercli -config list # List all profiles
178
+ signercli -config show <name> # Show profile details
179
+
180
+ # Create/Update profile
181
+ signercli -config set <name> [options]
182
+
183
+ # Delete profile
184
+ signercli -config delete <name>
185
+ ```
186
+
187
+ **Profile Inheritance Example:**
188
+
189
+ ```bash
190
+ # Set common settings in 'default' profile
191
+ signercli -config set default -host https://signer.example.com -username admin
192
+
193
+ # Create 'production' profile (inherits host and username from default)
194
+ signercli -config set production -cert-id prod-cert-001
195
+
196
+ # Create 'staging' profile with different host (overrides default)
197
+ signercli -config set staging -host https://staging.example.com -cert-id staging-cert
198
+
199
+ # Usage
200
+ signercli -login # Uses 'default' profile
201
+ signercli -login -profile production # Uses 'production' (inherits from default)
202
+ signercli -login -profile staging # Uses 'staging' (overrides host)
203
+ ```
204
+
205
+ **Profile Options:**
206
+
207
+ | Option | Description |
208
+ |--------|-------------|
209
+ | `-host <url>` | Server URL |
210
+ | `-ssh-key-path <path>` | SSH private key path |
211
+ | `-username <name>` | SSH username |
212
+ | `-user <id>` | Password auth user ID |
213
+ | `-cert-id <id>` | Default certificate ID |
214
+ | `-cert-serial <sn>` | Certificate serial number |
215
+ | `-expires <time>` | Token expiration (24h, 7d, 1w) |
216
+ | `-include-chain` | Include certificate chain |
217
+ | `-timestamp-url <url>` | Timestamp server URL |
218
+ | `-hash-algorithm <alg>` | Default hash algorithm |
219
+
220
+ **Document Signing Options:**
221
+
222
+ | Option | Description |
223
+ |--------|-------------|
224
+ | `-doc-style <style>` | watermark, box, barcode, qrcode |
225
+ | `-doc-position <pos>` | bottom-right, bottom-left, top-right, top-left, center |
226
+ | `-doc-sig-position <pos>` | left, center, right (for barcode/qrcode) |
227
+ | `-doc-font-size <size>` | Font size for signature |
228
+ | `-doc-opacity <value>` | Opacity (0.0-1.0 or 0-100) |
229
+
230
+ #### Windows DLL Installation
231
+
232
+ ```bash
233
+ # Install DLLs to System32 (auto-requests UAC elevation)
234
+ signercli -install
235
+
236
+ # Remove DLLs from System32
237
+ signercli -uninstall
238
+ ```
239
+
240
+ #### Help & Debugging
241
+
242
+ ```bash
243
+ signercli -help
244
+ signercli -version
245
+
246
+ # Log levels (append to any command for debugging)
247
+ signercli <file> LOG_DBG # Debug output
248
+ signercli <file> LOG_INF # Info output
249
+ # Available: LOG_NON, LOG_ERR, LOG_WRN, LOG_DBG, LOG_INF
250
+ ```
251
+
252
+ ---
253
+
254
+ ### signer (Windows only)
255
+
256
+ Windows-specific tool that integrates with the Windows cryptographic system via KSP (Key Storage Provider). Instead of signing files directly, it registers certificates in the Windows certificate store, allowing you to use standard Windows tools like `signtool.exe`.
257
+
258
+ **How it works:**
259
+ 1. Login fetches your certificate from the server
260
+ 2. Certificate is registered in Windows certificate store
261
+ 3. KSP provider enables private key operations via the server
262
+ 4. Use `signtool.exe` or other Windows signing tools normally
263
+
264
+ ```bash
265
+ # Authentication (fetches certificate to Windows store)
266
+ signer -login <url> [username] # Login and register certificate
267
+ signer -logout # Logout and remove certificate
268
+ signer -list # List available certificates
269
+
270
+ # KSP Provider Management
271
+ signer -register # Register Certchip KSP provider
272
+ signer -unregister # Unregister KSP provider
273
+ signer -enum # List all crypto providers
274
+ signer -container # List key containers
275
+
276
+ # DLL Installation (auto-requests UAC elevation)
277
+ signer -install # Install DLLs to System32
278
+ signer -uninstall # Remove DLLs from System32
279
+
280
+ # After login, use standard Windows signing tools
281
+ signtool sign /n "Certificate Name" /fd sha256 myapp.exe
282
+ signtool sign /sha1 <thumbprint> /fd sha256 /tr http://timestamp.digicert.com myapp.exe
283
+ ```
284
+
285
+ ---
286
+
287
+ ### DLL System Installation (Windows)
288
+
289
+ For system-wide DLL access (required for signtool integration), install DLLs to System32:
290
+
291
+ ```bash
292
+ # Both tools auto-request UAC elevation when needed
293
+ signercli -install
294
+ signer -install
295
+
296
+ # To remove DLLs from System32
297
+ signercli -uninstall
298
+ signer -uninstall
299
+ ```
300
+
301
+ **What gets installed:**
302
+ - `otpkey.dll` → `C:\Windows\System32\otpkey.dll`
303
+ - `Certchip.dll` → `C:\Windows\System32\Certchip.dll`
304
+
305
+ **When to use:**
306
+ - When using `signtool.exe` with the Certchip KSP provider
307
+ - When other applications need to access the DLLs
308
+ - For system-wide certificate store integration
309
+
310
+ ## Node.js API
311
+
312
+ ```javascript
313
+ const signer = require('@certchip/signer');
314
+
315
+ // Login
316
+ await signer.login('https://signer.example.com', 'username', {
317
+ keyPath: '~/.ssh/id_ed25519',
318
+ expires: '24h',
319
+ certId: 'abc123',
320
+ includeChain: true
321
+ });
322
+
323
+ // Sign a file
324
+ await signer.sign('myapp.exe', {
325
+ output: 'myapp_signed.exe',
326
+ hashAlgorithm: 'sha256',
327
+ timestampUrl: 'http://timestamp.digicert.com'
328
+ });
329
+
330
+ // Verify signature
331
+ const result = await signer.verify('myapp_signed.exe');
332
+ console.log(result.valid ? 'Valid' : 'Invalid');
333
+
334
+ // Configuration
335
+ await signer.config('set', 'production', {
336
+ host: 'https://signer.example.com',
337
+ username: 'john',
338
+ expires: '12h'
339
+ });
340
+
341
+ // List certificates
342
+ const certs = await signer.listCertificates();
343
+
344
+ // Logout
345
+ await signer.logout();
346
+ ```
347
+
348
+ ## Configuration Profiles
349
+
350
+ Create reusable signing profiles:
351
+
352
+ ```bash
353
+ # Create a code signing profile
354
+ signercli -config set production \
355
+ -host https://signer.example.com \
356
+ -username john \
357
+ -ssh-key-path ~/.ssh/id_ed25519 \
358
+ -expires 24h \
359
+ -cert-id abc123 \
360
+ -include-chain \
361
+ -timestamp-url http://timestamp.digicert.com \
362
+ -hash-algorithm sha256
363
+
364
+ # Create a document signing profile with visual signature
365
+ signercli -config set pdf-signing \
366
+ -host https://signer.example.com \
367
+ -username john \
368
+ -doc-style qrcode \
369
+ -doc-position bottom-right \
370
+ -doc-sig-position center \
371
+ -doc-font-size 12 \
372
+ -doc-opacity 0.8
373
+
374
+ # Use the profile
375
+ signercli -login -profile production
376
+ signercli myapp.exe -profile production
377
+ signercli document.pdf -profile pdf-signing
378
+ ```
379
+
380
+ ## Supported File Types
381
+
382
+ | Type | Extensions | Method |
383
+ |------|------------|--------|
384
+ | **Code** | .exe, .dll, .sys, .ocx, .msi, .msp, .cab | Authenticode |
385
+ | **Document** | .pdf | Server-based with visual signature |
386
+ | **Script** | .ps1, .vbs | Authenticode |
387
+ | **Java** | .jar | JAR signing |
388
+ | **Text/Source** | .js, .ts, .py, .java, .c, .cpp, .go, .rs, .sh, .yml, .json, .xml, .html, .sql, .md, .txt | Embedded signature |
389
+
390
+ ## Package Contents
391
+
392
+ ```
393
+ @certchip/signer/
394
+ ├── lib/
395
+ │ ├── index.js # Node.js API
396
+ │ └── index.d.ts # TypeScript definitions
397
+ └── bin/
398
+ ├── signercli.js # Cross-platform wrapper
399
+ ├── signer.js # Windows KSP wrapper
400
+ └── win32-x64/
401
+ ├── signercli.exe # 9.3 MB (static build)
402
+ ├── signer.exe # 420 KB (static build)
403
+ ├── otpkey.dll # 6.2 MB (static linked)
404
+ └── Certchip.dll # 700 KB (KSP provider)
405
+ ```
406
+
407
+ ### Binary Comparison
408
+
409
+ | | signercli | signer |
410
+ |---|-----------|--------|
411
+ | **Platform** | Windows, Linux, macOS | Windows only |
412
+ | **Dependencies** | None (static build) | otpkey.dll, Certchip.dll |
413
+ | **Size** | 9.3 MB | 420 KB + 6.9 MB DLLs |
414
+ | **Signing method** | Direct (via server API) | Indirect (via Windows crypto) |
415
+ | **Windows KSP** | No | Yes |
416
+ | **signtool compatible** | No | Yes |
417
+ | **UAC auto-elevation** | Yes | Yes |
418
+ | **Use case** | CI/CD, cross-platform | Windows developers, signtool |
419
+
420
+ ## Examples
421
+
422
+ ### CI/CD Pipeline
423
+
424
+ ```bash
425
+ #!/bin/bash
426
+ set -e
427
+
428
+ # Login
429
+ signercli -login "$SIGNER_URL" "$SIGNER_USER" -key "$SSH_KEY_PATH"
430
+
431
+ # Sign all executables
432
+ for exe in dist/*.exe; do
433
+ signercli "$exe"
434
+ done
435
+
436
+ # Logout
437
+ signercli -logout
438
+ ```
439
+
440
+ ### TypeScript Usage
441
+
442
+ ```typescript
443
+ import * as signer from '@certchip/signer';
444
+
445
+ async function signRelease() {
446
+ await signer.login('https://signer.example.com', 'username');
447
+
448
+ const result = await signer.sign('app.exe', {
449
+ output: 'app_signed.exe',
450
+ hashAlgorithm: 'sha256'
451
+ });
452
+
453
+ if (result.success) {
454
+ console.log('Signed:', result.outputPath);
455
+ }
456
+
457
+ await signer.logout();
458
+ }
459
+ ```
460
+
461
+ ### Document Signing with Visual Signature
462
+
463
+ ```bash
464
+ # Login
465
+ signercli -login https://signer.example.com username
466
+
467
+ # Sign PDF with QR code signature
468
+ signercli document.pdf \
469
+ -doc-style qrcode \
470
+ -doc-position bottom-right \
471
+ -o document_signed.pdf
472
+
473
+ # Sign PDF with watermark
474
+ signercli contract.pdf \
475
+ -doc-style watermark \
476
+ -doc-position center \
477
+ -doc-opacity 0.3
478
+ ```
479
+
480
+ ### Windows KSP with signtool
481
+
482
+ ```batch
483
+ REM First-time setup (run as Administrator):
484
+ REM 1. Install DLLs to System32
485
+ signercli -install
486
+
487
+ REM 2. Register KSP provider
488
+ signer -register
489
+
490
+ REM Daily usage:
491
+ REM Login to get certificate
492
+ signer -login https://signer.example.com username
493
+
494
+ REM Sign with signtool
495
+ signtool sign /n "Your Certificate" /fd sha256 /tr http://timestamp.digicert.com myapp.exe
496
+
497
+ REM Verify
498
+ signtool verify /pa myapp.exe
499
+
500
+ REM Logout
501
+ signer -logout
502
+
503
+ REM To uninstall:
504
+ signer -unregister
505
+ signercli -uninstall
506
+ ```
507
+
508
+ ## Authentication
509
+
510
+ ### SSH Key (Recommended)
511
+
512
+ ```bash
513
+ # Generate Ed25519 key (if needed)
514
+ ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519
515
+
516
+ # Login with default key
517
+ signercli -login https://server.com username
518
+
519
+ # Login with specific key
520
+ signercli -login https://server.com username -key ~/.ssh/id_rsa
521
+ ```
522
+
523
+ ### Password
524
+
525
+ ```bash
526
+ signercli -login https://server.com -user admin -pw password
527
+ ```
528
+
529
+ ## Environment Variables
530
+
531
+ | Variable | Description |
532
+ |----------|-------------|
533
+ | `SIGNER_URL` | Default server URL |
534
+ | `SIGNER_USER` | Default username |
535
+ | `SIGNER_KEY_PATH` | Default SSH key path |
536
+ | `SIGNER_CERT_ID` | Default certificate ID |
537
+
538
+ ## Troubleshooting
539
+
540
+ ### "signer command is only available on Windows"
541
+
542
+ The `signer` command requires Windows KSP integration. Use `signercli` for cross-platform signing.
543
+
544
+ ### "DLL not found"
545
+
546
+ The `signer.exe` requires `otpkey.dll` and `Certchip.dll`. These are automatically included when installed via npm. No additional MSYS2 or system DLLs are required.
547
+
548
+ For signtool integration or system-wide access, install DLLs to System32:
549
+ ```bash
550
+ signercli -install
551
+ ```
552
+
553
+ ### "Access denied" during installation
554
+
555
+ The `-install` command requires Administrator privileges. Both `signercli` and `signer` will automatically request UAC elevation. If the UAC prompt is cancelled, run the command prompt as Administrator.
556
+
557
+ ### "Token expired"
558
+
559
+ Login again:
560
+ ```bash
561
+ signercli -login https://server.com username
562
+ ```
563
+
564
+ ### "Permission denied"
565
+
566
+ On Linux/macOS, ensure the binary is executable:
567
+ ```bash
568
+ chmod +x node_modules/@certchip/signer/bin/*/signercli
569
+ ```
570
+
571
+ ## Requirements
572
+
573
+ - **Node.js** >= 14.0.0
574
+ - **Platforms**: Windows x64 (Linux x64/arm64, macOS x64/arm64 coming soon)
575
+ - **Server**: Certchip Signer API compatible server
576
+
577
+ > **Note:** The current npm package includes Windows x64 binaries only. Linux and macOS binaries will be added in a future release.
578
+
579
+ ## License
580
+
581
+ Copyright (c) 2025 Certchip. All rights reserved.
582
+
583
+ ## Links
584
+
585
+ - [Homepage](https://certchip.com/signer)
586
+ - [Documentation](https://certchip.com/signer/help)
587
+ - [Issues](https://github.com/certchip/signer-cli/issues)
package/bin/signer.js ADDED
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ const { spawn } = require('child_process');
3
+ const path = require('path');
4
+ const os = require('os');
5
+
6
+ function getBinaryPath(name) {
7
+ const platform = os.platform();
8
+ if (platform !== 'win32') {
9
+ console.error('signer command is only available on Windows (Windows KSP integration)');
10
+ console.error('Use signercli for cross-platform code signing');
11
+ process.exit(1);
12
+ }
13
+ return path.join(__dirname, 'win32-x64', name + '.exe');
14
+ }
15
+
16
+ const binary = getBinaryPath('signer');
17
+ const child = spawn(binary, process.argv.slice(2), { stdio: 'inherit', windowsHide: true });
18
+ child.on('close', code => process.exit(code || 0));
19
+ child.on('error', err => { console.error(err.message); process.exit(1); });
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env node
2
+ const { spawn } = require('child_process');
3
+ const path = require('path');
4
+ const os = require('os');
5
+
6
+ function getBinaryPath(name) {
7
+ const platform = os.platform();
8
+ const arch = os.arch();
9
+ let platformId = platform === 'win32' ? 'win32' : platform === 'darwin' ? 'darwin' : 'linux';
10
+ let archId = arch === 'x64' || arch === 'x86_64' ? 'x64' : 'arm64';
11
+ const ext = platform === 'win32' ? '.exe' : '';
12
+ return path.join(__dirname, platformId + '-' + archId, name + ext);
13
+ }
14
+
15
+ const binary = getBinaryPath('signercli');
16
+ const child = spawn(binary, process.argv.slice(2), { stdio: 'inherit', windowsHide: true });
17
+ child.on('close', code => process.exit(code || 0));
18
+ child.on('error', err => { console.error(err.message); process.exit(1); });
Binary file
Binary file
Binary file
Binary file
package/lib/index.d.ts ADDED
@@ -0,0 +1,122 @@
1
+ /**
2
+ * @certchip/signer - TypeScript definitions
3
+ */
4
+
5
+ export interface ExecResult {
6
+ stdout: string;
7
+ stderr: string;
8
+ code: number;
9
+ }
10
+
11
+ export interface LoginOptions {
12
+ keyPath?: string;
13
+ expires?: string;
14
+ certId?: string;
15
+ certSerial?: string;
16
+ includeChain?: boolean;
17
+ }
18
+
19
+ export interface LoginResult {
20
+ success: boolean;
21
+ message: string;
22
+ code: number;
23
+ }
24
+
25
+ export interface LogoutResult {
26
+ success: boolean;
27
+ message: string;
28
+ code: number;
29
+ }
30
+
31
+ export interface SignOptions {
32
+ output?: string;
33
+ profile?: string;
34
+ hashAlgorithm?: string;
35
+ timestampUrl?: string;
36
+ hashOnly?: boolean;
37
+ }
38
+
39
+ export interface SignResult {
40
+ success: boolean;
41
+ message: string;
42
+ code: number;
43
+ outputPath?: string;
44
+ }
45
+
46
+ export interface VerifyOptions {
47
+ signatureId?: string;
48
+ profile?: string;
49
+ }
50
+
51
+ export interface VerifyResult {
52
+ valid: boolean;
53
+ message: string;
54
+ code: number;
55
+ }
56
+
57
+ export interface ConfigOptions {
58
+ host?: string;
59
+ username?: string;
60
+ keyPath?: string;
61
+ expires?: string;
62
+ certId?: string;
63
+ timestampUrl?: string;
64
+ includeChain?: boolean;
65
+ }
66
+
67
+ export interface ConfigResult {
68
+ success: boolean;
69
+ message: string;
70
+ code: number;
71
+ }
72
+
73
+ export interface ListCertificatesResult {
74
+ success: boolean;
75
+ message: string;
76
+ code: number;
77
+ }
78
+
79
+ /**
80
+ * Execute signer command with raw arguments
81
+ */
82
+ export function exec(args: string[]): Promise<ExecResult>;
83
+
84
+ /**
85
+ * Login to signing server
86
+ */
87
+ export function login(serverUrl: string, username?: string, options?: LoginOptions): Promise<LoginResult>;
88
+
89
+ /**
90
+ * Logout from signing server
91
+ */
92
+ export function logout(serverUrl?: string): Promise<LogoutResult>;
93
+
94
+ /**
95
+ * Sign a file
96
+ */
97
+ export function sign(filePath: string, options?: SignOptions): Promise<SignResult>;
98
+
99
+ /**
100
+ * Verify signature on a file
101
+ */
102
+ export function verify(filePath: string, options?: VerifyOptions): Promise<VerifyResult>;
103
+
104
+ /**
105
+ * Get or set configuration
106
+ */
107
+ export function config(action?: string, profile?: string, options?: ConfigOptions): Promise<ConfigResult>;
108
+
109
+ /**
110
+ * List available certificates
111
+ */
112
+ export function listCertificates(): Promise<ListCertificatesResult>;
113
+
114
+ /**
115
+ * Get version information
116
+ */
117
+ export function version(): Promise<string>;
118
+
119
+ /**
120
+ * Get the path to the signer binary
121
+ */
122
+ export function getBinaryPath(): string;
package/lib/index.js ADDED
@@ -0,0 +1,285 @@
1
+ /**
2
+ * @certchip/signercli - Node.js API
3
+ *
4
+ * Provides programmatic access to the signercli CLI functionality.
5
+ *
6
+ * @example
7
+ * const signercli = require('@certchip/signercli');
8
+ *
9
+ * // Login with SSH key
10
+ * await signercli.login('https://server.com', 'username');
11
+ *
12
+ * // Sign a file
13
+ * await signercli.sign('myapp.exe');
14
+ *
15
+ * // Verify signature
16
+ * const result = await signercli.verify('myapp.exe');
17
+ *
18
+ * // Logout
19
+ * await signercli.logout();
20
+ */
21
+
22
+ 'use strict';
23
+
24
+ const { spawn } = require('child_process');
25
+ const path = require('path');
26
+ const os = require('os');
27
+
28
+ /**
29
+ * Get the path to the signer binary
30
+ */
31
+ function getBinaryPath() {
32
+ const platform = os.platform();
33
+ const arch = os.arch();
34
+
35
+ let platformId;
36
+ switch (platform) {
37
+ case 'win32': platformId = 'win32'; break;
38
+ case 'linux': platformId = 'linux'; break;
39
+ case 'darwin': platformId = 'darwin'; break;
40
+ default: throw new Error(`Unsupported platform: ${platform}`);
41
+ }
42
+
43
+ let archId;
44
+ switch (arch) {
45
+ case 'x64':
46
+ case 'x86_64':
47
+ archId = 'x64';
48
+ break;
49
+ case 'arm64':
50
+ case 'aarch64':
51
+ archId = 'arm64';
52
+ break;
53
+ default:
54
+ throw new Error(`Unsupported architecture: ${arch}`);
55
+ }
56
+
57
+ const binaryName = platform === 'win32' ? 'signercli.exe' : 'signercli';
58
+ return path.join(__dirname, '..', 'bin', `${platformId}-${archId}`, binaryName);
59
+ }
60
+
61
+ /**
62
+ * Execute signer command
63
+ * @param {string[]} args - Command line arguments
64
+ * @returns {Promise<{stdout: string, stderr: string, code: number}>}
65
+ */
66
+ function exec(args) {
67
+ return new Promise((resolve, reject) => {
68
+ const binary = getBinaryPath();
69
+ const proc = spawn(binary, args, {
70
+ stdio: ['inherit', 'pipe', 'pipe'],
71
+ windowsHide: true
72
+ });
73
+
74
+ let stdout = '';
75
+ let stderr = '';
76
+
77
+ proc.stdout.on('data', (data) => {
78
+ stdout += data.toString();
79
+ });
80
+
81
+ proc.stderr.on('data', (data) => {
82
+ stderr += data.toString();
83
+ });
84
+
85
+ proc.on('close', (code) => {
86
+ resolve({ stdout, stderr, code: code || 0 });
87
+ });
88
+
89
+ proc.on('error', (error) => {
90
+ reject(error);
91
+ });
92
+ });
93
+ }
94
+
95
+ /**
96
+ * Login to signing server
97
+ * @param {string} serverUrl - Server URL
98
+ * @param {string} [username] - SSH username (optional for keyless mode)
99
+ * @param {Object} [options] - Login options
100
+ * @param {string} [options.keyPath] - SSH key path
101
+ * @param {string} [options.expires] - Token expiration (e.g., "24h")
102
+ * @param {string} [options.certId] - Certificate ID
103
+ * @param {boolean} [options.includeChain] - Include certificate chain
104
+ * @returns {Promise<{success: boolean, message: string}>}
105
+ */
106
+ async function login(serverUrl, username, options = {}) {
107
+ const args = ['-login', serverUrl];
108
+
109
+ if (username) {
110
+ args.push(username);
111
+ }
112
+ if (options.keyPath) {
113
+ args.push('-key', options.keyPath);
114
+ }
115
+ if (options.expires) {
116
+ args.push('-expires', options.expires);
117
+ }
118
+ if (options.certId) {
119
+ args.push('-cert-id', options.certId);
120
+ }
121
+ if (options.includeChain) {
122
+ args.push('-include-chain');
123
+ }
124
+
125
+ const result = await exec(args);
126
+ return {
127
+ success: result.code === 0,
128
+ message: result.stdout || result.stderr,
129
+ code: result.code
130
+ };
131
+ }
132
+
133
+ /**
134
+ * Logout from signing server
135
+ * @param {string} [serverUrl] - Server URL (optional)
136
+ * @returns {Promise<{success: boolean, message: string}>}
137
+ */
138
+ async function logout(serverUrl) {
139
+ const args = ['-logout'];
140
+ if (serverUrl) {
141
+ args.push(serverUrl);
142
+ }
143
+
144
+ const result = await exec(args);
145
+ return {
146
+ success: result.code === 0,
147
+ message: result.stdout || result.stderr,
148
+ code: result.code
149
+ };
150
+ }
151
+
152
+ /**
153
+ * Sign a file
154
+ * @param {string} filePath - Path to file to sign
155
+ * @param {Object} [options] - Signing options
156
+ * @param {string} [options.output] - Output file path
157
+ * @param {string} [options.profile] - Config profile name
158
+ * @param {string} [options.hashAlgorithm] - Hash algorithm
159
+ * @param {string} [options.timestampUrl] - Timestamp server URL
160
+ * @param {boolean} [options.hashOnly] - Only compute hash
161
+ * @returns {Promise<{success: boolean, message: string, outputPath?: string}>}
162
+ */
163
+ async function sign(filePath, options = {}) {
164
+ const args = [filePath];
165
+
166
+ if (options.output) {
167
+ args.push('-o', options.output);
168
+ }
169
+ if (options.profile) {
170
+ args.push('-profile', options.profile);
171
+ }
172
+ if (options.hashAlgorithm) {
173
+ args.push('-hash-algorithm', options.hashAlgorithm);
174
+ }
175
+ if (options.timestampUrl) {
176
+ args.push('-timestamp-url', options.timestampUrl);
177
+ }
178
+ if (options.hashOnly) {
179
+ args.push('-hash-only');
180
+ }
181
+
182
+ const result = await exec(args);
183
+ return {
184
+ success: result.code === 0,
185
+ message: result.stdout || result.stderr,
186
+ code: result.code,
187
+ outputPath: options.output
188
+ };
189
+ }
190
+
191
+ /**
192
+ * Verify signature on a file
193
+ * @param {string} filePath - Path to file to verify
194
+ * @param {Object} [options] - Verification options
195
+ * @param {string} [options.signatureId] - Specific signature ID
196
+ * @param {string} [options.profile] - Config profile name
197
+ * @returns {Promise<{valid: boolean, message: string, details?: Object}>}
198
+ */
199
+ async function verify(filePath, options = {}) {
200
+ const args = ['-verify', filePath];
201
+
202
+ if (options.signatureId) {
203
+ args.push('-signature-id', options.signatureId);
204
+ }
205
+ if (options.profile) {
206
+ args.push('-profile', options.profile);
207
+ }
208
+
209
+ const result = await exec(args);
210
+ return {
211
+ valid: result.code === 0,
212
+ message: result.stdout || result.stderr,
213
+ code: result.code
214
+ };
215
+ }
216
+
217
+ /**
218
+ * Get or set configuration
219
+ * @param {string} [action] - Config action (list, show, set, delete)
220
+ * @param {string} [profile] - Profile name
221
+ * @param {Object} [options] - Config options for set action
222
+ * @returns {Promise<{success: boolean, message: string}>}
223
+ */
224
+ async function config(action, profile, options = {}) {
225
+ const args = ['-config'];
226
+
227
+ if (action) {
228
+ args.push(action);
229
+ }
230
+ if (profile) {
231
+ args.push(profile);
232
+ }
233
+
234
+ // Add options for 'set' action
235
+ if (action === 'set' && options) {
236
+ if (options.host) args.push('-host', options.host);
237
+ if (options.username) args.push('-username', options.username);
238
+ if (options.keyPath) args.push('-ssh-key-path', options.keyPath);
239
+ if (options.expires) args.push('-expires', options.expires);
240
+ if (options.certId) args.push('-cert-id', options.certId);
241
+ if (options.timestampUrl) args.push('-timestamp-url', options.timestampUrl);
242
+ if (options.includeChain) args.push('-include-chain');
243
+ }
244
+
245
+ const result = await exec(args);
246
+ return {
247
+ success: result.code === 0,
248
+ message: result.stdout || result.stderr,
249
+ code: result.code
250
+ };
251
+ }
252
+
253
+ /**
254
+ * List available certificates
255
+ * @returns {Promise<{success: boolean, certificates: Array}>}
256
+ */
257
+ async function listCertificates() {
258
+ const result = await exec(['-codesign-list']);
259
+ return {
260
+ success: result.code === 0,
261
+ message: result.stdout || result.stderr,
262
+ code: result.code
263
+ };
264
+ }
265
+
266
+ /**
267
+ * Get version information
268
+ * @returns {Promise<string>}
269
+ */
270
+ async function version() {
271
+ const result = await exec(['-version']);
272
+ return result.stdout.trim();
273
+ }
274
+
275
+ module.exports = {
276
+ exec,
277
+ login,
278
+ logout,
279
+ sign,
280
+ verify,
281
+ config,
282
+ listCertificates,
283
+ version,
284
+ getBinaryPath
285
+ };
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@certchip/signer",
3
+ "version": "0.1.11",
4
+ "description": "Cross-platform code and document signing CLI tool",
5
+ "main": "lib/index.js",
6
+ "types": "lib/index.d.ts",
7
+ "bin": {
8
+ "signercli": "./bin/signercli.js",
9
+ "signer": "./bin/signer.js"
10
+ },
11
+ "scripts": {
12
+ "test": "echo \"Error: no test specified\" && exit 1",
13
+ "version:generate": "node scripts/generate-version.js",
14
+ "prebuild": "npm run version:generate",
15
+ "prepack": "npm run version:generate"
16
+ },
17
+ "files": [
18
+ "bin/",
19
+ "lib/",
20
+ "README.md"
21
+ ],
22
+ "keywords": [
23
+ "code-signing",
24
+ "digital-signature",
25
+ "authenticode",
26
+ "pdf-signing",
27
+ "document-signing",
28
+ "cli",
29
+ "cross-platform"
30
+ ],
31
+ "author": "Certchip <support@certchip.com>",
32
+ "license": "MIT",
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "git+https://github.com/certchip/signer-cli.git"
36
+ },
37
+ "bugs": {
38
+ "url": "https://github.com/certchip/signer-cli/issues"
39
+ },
40
+ "homepage": "https://certchip.com/signer",
41
+ "engines": {
42
+ "node": ">=14.0.0"
43
+ },
44
+ "os": [
45
+ "win32",
46
+ "linux",
47
+ "darwin"
48
+ ],
49
+ "cpu": [
50
+ "x64",
51
+ "arm64"
52
+ ]
53
+ }