# PEM and DER Utilities
Two top-level functions handle conversion between PEM and DER byte representations.
## Free functions
| `pem_to_der(data: bytes)` | `list[bytes]` | Decode one or more PEM blocks to DER. Always returns a list (one entry per block); no block present → `ValueError`. |
| `der_to_pem(der: bytes, label: str)` | `bytes` | Wrap DER bytes in a `-----BEGIN {label}-----` / `-----END {label}-----` block. |
## Usage
```python
import synta
# Decode a PEM file with one or more certificates
pem_data = open("bundle.pem", "rb").read()
der_list = synta.pem_to_der(pem_data) # list[bytes] — one entry per PEM block
for der in der_list:
cert = synta.Certificate.from_der(der)
print(cert.subject)
# Single-block PEM (typical for one certificate)
der_list = synta.pem_to_der(open("cert.pem", "rb").read())
cert = synta.Certificate.from_der(der_list[0])
# Wrap DER bytes back into PEM
der = cert.to_der()
pem = synta.der_to_pem(der, "CERTIFICATE")
open("cert.pem", "wb").write(pem)
# Private key PEM
key_der = open("key.der", "rb").read()
pem = synta.der_to_pem(key_der, "PRIVATE KEY")
# Empty PEM raises ValueError
try:
synta.pem_to_der(b"not pem")
except ValueError as e:
print(f"No PEM block found: {e}")
```
## Notes
- `pem_to_der` always returns a `list`, even when only one block is present. A single-block
result is accessed as `result[0]`.
- `pem_to_der` raises `ValueError` when no PEM block is found. It does **not** silently return
an empty list.
- Malformed base64 inside a PEM block is also rejected with `ValueError`.
- `der_to_pem` produces the standard 64-character line wrap required by RFC 7468.
- The `label` argument to `der_to_pem` appears verbatim between the `BEGIN` / `END` markers
(e.g. `"CERTIFICATE"`, `"PRIVATE KEY"`, `"CERTIFICATE REQUEST"`).
See also [PKCS Loaders](pkcs-loaders.md) for format-agnostic reading of PEM bundles,
PKCS#7 files, and PKCS#12 archives.