Class: VerifactuRails::Certificado
- Inherits:
-
Object
- Object
- VerifactuRails::Certificado
- Defined in:
- lib/verifactu_rails/certificado.rb
Overview
Envuelve el certificado del obligado tributario usado para la autenticación mutua TLS contra la AEAT.
En modalidad VERI*FACTU el certificado NO firma los registros: solo autentica el canal. Aun así es la credencial más sensible del sistema, así que esta clase deliberadamente NO sabe leer ficheros ni variables de entorno: recibe los bytes ya cargados. La política de almacenamiento es de la capa de integración.
Constant Summary collapse
- AVISO_CADUCIDAD_DIAS =
30- PATRON_NIF =
NIF del titular, tal como lo graba la FNMT en el sujeto.
Sirve para una comprobación que ahorra viajes: la AEAT exige que el titular del certificado coincida con el ObligadoEmision, y si no coinciden responde un 4104 "no está identificado" que parece decir que el NIF no existe.
Es una heurística sobre formatos observados (serialNumber "IDCES-xxxxxxxxX" o el NIF suelto, y CN terminado en "- xxxxxxxxX"), no un campo normalizado, así que devuelve nil si no lo reconoce en vez de inventarse nada.
/\b([A-Z]?\d{7,8}[A-Z0-9])\b/
Instance Attribute Summary collapse
-
#cadena ⇒ Object
readonly
Returns the value of attribute cadena.
-
#certificado ⇒ Object
readonly
Returns the value of attribute certificado.
-
#clave ⇒ Object
readonly
Returns the value of attribute clave.
Class Method Summary collapse
-
.desde_pkcs12(datos, password) ⇒ Object
Construye desde los bytes de un .p12/.pfx (lo que el usuario descarga de la FNMT).
Instance Method Summary collapse
-
#caduca_pronto?(umbral = AVISO_CADUCIDAD_DIAS) ⇒ Boolean
Los .p12 caducan siempre en mal momento.
- #caducado? ⇒ Boolean
- #dias_para_caducar ⇒ Object
-
#initialize(certificado:, clave:, cadena: nil) ⇒ Certificado
constructor
A new instance of Certificado.
- #nif ⇒ Object
- #resumen ⇒ Object
-
#sello? ⇒ Boolean
Un certificado de sello de entidad se dirige a un endpoint DISTINTO de la AEAT.
- #sujeto ⇒ Object
-
#titular ⇒ Object
Nombre o razón social del titular, sin el NIF que la FNMT le pega al final del CN ("GARCIA LOPEZ ANA - 89890001K").
Constructor Details
#initialize(certificado:, clave:, cadena: nil) ⇒ Certificado
Returns a new instance of Certificado.
23 24 25 26 27 28 |
# File 'lib/verifactu_rails/certificado.rb', line 23 def initialize(certificado:, clave:, cadena: nil) @certificado = certificado @clave = clave @cadena = Array(cadena) validar! end |
Instance Attribute Details
#cadena ⇒ Object (readonly)
Returns the value of attribute cadena.
21 22 23 |
# File 'lib/verifactu_rails/certificado.rb', line 21 def cadena @cadena end |
#certificado ⇒ Object (readonly)
Returns the value of attribute certificado.
21 22 23 |
# File 'lib/verifactu_rails/certificado.rb', line 21 def certificado @certificado end |
#clave ⇒ Object (readonly)
Returns the value of attribute clave.
21 22 23 |
# File 'lib/verifactu_rails/certificado.rb', line 21 def clave @clave end |
Class Method Details
.desde_pkcs12(datos, password) ⇒ Object
Construye desde los bytes de un .p12/.pfx (lo que el usuario descarga de la FNMT). Evitamos obligarle a partirlo en PEM con openssl a mano.
32 33 34 35 36 37 38 39 40 41 |
# File 'lib/verifactu_rails/certificado.rb', line 32 def self.desde_pkcs12(datos, password) raise CertificadoError, 'Datos PKCS12 vacíos' if datos.nil? || datos.empty? p12 = OpenSSL::PKCS12.new(datos, password.to_s) new(certificado: p12.certificate, clave: p12.key, cadena: p12.ca_certs) rescue OpenSSL::PKCS12::PKCS12Error => e raise CertificadoError, 'No se pudo abrir el PKCS12: contraseña incorrecta o fichero corrupto ' \ "(#{e.})" end |
Instance Method Details
#caduca_pronto?(umbral = AVISO_CADUCIDAD_DIAS) ⇒ Boolean
Los .p12 caducan siempre en mal momento. Que el sistema avise solo.
52 53 54 |
# File 'lib/verifactu_rails/certificado.rb', line 52 def caduca_pronto?(umbral = AVISO_CADUCIDAD_DIAS) dias_para_caducar <= umbral end |
#caducado? ⇒ Boolean
43 44 45 |
# File 'lib/verifactu_rails/certificado.rb', line 43 def caducado? Time.now > certificado.not_after end |
#dias_para_caducar ⇒ Object
47 48 49 |
# File 'lib/verifactu_rails/certificado.rb', line 47 def dias_para_caducar ((certificado.not_after - Time.now) / 86_400).floor end |
#nif ⇒ Object
78 79 80 81 82 83 84 85 |
# File 'lib/verifactu_rails/certificado.rb', line 78 def nif candidatos = [sujeto['serialNumber'], sujeto['CN']].compact candidatos.each do |valor| m = valor.upcase.sub(/\AIDCES-/, '').match(PATRON_NIF) return m[1] if m && m[1].length == 9 end nil end |
#resumen ⇒ Object
100 101 102 103 104 |
# File 'lib/verifactu_rails/certificado.rb', line 100 def resumen { titular: sujeto['CN'], organizacion: sujeto['O'], caduca: certificado.not_after, dias_restantes: dias_para_caducar, sello: sello? } end |
#sello? ⇒ Boolean
Un certificado de sello de entidad se dirige a un endpoint DISTINTO de la AEAT. Heurística sobre el sujeto; el integrador puede forzarlo en config.
62 63 64 65 |
# File 'lib/verifactu_rails/certificado.rb', line 62 def sello? valores = sujeto.values.join(' ').downcase valores.include?('sello') || valores.include?('seal') end |
#sujeto ⇒ Object
56 57 58 |
# File 'lib/verifactu_rails/certificado.rb', line 56 def sujeto certificado.subject.to_a.each_with_object({}) { |(k, v, _), h| h[k] = v } end |
#titular ⇒ Object
Nombre o razón social del titular, sin el NIF que la FNMT le pega al final del CN ("GARCIA LOPEZ ANA - 89890001K").
Importa porque la AEAT identifica al obligado por el PAR NIF + NombreRazon: con el NIF correcto y un nombre que no cuadre responde 4104, y el mensaje habla solo del NIF aunque en el detalle devuelva los dos campos.
93 94 95 96 97 98 |
# File 'lib/verifactu_rails/certificado.rb', line 93 def titular cn = sujeto['CN'] return nil if cn.nil? cn.sub(/\s*-\s*#{Regexp.escape(nif.to_s)}\s*\z/, '').strip end |