ksef-client-ts 0.7.2-alpha.0 → 0.8.0-alpha.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/dist/cli.js +44 -39
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +48 -38
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -10
- package/dist/index.d.ts +20 -10
- package/dist/index.js +47 -38
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2453,20 +2453,13 @@ var init_cryptography_service = __esm({
|
|
|
2453
2453
|
* Generate a random AES-256 key and IV, then wrap the key with the
|
|
2454
2454
|
* SymmetricKeyEncryption certificate's RSA public key (RSA-OAEP SHA-256).
|
|
2455
2455
|
*/
|
|
2456
|
-
getEncryptionData() {
|
|
2456
|
+
async getEncryptionData() {
|
|
2457
2457
|
const key = crypto.randomBytes(32);
|
|
2458
2458
|
const iv = crypto.randomBytes(16);
|
|
2459
2459
|
const certPem = this.fetcher.getSymmetricKeyEncryptionPem();
|
|
2460
|
-
const encryptedKey =
|
|
2461
|
-
{
|
|
2462
|
-
key: this.extractSpkiPem(certPem),
|
|
2463
|
-
oaepHash: "sha256",
|
|
2464
|
-
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING
|
|
2465
|
-
},
|
|
2466
|
-
key
|
|
2467
|
-
);
|
|
2460
|
+
const encryptedKey = await this.rsaOaepEncrypt(certPem, new Uint8Array(key));
|
|
2468
2461
|
const encryptionInfo = {
|
|
2469
|
-
encryptedSymmetricKey: encryptedKey.toString("base64"),
|
|
2462
|
+
encryptedSymmetricKey: Buffer.from(encryptedKey).toString("base64"),
|
|
2470
2463
|
initializationVector: iv.toString("base64")
|
|
2471
2464
|
};
|
|
2472
2465
|
return {
|
|
@@ -2491,14 +2484,14 @@ var init_cryptography_service = __esm({
|
|
|
2491
2484
|
* The EC variant outputs bytes in the Java-compatible format:
|
|
2492
2485
|
* `[ephemeralSPKI | nonce(12) | ciphertext+tag]`.
|
|
2493
2486
|
*/
|
|
2494
|
-
encryptKsefToken(token, challengeTimestamp) {
|
|
2487
|
+
async encryptKsefToken(token, challengeTimestamp) {
|
|
2495
2488
|
const timestampMs = new Date(challengeTimestamp).getTime();
|
|
2496
2489
|
const plaintext = Buffer.from(`${token}|${timestampMs}`, "utf-8");
|
|
2497
2490
|
const certPem = this.fetcher.getKsefTokenEncryptionPem();
|
|
2498
2491
|
const cert = new crypto.X509Certificate(certPem);
|
|
2499
2492
|
const publicKey = cert.publicKey;
|
|
2500
2493
|
if (publicKey.asymmetricKeyType === "rsa") {
|
|
2501
|
-
return this.
|
|
2494
|
+
return this.rsaOaepEncrypt(certPem, plaintext);
|
|
2502
2495
|
}
|
|
2503
2496
|
if (publicKey.asymmetricKeyType === "ec") {
|
|
2504
2497
|
return this.encryptEcdhAesGcm(publicKey, plaintext);
|
|
@@ -2590,27 +2583,39 @@ var init_cryptography_service = __esm({
|
|
|
2590
2583
|
// Private helpers
|
|
2591
2584
|
// ---------------------------------------------------------------------------
|
|
2592
2585
|
/**
|
|
2593
|
-
* Extract
|
|
2594
|
-
*
|
|
2595
|
-
*
|
|
2596
|
-
* to keep the library portable across runtimes.
|
|
2586
|
+
* Extract the public-key SPKI DER bytes from a CERTIFICATE PEM. Web Crypto's
|
|
2587
|
+
* `importKey('spki', ...)` consumes raw DER; we parse the cert via native
|
|
2588
|
+
* `X509Certificate` (available on Node and Deno) and export the key directly.
|
|
2597
2589
|
*/
|
|
2598
|
-
|
|
2590
|
+
spkiDerFromCert(certPem) {
|
|
2599
2591
|
const publicKey = new crypto.X509Certificate(certPem).publicKey;
|
|
2600
|
-
return publicKey.export({ type: "spki", format: "
|
|
2592
|
+
return new Uint8Array(publicKey.export({ type: "spki", format: "der" }));
|
|
2601
2593
|
}
|
|
2602
|
-
/**
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2594
|
+
/**
|
|
2595
|
+
* RSA-OAEP SHA-256 encryption via Web Crypto.
|
|
2596
|
+
*
|
|
2597
|
+
* Uses `crypto.webcrypto.subtle` instead of `node:crypto.publicEncrypt`
|
|
2598
|
+
* because Deno's Node-compat layer silently ignores the `oaepHash` option
|
|
2599
|
+
* and defaults MGF1+OAEP to SHA-1, producing ciphertext KSeF rejects as
|
|
2600
|
+
* "Invalid token encryption". Web Crypto requires the hash up-front in
|
|
2601
|
+
* `importKey` as part of the key's algorithm identity, so there is no room
|
|
2602
|
+
* for a runtime to swap it — output is identical OAEP-SHA256 on every
|
|
2603
|
+
* Web Crypto implementation (Node 18+, Deno, Bun, browsers, edge runtimes).
|
|
2604
|
+
*/
|
|
2605
|
+
async rsaOaepEncrypt(certPem, plaintext) {
|
|
2606
|
+
const key = await crypto.webcrypto.subtle.importKey(
|
|
2607
|
+
"spki",
|
|
2608
|
+
this.spkiDerFromCert(certPem),
|
|
2609
|
+
{ name: "RSA-OAEP", hash: "SHA-256" },
|
|
2610
|
+
false,
|
|
2611
|
+
["encrypt"]
|
|
2612
|
+
);
|
|
2613
|
+
const ciphertext = await crypto.webcrypto.subtle.encrypt(
|
|
2614
|
+
{ name: "RSA-OAEP" },
|
|
2615
|
+
key,
|
|
2616
|
+
plaintext
|
|
2613
2617
|
);
|
|
2618
|
+
return new Uint8Array(ciphertext);
|
|
2614
2619
|
}
|
|
2615
2620
|
/**
|
|
2616
2621
|
* ECDH (P-256) + AES-256-GCM encryption.
|
|
@@ -3061,7 +3066,7 @@ var init_offline_invoice_workflow = __esm({
|
|
|
3061
3066
|
}
|
|
3062
3067
|
if (pending.length === 0) return result;
|
|
3063
3068
|
await client.crypto.init();
|
|
3064
|
-
const encData = client.crypto.getEncryptionData();
|
|
3069
|
+
const encData = await client.crypto.getEncryptionData();
|
|
3065
3070
|
const formCode = options.formCode ?? DEFAULT_FORM_CODE;
|
|
3066
3071
|
const openResp = await client.onlineSession.openSession(
|
|
3067
3072
|
{ formCode, encryption: encData.encryptionInfo }
|
|
@@ -3150,7 +3155,7 @@ var init_offline_invoice_workflow = __esm({
|
|
|
3150
3155
|
}
|
|
3151
3156
|
const originalHash = crypto3.createHash("sha256").update(original.invoiceXml).digest("base64");
|
|
3152
3157
|
await client.crypto.init();
|
|
3153
|
-
const encData = client.crypto.getEncryptionData();
|
|
3158
|
+
const encData = await client.crypto.getEncryptionData();
|
|
3154
3159
|
const formCode = options.formCode ?? DEFAULT_FORM_CODE;
|
|
3155
3160
|
const openResp = await client.onlineSession.openSession(
|
|
3156
3161
|
{ formCode, encryption: encData.encryptionInfo }
|
|
@@ -3700,7 +3705,7 @@ var init_client = __esm({
|
|
|
3700
3705
|
async loginWithToken(token, nip) {
|
|
3701
3706
|
const challenge2 = await this.auth.getChallenge();
|
|
3702
3707
|
await this.crypto.init();
|
|
3703
|
-
const encryptedToken = this.crypto.encryptKsefToken(token, challenge2.timestamp);
|
|
3708
|
+
const encryptedToken = await this.crypto.encryptKsefToken(token, challenge2.timestamp);
|
|
3704
3709
|
const submitResult = await this.auth.submitKsefTokenAuthRequest({
|
|
3705
3710
|
challenge: challenge2.challenge,
|
|
3706
3711
|
contextIdentifier: { type: "Nip", value: nip },
|
|
@@ -6689,7 +6694,7 @@ async function uploadBatch(client, zipData, options) {
|
|
|
6689
6694
|
}
|
|
6690
6695
|
}
|
|
6691
6696
|
}
|
|
6692
|
-
const encData = client.crypto.getEncryptionData();
|
|
6697
|
+
const encData = await client.crypto.getEncryptionData();
|
|
6693
6698
|
const formCode = options?.formCode ?? DEFAULT_FORM_CODE;
|
|
6694
6699
|
const encryptFn = (part) => client.crypto.encryptAES256(part, encData.cipherKey, encData.cipherIv);
|
|
6695
6700
|
const { batchFile, encryptedParts } = BatchFileBuilder.build(zipData, encryptFn, {
|
|
@@ -6737,7 +6742,7 @@ async function uploadBatchStream(client, zipStreamFactory, zipSize, options) {
|
|
|
6737
6742
|
throw new Error("parallelism must be a positive integer");
|
|
6738
6743
|
}
|
|
6739
6744
|
await client.crypto.init();
|
|
6740
|
-
const encData = client.crypto.getEncryptionData();
|
|
6745
|
+
const encData = await client.crypto.getEncryptionData();
|
|
6741
6746
|
const formCode = options?.formCode ?? DEFAULT_FORM_CODE;
|
|
6742
6747
|
const encryptStreamFn = (stream) => client.crypto.encryptAES256Stream(stream, encData.cipherKey, encData.cipherIv);
|
|
6743
6748
|
const hashStreamFn = (stream) => client.crypto.getFileMetadataFromStream(stream);
|
|
@@ -7206,7 +7211,7 @@ init_auth_xml_builder();
|
|
|
7206
7211
|
async function authenticateWithToken(client, options) {
|
|
7207
7212
|
const challenge2 = await client.auth.getChallenge();
|
|
7208
7213
|
await client.crypto.init();
|
|
7209
|
-
const encryptedToken = client.crypto.encryptKsefToken(options.token, challenge2.timestamp);
|
|
7214
|
+
const encryptedToken = await client.crypto.encryptKsefToken(options.token, challenge2.timestamp);
|
|
7210
7215
|
const submitResult = await client.auth.submitKsefTokenAuthRequest({
|
|
7211
7216
|
challenge: challenge2.challenge,
|
|
7212
7217
|
contextIdentifier: { type: "Nip", value: options.nip },
|
|
@@ -7790,7 +7795,7 @@ var open = defineCommand3({
|
|
|
7790
7795
|
throw new Error("NIP is required. Provide --nip or set it via `ksef config set --nip <nip>`.");
|
|
7791
7796
|
}
|
|
7792
7797
|
await client.crypto.init();
|
|
7793
|
-
const encryptionData = client.crypto.getEncryptionData();
|
|
7798
|
+
const encryptionData = await client.crypto.getEncryptionData();
|
|
7794
7799
|
const formCodeKey = args.formCode;
|
|
7795
7800
|
let formCode = DEFAULT_FORM_CODE;
|
|
7796
7801
|
if (formCodeKey) {
|
|
@@ -8261,7 +8266,7 @@ function verifyHash(data, expectedHash) {
|
|
|
8261
8266
|
// src/workflows/invoice-export-workflow.ts
|
|
8262
8267
|
async function doExport(client, filters, options) {
|
|
8263
8268
|
await client.crypto.init();
|
|
8264
|
-
const encData = client.crypto.getEncryptionData();
|
|
8269
|
+
const encData = await client.crypto.getEncryptionData();
|
|
8265
8270
|
const opResp = await client.invoices.exportInvoices({
|
|
8266
8271
|
encryption: encData.encryptionInfo,
|
|
8267
8272
|
filters,
|
|
@@ -9257,7 +9262,7 @@ var send = defineCommand6({
|
|
|
9257
9262
|
}
|
|
9258
9263
|
if (!args.json) consola11.start(`Sending ${xmlFiles.length} invoices via batch session...`);
|
|
9259
9264
|
await client.crypto.init();
|
|
9260
|
-
const encryptionData = client.crypto.getEncryptionData();
|
|
9265
|
+
const encryptionData = await client.crypto.getEncryptionData();
|
|
9261
9266
|
const parts = fileBuffers.map(({ content }, i) => {
|
|
9262
9267
|
const metadata = client.crypto.getFileMetadata(new Uint8Array(content));
|
|
9263
9268
|
return {
|
|
@@ -9434,7 +9439,7 @@ var exportCmd = defineCommand6({
|
|
|
9434
9439
|
const { client } = await requireSession(globalOpts);
|
|
9435
9440
|
if (!args.json) consola11.start("Starting invoice export...");
|
|
9436
9441
|
await client.crypto.init();
|
|
9437
|
-
const encryptionData = client.crypto.getEncryptionData();
|
|
9442
|
+
const encryptionData = await client.crypto.getEncryptionData();
|
|
9438
9443
|
const filters = buildQueryFilters(args);
|
|
9439
9444
|
const result = await client.invoices.exportInvoices(
|
|
9440
9445
|
{ encryption: encryptionData.encryptionInfo, filters, onlyMetadata: args.onlyMetadata }
|