@devlas/dte-sii 2.12.20 → 2.12.21

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/EnviadorSII.js CHANGED
@@ -1174,7 +1174,11 @@ class EnviadorSII {
1174
1174
  log.log('XML Length:', xml.length, 'bytes');
1175
1175
 
1176
1176
  const [rutNum, dv] = rutEmisor.split('-');
1177
- const [rutEnviaNum, dvEnvia] = this.certificado.rut.split('-');
1177
+ // Fallback a rutEmisor si el certificado no expone RUT (ver
1178
+ // docs/EXTRACCION_RUT_CERTIFICADO.md) — evita un TypeError por .split
1179
+ // sobre null cuando el PFX no tiene el RUT en ningún campo conocido.
1180
+ const rutEnvia = this.certificado.rut || rutEmisor;
1181
+ const [rutEnviaNum, dvEnvia] = rutEnvia.split('-');
1178
1182
 
1179
1183
  const boundary = '----WebKitFormBoundary' + Math.random().toString(36).substring(2);
1180
1184
 
@@ -214,7 +214,7 @@ class BoletaCert {
214
214
 
215
215
  const envioBoleta = new EnvioBOLETA({
216
216
  rutEmisor: this.emisor.rut,
217
- rutEnvia: this.certificado.rut,
217
+ rutEnvia: this.certificado.rut || this.emisor.rut,
218
218
  fchResol: this.resolucion.fecha,
219
219
  nroResol: this.resolucion.numero,
220
220
  certificado: this.certificado,
@@ -228,7 +228,7 @@ class BoletaCert {
228
228
  // Carátula para certificación
229
229
  envioBoleta.setCaratula({
230
230
  RutEmisor: this.emisor.rut,
231
- RutEnvia: this.certificado.rut,
231
+ RutEnvia: this.certificado.rut || this.emisor.rut,
232
232
  RutReceptor: '60803000-K', // SII en certificación
233
233
  FchResol: this.resolucion.fecha,
234
234
  NroResol: this.resolucion.numero,
@@ -291,7 +291,7 @@ class BoletaCert {
291
291
  // Establecer carátula
292
292
  consumoFolio.setCaratula({
293
293
  RutEmisor: this.emisor.rut,
294
- RutEnvia: this.certificado.rut,
294
+ RutEnvia: this.certificado.rut || this.emisor.rut,
295
295
  FchResol: this.resolucion.fecha,
296
296
  NroResol: this.resolucion.numero,
297
297
  SecEnvio: options?.secEnvio || 1,
@@ -358,7 +358,7 @@ class BoletaCert {
358
358
 
359
359
  consumoFolio.setCaratula({
360
360
  RutEmisor: this.emisor.rut,
361
- RutEnvia: this.certificado.rut,
361
+ RutEnvia: this.certificado.rut || this.emisor.rut,
362
362
  FchResol: this.resolucion.fecha,
363
363
  NroResol: this.resolucion.numero,
364
364
  SecEnvio: options.secEnvio,
package/package.json CHANGED
@@ -1,12 +1,15 @@
1
1
  {
2
2
  "name": "@devlas/dte-sii",
3
- "version": "2.12.20",
3
+ "version": "2.12.21",
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",
7
7
  "license": "MIT",
8
8
  "author": "Devlas SpA <ti@devlas.cl> (https://devlas.cl)",
9
9
  "homepage": "https://github.com/devlas-cl/dte-sii",
10
+ "scripts": {
11
+ "test": "node test/pfx.test.js"
12
+ },
10
13
  "repository": {
11
14
  "type": "git",
12
15
  "url": "https://github.com/devlas-cl/dte-sii.git"
package/utils/pfx.js CHANGED
@@ -179,11 +179,48 @@ function extractSubjectFields(cert) {
179
179
  }
180
180
 
181
181
  /**
182
- * Extraer RUT del certificado (desde serialNumber, OU o CN)
182
+ * Extraer RUT desde el Subject Alternative Name (SAN) con OID
183
+ * 1.3.6.1.4.1.8321.1 — reservado bajo la normativa chilena de firma
184
+ * electrónica (Ley 19.799). A diferencia de serialNumber/OU/CN, que son
185
+ * convención de cada CA, este OID es consistente entre CAs (verificado
186
+ * contra Acepta, IDOK y Signapis — ver docs/EXTRACCION_RUT_CERTIFICADO.md).
187
+ * @param {forge.pki.Certificate} cert - Certificado
188
+ * @returns {string|null} RUT o null
189
+ */
190
+ function extractRutFromSan(cert) {
191
+ const san = cert.getExtension('subjectAltName');
192
+ if (!san || !san.altNames) return null;
193
+
194
+ for (const alt of san.altNames) {
195
+ if (alt.type !== 0) continue; // 0 = otherName
196
+ try {
197
+ const oid = forge.asn1.derToOid(alt.value[0].value);
198
+ if (oid !== '1.3.6.1.4.1.8321.1') continue;
199
+
200
+ const inner = alt.value[1] && alt.value[1].value && alt.value[1].value[0];
201
+ if (!inner || !inner.value) continue;
202
+
203
+ const clean = inner.value.replace(/\./g, '').toUpperCase();
204
+ if (/^\d{7,8}-[\dK]$/.test(clean)) {
205
+ return clean;
206
+ }
207
+ } catch {
208
+ // OID no parseable en este altName — seguir con el resto y caer al fallback
209
+ }
210
+ }
211
+
212
+ return null;
213
+ }
214
+
215
+ /**
216
+ * Extraer RUT del certificado (desde SAN OID, serialNumber, OU o CN)
183
217
  * @param {forge.pki.Certificate} cert - Certificado
184
218
  * @returns {string|null} RUT o null
185
219
  */
186
220
  function extractRutFromCertificate(cert) {
221
+ const sanRut = extractRutFromSan(cert);
222
+ if (sanRut) return sanRut;
223
+
187
224
  const subject = extractSubjectFields(cert);
188
225
 
189
226
  // Intentar desde serialNumber (más confiable)