Module: Nfe::CertificateValidator

Defined in:
lib/nfe/certificate.rb,
sig/nfe/certificate.rbs

Overview

Local-only validation and inspection of A1 digital certificates (PKCS#12, +.pfx+/+.p12+). Uses only the openssl stdlib — no network access, no new runtime dependency.

The PKCS#12 bytes and password are handled in-memory only and never logged, persisted, or echoed into exception messages.

Constant Summary collapse

DEFAULT_THRESHOLD_DAYS =

Default window (in days) within which a certificate is "expiring soon".

Returns:

  • (Integer)
30
SECONDS_PER_DAY =

Number of seconds in a day, for day-delta math.

Returns:

  • (Integer)
86_400

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.coerce_time(value) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



83
84
85
# File 'lib/nfe/certificate.rb', line 83

def coerce_time(value)
  value.is_a?(Time) ? value : Time.parse(value.to_s)
end

.days_until_expiration(not_after) ⇒ Integer

Whole days from now until not_after (negative once expired).

Parameters:

  • not_after (Time, String)

    the expiry instant.

Returns:

  • (Integer)


69
70
71
72
# File 'lib/nfe/certificate.rb', line 69

def days_until_expiration(not_after)
  expiry = coerce_time(not_after)
  ((expiry - Time.now) / SECONDS_PER_DAY).floor
end

.expiring_soon?(not_after, threshold_days = DEFAULT_THRESHOLD_DAYS) ⇒ Boolean

Returns true when expiry is within [0, threshold_days).

Parameters:

  • not_after (Time, String)

    the expiry instant.

  • threshold_days (Integer) (defaults to: DEFAULT_THRESHOLD_DAYS)

    the "soon" window (default 30).

Returns:

  • (Boolean)

    true when expiry is within [0, threshold_days).



77
78
79
80
# File 'lib/nfe/certificate.rb', line 77

def expiring_soon?(not_after, threshold_days = DEFAULT_THRESHOLD_DAYS)
  days = days_until_expiration(not_after)
  days >= 0 && days < threshold_days
end

.supported_format?(filename) ⇒ Boolean

Returns true for +.pfx+/+.p12+ (case-insensitive).

Parameters:

  • filename (String, nil)

    a certificate filename.

Returns:

  • (Boolean)

    true for +.pfx+/+.p12+ (case-insensitive).



37
38
39
40
# File 'lib/nfe/certificate.rb', line 37

def supported_format?(filename)
  ext = filename.to_s.downcase.split(".").last
  %w[pfx p12].include?(ext)
end

.validate(der_bytes, password) ⇒ Nfe::CertificateInfo

Parse PKCS#12 bytes with the given password and extract certificate metadata.

Parameters:

  • der_bytes (String)

    the raw +.pfx+/+.p12+ bytes.

  • password (String)

    the certificate password.

Returns:

Raises:

  • (Nfe::InvalidRequestError)

    on a wrong password or malformed DER. The message never includes the password or the raw bytes.



50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/nfe/certificate.rb', line 50

def validate(der_bytes, password)
  pkcs12 = OpenSSL::PKCS12.new(der_bytes, password)
  cert = pkcs12.certificate

  CertificateInfo.new(
    subject: cert.subject.to_s,
    issuer: cert.issuer.to_s,
    not_before: cert.not_before,
    not_after: cert.not_after,
    serial_number: cert.serial.to_s
  )
rescue OpenSSL::OpenSSLError, ArgumentError, TypeError
  raise Nfe::InvalidRequestError, "Certificado ou senha inválidos"
end

Instance Method Details

#self?.coerce_timeTime

Parameters:

  • value (Object)

Returns:

  • (Time)


43
# File 'sig/nfe/certificate.rbs', line 43

def self?.coerce_time: (untyped value) -> Time

#self?.days_until_expirationInteger

Parameters:

  • not_after (Time, String)

Returns:

  • (Integer)


39
# File 'sig/nfe/certificate.rbs', line 39

def self?.days_until_expiration: ((Time | String) not_after) -> Integer

#self?.expiring_soon?Boolean

Parameters:

  • not_after (Time, String)
  • threshold_days (Integer)

Returns:

  • (Boolean)


41
# File 'sig/nfe/certificate.rbs', line 41

def self?.expiring_soon?: ((Time | String) not_after, ?Integer threshold_days) -> bool

#self?.supported_format?Boolean

Parameters:

  • filename (String, nil)

Returns:

  • (Boolean)


35
# File 'sig/nfe/certificate.rbs', line 35

def self?.supported_format?: (String? filename) -> bool

#self?.validateNfe::CertificateInfo

Parameters:

  • der_bytes (String)
  • password (String)

Returns:



37
# File 'sig/nfe/certificate.rbs', line 37

def self?.validate: (String der_bytes, String password) -> Nfe::CertificateInfo