# `Sat.Certificados.Certificate`

Certificado X.509 (.cer) del SAT en formato DER con parseo OTP para consultas (RFC, vigencia, etc.).

# `t`

```elixir
@type t() :: %Sat.Certificados.Certificate{
  parsed: Clir.Openssl.X509.otp_cert(),
  raw_der: binary()
}
```

# `ac_version`

```elixir
@spec ac_version(t()) :: integer() | nil
```

Versión de la Autoridad Certificadora del SAT (`4` o `5`) que emitió el cert.

El SAT codifica el número de AC en el dígito 12 (índice 11) del
`no_certificado` (20 dígitos decimales).

Retorna `nil` si no se puede determinar.

# `certificate_type`

```elixir
@spec certificate_type(t()) :: :csd | :fiel | :unknown
```

Detecta el tipo de certificado por **extensiones X.509 estándar**:

  - `:csd` (Certificado de Sello Digital): `keyUsage` con `digitalSignature`
    y `nonRepudiation` activos, sin `dataEncipherment` ni `keyAgreement`.
  - `:fiel` (e.firma / FIEL): `extKeyUsage` con `emailProtection` y
    `clientAuth` activos.

Si las extensiones no resuelven el tipo, hace fallback a la heurística del
OU del subject (`Prueba_CFDI`, `FIEL`, etc.). Retorna `:unknown` si no
se puede determinar.

# `expired?`

```elixir
@spec expired?(t()) :: boolean()
```

`true` si la fecha actual es posterior a `valid_to/1`.

# `fingerprint`

```elixir
@spec fingerprint(t()) :: String.t()
```

Huella SHA-1 del DER, formato `AA:BB:CC:...` en mayúsculas (estilo Node).

# `fingerprint_sha256`

```elixir
@spec fingerprint_sha256(t()) :: String.t()
```

Huella SHA-256 del DER (hex mayúsculas, sin separadores).

# `from_der`

```elixir
@spec from_der(binary()) :: {:ok, t()} | {:error, term()}
```

Crea un certificado a partir del contenido DER binario (.cer).

# `from_file`

```elixir
@spec from_file(String.t()) :: {:ok, t()} | {:error, term()}
```

Lee un archivo `.cer` (DER o PEM).

# `from_pem`

```elixir
@spec from_pem(String.t()) :: {:ok, t()} | {:error, term()}
```

Crea un certificado a partir de un string PEM.

# `is_csd?`

```elixir
@spec is_csd?(t()) :: boolean()
```

`true` si es CSD. Equivalente a `certificate_type/1 == :csd`.

# `is_fiel?`

```elixir
@spec is_fiel?(t()) :: boolean()
```

`true` si es FIEL (e.firma). Equivalente a `certificate_type/1 == :fiel`.

# `issued_by?`

```elixir
@spec issued_by?(t(), t()) :: boolean()
```

Verifica que este certificado fue firmado por el `issuer` dado.
Útil para validar la cadena de confianza contra `AC4_SAT.cer` / `AC5_SAT.cer`.

Retorna `true` si la firma del cert es válida bajo la pública del emisor.

# `issuer`

```elixir
@spec issuer(t()) :: %{required(String.t()) =&gt; String.t()}
```

Issuer como mapa atributo => valor.

# `legal_name`

```elixir
@spec legal_name(t()) :: String.t()
```

Nombre legal (CN o, en su defecto, givenName).

# `no_certificado`

```elixir
@spec no_certificado(t()) :: String.t()
```

Número de certificado en formato SAT (ver `Clir.Openssl.X509.no_certificado/1`).

# `public_key`

```elixir
@spec public_key(t()) :: {:RSAPublicKey, integer(), integer()}
```

Llave pública RSA decodificada (`{:RSAPublicKey, modulus, exponent}`).

# `public_key_pem`

```elixir
@spec public_key_pem(t()) :: String.t()
```

Llave pública del certificado en formato PEM `-----BEGIN PUBLIC KEY-----`.

# `rfc`

```elixir
@spec rfc(t()) :: String.t()
```

RFC del titular, inferido del subject (OIDs típicos del SAT).

# `rsa_encrypt`

```elixir
@spec rsa_encrypt(t(), iodata()) :: String.t()
```

Encripta un mensaje con la llave pública del certificado (RSA PKCS#1 v1.5).
Solo el dueño de la llave privada puede desencriptar. Retorna Base64.

# `serial_number`

```elixir
@spec serial_number(t()) :: String.t()
```

Número de serie en hexadecimal (mayúsculas).

# `subject`

```elixir
@spec subject(t()) :: %{required(String.t()) =&gt; String.t()}
```

Subject como mapa atributo => valor.

# `subject_type`

```elixir
@spec subject_type(t()) :: :moral | :fisica | :unknown
```

Tipo de sujeto (titular):

  - `:moral` — persona moral / empresa: el OID 2.5.4.45 contiene `RFC / CURP`.
  - `:fisica` — persona física: RFC de 13 caracteres sin `/`.
  - `:unknown` — no se pudo determinar.

# `to_base64`

```elixir
@spec to_base64(t()) :: String.t()
```

Contenido del certificado en Base64 (sin cabeceras PEM ni saltos).

# `to_der`

```elixir
@spec to_der(t()) :: binary()
```

DER crudo del certificado.

# `to_map`

```elixir
@spec to_map(t()) :: map()
```

Proyecta toda la metadata útil del certificado a un mapa con llaves
snake_case (átomos por default).

Incluye: tipo (CSD/FIEL), persona física o moral, RFC, nombre legal,
número de certificado SAT, número de serie hex, fechas de vigencia,
estado de expiración, fingerprints (SHA-1 y SHA-256), versión de la AC,
issuer, y subject completo (mapa con códigos LDAP estándar como
llaves: "CN", "O", "OU", "RFC", etc.).

La llave privada NO se incluye (es PII y vive en `PrivateKey`).

Opciones:
  * `:keys` — `:atom` (default), `:string` o `:existing`. Misma semántica
    que `CFDI.to_map/2`. Aplica al primer nivel y al mapa anidado bajo
    `:subject`.

# `to_map`

```elixir
@spec to_map(
  t(),
  keyword()
) :: map()
```

# `to_pem`

```elixir
@spec to_pem(t()) :: String.t()
```

PEM del certificado (incluye cabeceras).

# `valid?`

```elixir
@spec valid?(t()) :: boolean()
```

`true` si el certificado está vigente: `valid_from <= now <= valid_to`.
A diferencia de `expired?/1`, también verifica que el inicio de vigencia
ya esté en el pasado.

# `valid_from`

```elixir
@spec valid_from(t()) :: DateTime.t()
```

Inicio de vigencia (UTC).

# `valid_to`

```elixir
@spec valid_to(t()) :: DateTime.t()
```

Fin de vigencia (UTC).

# `verify`

```elixir
@spec verify(t(), iodata(), String.t(), atom()) :: boolean()
```

Verifica una firma Base64 contra `data` con la llave pública del cert.
Default SHA-256 (lo que exige el SAT).

# `verify_integrity`

```elixir
@spec verify_integrity(t(), t()) :: boolean()
```

Alias de `issued_by?/2` por compatibilidad con `e.firma`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
