# Certificate
The `synta-certificate` crate provides typed X.509 v3 certificate structures
based on RFC 5280.
## Dependency
```toml
[dependencies]
synta-certificate = "0.1"
synta = "0.1"
```
## Core types
```mermaid
graph TD
C(["Certificate"])
T(["tbsCertificate"])
SA["signatureAlgorithm<br/>AlgorithmIdentifier"]
SV["signatureValue<br/>BitString"]
C --> T
C --> SA
C --> SV
T --> VER["version<br/>Option<Integer>"]
T --> SER["serialNumber<br/>Integer"]
T --> SIG["signature<br/>AlgorithmIdentifier"]
T --> ISS["issuer<br/>RawDer"]
T --> VAL["validity<br/>notBefore · notAfter"]
T --> SUB["subject<br/>RawDer"]
T --> SPKI(["subjectPublicKeyInfo"])
T --> EXT["extensions<br/>Option<RawDer>"]
SPKI --> ALG["algorithm<br/>AlgorithmIdentifier"]
SPKI --> KEY["subjectPublicKey<br/>BitStringRef"]
```
| `Certificate<'a>` | Top-level X.509 certificate (lifetime bound to input buffer) |
| `TBSCertificate<'a>` | To-be-signed certificate data |
| `AlgorithmIdentifier<'a>` | Algorithm OID with optional parameters |
| `SubjectPublicKeyInfo<'a>` | Public key algorithm and key bits |
| `Validity` | `notBefore` / `notAfter` validity period |
| `Time` | CHOICE of `UTCTime` or `GeneralizedTime` |
## Key field types
| `tbs_certificate.issuer` | `RawDer<'a>` | Zero-copy; raw DER bytes of the Name SEQUENCE |
| `tbs_certificate.subject` | `RawDer<'a>` | Zero-copy; raw DER bytes of the Name SEQUENCE |
| `tbs_certificate.extensions` | `Option<RawDer<'a>>` | Zero-copy; decoded lazily |
| `subject_public_key_info.subject_public_key` | `BitStringRef<'a>` | Zero-copy; borrowed key bits |
| `tbs_certificate.serial_number` | `Integer` | Eagerly decoded |
| `tbs_certificate.version` | `Option<Integer>` | Eagerly decoded |
| `signature_algorithm.algorithm` | `ObjectIdentifier` | Eagerly decoded |
## Parsing a certificate
```rust,ignore
use synta::{Decoder, Encoding};
use synta_certificate::{Certificate, format_dn};
let der = std::fs::read("cert.der")?;
let mut decoder = Decoder::new(&der, Encoding::Der);
let cert: Certificate = decoder.decode()?;
let tbs = &cert.tbs_certificate;
// Serial number (eagerly decoded)
println!("Serial: {:?}", tbs.serial_number);
// Issuer and subject — zero-copy RawDer<'a> spans
let issuer_str = format_dn(tbs.issuer.as_bytes());
let subject_str = format_dn(tbs.subject.as_bytes());
println!("Issuer: {}", issuer_str);
println!("Subject: {}", subject_str);
```
## Encoding a certificate
```rust,ignore
use synta::{Encoder, Encoding};
use synta_certificate::Certificate;
let cert = Certificate { /* ... */ };
let mut encoder = Encoder::new(Encoding::Der);
encoder.encode(&cert)?;
let der_bytes = encoder.finish()?;
```
## DN formatting
`format_dn` walks the raw DER directly with no intermediate allocation,
matching the `openssl x509 -text` output style:
```rust,ignore
use synta_certificate::format_dn;
let dn = format_dn(cert.tbs_certificate.subject.as_bytes());
// → "CN=example.com, O=Example Inc, C=US"
```
`format_dn_slash` produces the slash-prefix form used in AKI DirName output:
```rust,ignore
use synta_certificate::format_dn_slash;
let dirn = format_dn_slash(name_der);
// → "/C=US/O=Example Inc/CN=example.com"
```
## Parsing DN attributes
`parse_name_attrs` extracts `(dotted_oid, value_str)` pairs from a raw Name DER
span. TeletexString values are normalised to Latin-1; BMP/Universal to UTF-8.
Unknown tags produce a `#hexstring`.
```rust,ignore
use synta_certificate::parse_name_attrs;
let attrs = parse_name_attrs(cert.tbs_certificate.subject.as_bytes());
for (oid, value) in &attrs {
println!("{} = {}", oid, value);
}
// e.g. "2.5.4.3 = example.com", "2.5.4.10 = Example Inc"
```
## Subject Alternative Names
```rust,ignore
for (tag, content) in cert.subject_alt_names() {
match tag {
2 => println!("DNS: {}", String::from_utf8_lossy(&content)),
7 if content.len() == 4 => {
let octets: [u8; 4] = content.try_into().unwrap();
println!("IP: {}", std::net::Ipv4Addr::from(octets));
}
1 => println!("Email: {}", String::from_utf8_lossy(&content)),
_ => {}
}
}
```
SAN tag numbers:
| 0 | otherName | OtherName value bytes |
| 1 | rfc822Name | IA5String bytes (email) |
| 2 | dNSName | IA5String bytes |
| 4 | directoryName | Complete Name SEQUENCE TLV |
| 6 | uniformResourceIdentifier | IA5String bytes |
| 7 | iPAddress | 4 bytes (IPv4) or 16 bytes (IPv6) |
| 8 | registeredID | OID value bytes |
## PEM encoding and decoding
```rust,ignore
use synta_certificate::{pem_to_der, der_to_pem};
// Decode one or more PEM blocks
let pem = std::fs::read("chain.pem")?;
let ders: Vec<Vec<u8>> = pem_to_der(&pem);
// Encode to PEM
let pem_out = der_to_pem("CERTIFICATE", &der_bytes);
```
Common labels: `"CERTIFICATE"`, `"CERTIFICATE REQUEST"`, `"X509 CRL"`,
`"OCSP RESPONSE"`.
## Signature algorithm identification
```rust,ignore
use synta_certificate::{identify_signature_algorithm, identify_public_key_algorithm};
let name = identify_signature_algorithm(&cert.signature_algorithm.algorithm);
println!("Signature algorithm: {}", name);
// → "SHA256WithRSA", "id-Ed25519", "id-ML-DSA-65", etc.
```
## Building a certificate (openssl feature)
See [PKI Workflow — Building a certificate](../tutorial/pki-workflow.md) for
a full example using `CertificateBuilder` and `OpensslCertificateSigner`.
## Type aliases
| `Version` | `Integer` | `Version` |
| `CertificateSerialNumber` | `Integer` | `CertificateSerialNumber` |
| `UniqueIdentifier` | `BitString` | `UniqueIdentifier` |
## Supported signature algorithms
| RSA (PKCS #1) | 1.2.840.113549.1.1.* | RFC 3279 |
| ECDSA | 1.2.840.10045.4.* | RFC 5480 |
| EdDSA (Ed25519) | 1.3.101.112 | RFC 8410 |
| EdDSA (Ed448) | 1.3.101.113 | RFC 8410 |
| ML-DSA-44/65/87 | 2.16.840.1.101.3.4.3.17–19 | FIPS 204 |
| DSA | 1.2.840.10040.4.* | FIPS 186 |
## Cargo features
| `std` | yes | Standard library support |
| `alloc` | no | Allocation for no_std environments |
| `derive` | yes | Derive macros for Encode/Decode |
| `serde` | no | Serialize/Deserialize on generated types |
| `openssl` | no | OpenSSL-backed PKCS#12/CMS and certificate/CSR signing |
| `deprecated-pkcs12-algorithms` | no | Legacy 3DES/RC2 PKCS#12 encryption (requires `openssl`) |