@devlas/dte-sii 2.12.9 → 2.12.10

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/SiiSession.js CHANGED
@@ -14,6 +14,8 @@ const {
14
14
  loadPfxFromBuffer,
15
15
  loadPfxFromFile,
16
16
  createTlsOptions,
17
+ createTlsAgent,
18
+ createTlsAgentFromPem,
17
19
  validateAmbiente,
18
20
  getHost,
19
21
  createScopedLogger,
@@ -44,6 +46,10 @@ class SiiSession {
44
46
  this.baseHost = getHost(this.ambiente);
45
47
  this.cookieJar = '';
46
48
  this.tlsOptions = null;
49
+ // Agent nativo con el certificado + flags TLS legacy del SII. got no reenvía
50
+ // secureOptions/maxVersion a través de su opción `https` bajo ningún nombre de
51
+ // clave, así que estos flags solo se pueden aplicar vía un https.Agent nativo.
52
+ this.tlsAgent = null;
47
53
 
48
54
  // Configurar TLS desde certificado
49
55
  if (options.certificado) {
@@ -61,15 +67,19 @@ class SiiSession {
61
67
  */
62
68
  _configureTlsFromCertificado(certificado) {
63
69
  try {
70
+ const keyPem = certificado.getPrivateKeyPEM();
71
+ const certPem = certificado.getCertificatePEM();
64
72
  this.tlsOptions = {
65
- key: certificado.getPrivateKeyPEM(),
66
- cert: certificado.getCertificatePEM(),
67
- certificate: certificado.getCertificatePEM(),
73
+ key: keyPem,
74
+ cert: certPem,
75
+ certificate: certPem,
68
76
  rejectUnauthorized: false,
69
77
  };
78
+ this.tlsAgent = createTlsAgentFromPem(keyPem, certPem);
70
79
  } catch (error) {
71
80
  log.error('Error configurando TLS desde certificado:', error.message);
72
81
  this.tlsOptions = null;
82
+ this.tlsAgent = null;
73
83
  }
74
84
  }
75
85
 
@@ -81,9 +91,11 @@ class SiiSession {
81
91
  try {
82
92
  const pfxData = loadPfxFromBuffer(pfxBuffer, password);
83
93
  this.tlsOptions = createTlsOptions(pfxData);
94
+ this.tlsAgent = createTlsAgent(pfxData);
84
95
  } catch (error) {
85
96
  log.error('Error configurando TLS desde PFX:', error.message);
86
97
  this.tlsOptions = null;
98
+ this.tlsAgent = null;
87
99
  }
88
100
  }
89
101
 
@@ -95,9 +107,11 @@ class SiiSession {
95
107
  try {
96
108
  const pfxData = loadPfxFromFile(pfxPath, password);
97
109
  this.tlsOptions = createTlsOptions(pfxData);
110
+ this.tlsAgent = createTlsAgent(pfxData);
98
111
  } catch (error) {
99
112
  log.error('Error configurando TLS desde archivo PFX:', error.message);
100
113
  this.tlsOptions = null;
114
+ this.tlsAgent = null;
101
115
  }
102
116
  }
103
117
 
@@ -215,6 +229,11 @@ class SiiSession {
215
229
  followRedirect: false,
216
230
  throwHttpErrors: false,
217
231
  https: this.tlsOptions || { rejectUnauthorized: false },
232
+ // got remapea `https` a un set fijo de claves y NO reenvía secureOptions/
233
+ // maxVersion bajo ningún nombre — sin el Agent nativo aquí, el certificado
234
+ // cliente se autentica con handshake TLS incompleto contra el SII (ver
235
+ // utils/pfx.js createTlsAgent).
236
+ ...(this.tlsAgent ? { agent: { https: this.tlsAgent } } : {}),
218
237
  responseType: 'buffer',
219
238
  timeout: { request: options.timeoutMs ?? 20000 },
220
239
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devlas/dte-sii",
3
- "version": "2.12.9",
3
+ "version": "2.12.10",
4
4
  "description": "Facturación y boletas electrónicas para el SII de Chile. Genera, timbra, firma y envía DTEs, libros electrónicos y automatiza la certificación.",
5
5
  "main": "index.js",
6
6
  "types": "dte-sii.d.ts",
package/utils/pfx.js CHANGED
@@ -10,9 +10,24 @@
10
10
  */
11
11
 
12
12
  const fs = require('fs');
13
+ const https = require('https');
14
+ const crypto = require('crypto');
13
15
  const forge = require('node-forge');
14
16
  const { certError, ERROR_CODES } = require('./error');
15
17
 
18
+ /**
19
+ * Flags TLS que la infraestructura legacy del SII requiere para completar la
20
+ * autenticación con certificado cliente (mismo set que SiiPortalAuth.SII_TLS_OPTS).
21
+ * Sin `maxVersion: 'TLSv1.2'` + renegociación insegura habilitada, el handshake
22
+ * de client-cert contra los servidores del SII no se completa correctamente.
23
+ */
24
+ const SII_LEGACY_TLS_FLAGS = {
25
+ maxVersion: 'TLSv1.2',
26
+ secureOptions:
27
+ crypto.constants.SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION |
28
+ crypto.constants.SSL_OP_LEGACY_SERVER_CONNECT,
29
+ };
30
+
16
31
  /**
17
32
  * Resultado de cargar un PFX
18
33
  * @typedef {Object} PfxData
@@ -217,17 +232,56 @@ function getDaysUntilExpiry(notAfter) {
217
232
 
218
233
  /**
219
234
  * Crear opciones TLS desde datos PFX
235
+ *
236
+ * IMPORTANTE: `got` (usado por SiiSession) remapea su opción `https` a un set
237
+ * fijo de claves y solo reconoce `certificate` para el certificado cliente —
238
+ * NO `cert` (ver got/dist/source/core/index.js, "HTTPS options remapping").
239
+ * Se incluyen ambas claves (`cert` para consumidores que usan `https` nativo,
240
+ * `certificate` para got) para que el certificado se envíe en ambos casos.
241
+ *
220
242
  * @param {PfxData} pfxData - Datos del PFX
221
243
  * @returns {Object} Opciones TLS para https/got
222
244
  */
223
245
  function createTlsOptions(pfxData) {
246
+ const certPem = pfxData.certificateChainPem || pfxData.certificatePem;
224
247
  return {
225
248
  key: pfxData.privateKeyPem,
226
- cert: pfxData.certificateChainPem || pfxData.certificatePem,
249
+ cert: certPem,
250
+ certificate: certPem,
227
251
  rejectUnauthorized: false,
228
252
  };
229
253
  }
230
254
 
255
+ /**
256
+ * Crea un https.Agent nativo con el certificado cliente + los flags TLS legacy
257
+ * que el SII requiere. `got` no reenvía `secureOptions`/`maxVersion` a través de
258
+ * su opción `https` bajo ningún nombre de clave — la única forma de aplicarlos
259
+ * es con un Agent nativo pasado como `agent.https` en las opciones de got.
260
+ *
261
+ * @param {PfxData} pfxData - Datos del PFX
262
+ * @returns {https.Agent}
263
+ */
264
+ function createTlsAgent(pfxData) {
265
+ return createTlsAgentFromPem(pfxData.privateKeyPem, pfxData.certificateChainPem || pfxData.certificatePem);
266
+ }
267
+
268
+ /**
269
+ * Crea un https.Agent nativo desde un par key/cert PEM ya extraído (ej. desde
270
+ * una instancia de Certificado), con los mismos flags legacy que createTlsAgent.
271
+ *
272
+ * @param {string} keyPem
273
+ * @param {string} certPem
274
+ * @returns {https.Agent}
275
+ */
276
+ function createTlsAgentFromPem(keyPem, certPem) {
277
+ return new https.Agent({
278
+ ...SII_LEGACY_TLS_FLAGS,
279
+ key: keyPem,
280
+ cert: certPem,
281
+ rejectUnauthorized: false,
282
+ });
283
+ }
284
+
231
285
  module.exports = {
232
286
  loadPfxFromBuffer,
233
287
  loadPfxFromFile,
@@ -236,4 +290,6 @@ module.exports = {
236
290
  isCertificateExpired,
237
291
  getDaysUntilExpiry,
238
292
  createTlsOptions,
293
+ createTlsAgent,
294
+ createTlsAgentFromPem,
239
295
  };