# PEM conversion

> 

PEM (`-----BEGIN PRIVATE KEY-----` / `-----BEGIN PUBLIC KEY-----`) is the key format used by OpenSSL, SSH, most CLI tools, and many legacy systems. unjwt converts between JWK and PEM with [`importPEM`](#importpem) and [`exportPEM`](#exportpem).

```ts
import { importPEM, exportPEM } from "unjwt/jwk";
```

## `importPEM`

```ts
importPEM(pem, alg, options?)
```

Parses a PEM string and returns it as a JWK. The PEM **type** is inferred from the `-----BEGIN <X>-----` label:

<table>
<thead>
  <tr>
    <th>
      PEM label
    </th>
    
    <th>
      Inferred <code>
        pemType
      </code>
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        -----BEGIN PRIVATE KEY-----
      </code>
    </td>
    
    <td>
      <code>
        "pkcs8"
      </code>
      
       — private key
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        -----BEGIN PUBLIC KEY-----
      </code>
    </td>
    
    <td>
      <code>
        "spki"
      </code>
      
       — public key
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        -----BEGIN CERTIFICATE-----
      </code>
    </td>
    
    <td>
      <code>
        "x509"
      </code>
      
       — X.509 certificate
    </td>
  </tr>
</tbody>
</table>

```ts [import.ts]
import { importPEM } from "unjwt/jwk";
import { readFile } from "node:fs/promises";

const pem = await readFile("./rsa-private.pem", "utf-8");
const jwk = await importPEM(pem, "RS256", {
  extractable: true,
  jwkParams: { kid: "rsa-main", use: "sig" },
});
```

### Options

<table>
<thead>
  <tr>
    <th>
      Option
    </th>
    
    <th>
      Default
    </th>
    
    <th>
      Effect
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        pemType
      </code>
    </td>
    
    <td>
      Inferred from label
    </td>
    
    <td>
      Override explicitly if the label is missing
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        extractable
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Web Crypto extractable flag
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        jwkParams
      </code>
    </td>
    
    <td>
      —
    </td>
    
    <td>
      Extra JWK metadata (<code>
        kid
      </code>
      
      , <code>
        use
      </code>
      
      , ...)
    </td>
  </tr>
</tbody>
</table>

Throws `ERR_JWK_INVALID` if:

- The PEM has no recognizable label and `pemType` is not supplied.
- The label doesn't match the requested or inferred `pemType`.

## `exportPEM`

```ts
exportPEM(jwk, options?)
```

Converts a JWK back to a PEM-encoded string. The PEM **format** is inferred from the JWK shape:

- JWK has `d` (or an equivalent private component) → `"pkcs8"` (private).
- No `d` → `"spki"` (public).

```ts [export.ts]
import { exportPEM } from "unjwt/jwk";

const pem = await exportPEM(jwk);
// "-----BEGIN PRIVATE KEY-----\n...base64...\n-----END PRIVATE KEY-----\n"
```

### Options

<table>
<thead>
  <tr>
    <th>
      Option
    </th>
    
    <th>
      Default
    </th>
    
    <th>
      Effect
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        pemFormat
      </code>
    </td>
    
    <td>
      Inferred from JWK
    </td>
    
    <td>
      <code>
        "pkcs8"
      </code>
      
       or <code>
        "spki"
      </code>
      
       — cert export is not supported
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        alg
      </code>
    </td>
    
    <td>
      <code>
        jwk.alg
      </code>
    </td>
    
    <td>
      Algorithm hint used when <code>
        jwk.alg
      </code>
      
       is absent
    </td>
  </tr>
</tbody>
</table>

<note>

**X.509 certificate export is intentionally not supported.** A certificate requires metadata (subject, issuer, validity period, extensions) and a CA signature — none of which a JWK carries. If you need a certificate, generate it with a PKI tool (OpenSSL, `step-ca`, your CA's API) and import it with `importPEM()`.

</note>

## Deprecated aliases

Earlier unjwt versions exposed positional equivalents:

```ts
// Deprecated — still work, emit IDE deprecation hints
importFromPEM(pem, pemType, alg, options?);
exportToPEM(jwk, pemFormat, alg?);
```

These remain for backward compatibility but you should migrate to `importPEM` / `exportPEM` on touch.

## See also

- [Importing & exporting →](/jwk/import-export) — for JWK/CryptoKey/bytes.
- [Generating keys →](/jwk/generating).
