---
title: "Issue from a CSR"
description: "How to issue an Amplia certificate from a CSR made on your server or in the user's browser: key media, the issue call, e-CNPJ and SSL/TLS examples, and issuing an e-CPF after a face check."
canonical: "https://docs.lacunasoftware.com/en-us/articles/amplia/integration/issue-from-csr"
locale: "en"
product: "Amplia"
last_updated: "2026-10-12"
---

# Issue from a CSR

When you make the key pair yourself, on your server or in the user's browser, you send [Amplia](https://docs.lacunasoftware.com/en-us/articles/amplia) only a certificate signing
request (CSR), and it returns the certificate. 

## Flow

1. Make the key pair and the CSR. Do it before creating the order, so that a key failure doesn't use up an order.
1. [Create the order](https://docs.lacunasoftware.com/en-us/articles/amplia/integration/orders). The certificate's subject comes from the order's parameters, never from the CSR.
1. Issue the certificate: `POST /api/v2/certificates` with the order's id, the CSR and the [key media](https://docs.lacunasoftware.com/en-us/articles/amplia/integration/issue-from-csr#key-media).
1. Keep the private key where it was made, and the certificate with it.

The samples make the key and the CSR with [PKI Express](https://docs.lacunasoftware.com/en-us/articles/pki-express), then issue the certificate and store both on the
server: 

**Node.js**

```js
const {KeyGenerator} = require('pki-express');
const {KeyMedia } = require('amplia-client/lib/enums');

const keyGenerator = new KeyGenerator({
	keySize: req.body.keySize,
	genCsr: true
});
let genKey = await keyGenerator.generate();
let generatedKey = genKey.getKey();
let cert = await client.issueCertificate(order.getId(), genKey.getCsr(), KeyMedia.PC);
let certId = cert.getId();
// WARNING: It is highly RECOMMENDED to ENCRYPT the key before storing
// in your database.
StorageMock.storeSync(generatedKey, certId, '.json');
StorageMock.storeSync(cert.getContentRaw(), certId, '.cer');
```

**Java**

```java
KeyGenerator keyGenerator = new KeyGenerator();
keyGenerator.setKeySize(2048);
keyGenerator.setKeyFormat(KeyFormats.JSON);
keyGenerator.setGenCsr(true);
KeyGenerationResult genKey = keyGenerator.generate();
Certificate cert = client.issueCertificate(order.getId(), genKey.getCsr());
// WARNING: It is highly RECOMMENDED to ENCRYPT the key before storing in your database.
StorageMock.storeKey(genKey.getKey().getBytes(), ".json", cert.getId());
StorageMock.storeKey(cert.getContent(), ".cer", cert.getId());
```

**PHP**

```php
use Lacuna\PkiExpress\KeyGenerator;
use Lacuna\PkiExpress\SupportedKeySizes;
use Lacuna\PkiExpress\KeyFormats;

$keyGenerator = new KeyGenerator();
$keyGenerator->keySize = SupportedKeySizes::S2048;
$keyGenerator->keyFormat = KeyFormats::JSON;
$keyGenerator->genCsr = true;
$genKey = $keyGenerator->generate();
$cert = $client->issueCertificate($order->getId(), $genKey->csr);
StorageMock::storeKey($genKey->key, ".json", $cert->id);
StorageMock::storeKey($cert->getContentRaw(), ".cer", $cert->id);
```

**Python**

```python
from pkiexpress import KeyGenerator, Pkcs12Generator, SupportedKeySizes

key_generator = KeyGenerator()
key_generator.key_size = SupportedKeySizes.S2048
key_generator.gen_csr = True
gen_key = key_generator.generate()
cert = client.issue_certificate(order.id, gen_key.csr)

pkcs12_generator = Pkcs12Generator()
pkcs12_generator.key = gen_key.key
pkcs12_generator.set_cert_file_from_base64(cert.content_base64)
pkcs12_gen_result = pkcs12_generator.generate('1234')
```

* The samples warn that the private key should be encrypted before it is stored in a database. 
* The Python sample then bundles the key and the certificate into a PKCS #12 file with PKI Express, to sign with it later. 
  To have Amplia make the key and return the PKCS #12 file instead, see [Issue a PKCS #12 file](https://docs.lacunasoftware.com/en-us/articles/amplia/integration/pkcs12).

## The issue call

`POST /api/v2/certificates` takes an `IssueCertificateRequest`: 

| Field | Meaning |
|---|---|
| `orderId` | Id of the order to fulfill. |
| `csr` | The CSR. The SSL/TLS demo sends it encoded as PEM. |
| `keyMedia` | Where the key is kept; see [Key media](https://docs.lacunasoftware.com/en-us/articles/amplia/integration/issue-from-csr#key-media). |
| `keyId` | Id of a key. |

TODO(docs): what `keyId` refers to.

The response is a `CertificateModel2` with the certificate's `id`, its `content` (Base64), dates, serial number and, in `info`, its
subject, issuer, CRL distribution points and OCSP URIs. `POST /api/certificates` takes the same body and returns the older
`CertificateModel`. 
The e-CNPJ demo sends this body, with the enum as a number: 

```json
{
  "OrderId": "00000000-0000-0000-0000-000000000000",
  "Csr": "<Csr, 944 bytes>",
  "KeyMedia": 1
}
```

To read the certificate later, use `GET /api/v2/certificates/{id}` (with `fillContent=true` to include `content`) or download it from
`GET /api/certificates/{id}/content`. 
TODO(docs): the format of the file returned by `GET /api/certificates/{id}/content`.

### Key media

`keyMedia` is `PC`, `CryptoDevice`, `Mobile` or `Cloud`. 

* A key made on your server, or by Web PKI in the user's browser on their computer: `PC`.
* A key made by Web PKI on a token or smart card: `CryptoDevice`.

An order's `keyMediaConstraints` lists the key media it accepts. 

Only the Node.js client sends `keyMedia`: its `KeyMedia` enum comes from `amplia-client/lib/enums`, since the package's entry point
doesn't export it. The Java client sends the order id, the CSR and optionally `keyId`; the PHP and Python clients send only the order id
and the CSR. 
TODO(docs): which key media Amplia records when the request has none.
TODO(docs): when to use `Mobile` and `Cloud` (Amplia keeps the keys of cloud certificates itself, see [cloud certificate management](https://docs.lacunasoftware.com/en-us/articles/amplia/on-premises/configure-cert-management)).

## Make the key in the user's browser

With [Web PKI](https://docs.lacunasoftware.com/en-us/articles/web-pki), the key can be made on the user's computer or token by your own page, and only the CSR reaches
your server. Web PKI returns the key's id with the CSR: once the certificate is issued, pass the id back to
[`importCertificate`](https://docs.lacunasoftware.com/content/typedocs/web-pki/classes/_lacuna_web_pki_d_.lacunawebpki.html#importcertificate) (or
[`importTokenCertificate`](https://docs.lacunasoftware.com/content/typedocs/web-pki/classes/_lacuna_web_pki_d_.lacunawebpki.html#importtokencertificate))
to install the certificate next to its key. 
The key pair itself comes from
[`generateSoftwareRsaKeyPair`](https://docs.lacunasoftware.com/content/typedocs/web-pki/classes/_lacuna_web_pki_d_.lacunawebpki.html#generatesoftwarersakeypair)
or [`generateTokenRsaKeyPair`](https://docs.lacunasoftware.com/content/typedocs/web-pki/classes/_lacuna_web_pki_d_.lacunawebpki.html#generatetokenrsakeypair). 

TODO(docs): no docs page or sample shows Web PKI key generation together with Amplia; to have Amplia's own page do it, see
[Issue on the holder's machine](https://docs.lacunasoftware.com/en-us/articles/amplia/integration/issue-on-user-machine).

## SSL/TLS certificates

The SSL/TLS demo makes an RSA 2048 key and a CSR on its server, creates an [SSL order](https://docs.lacunasoftware.com/en-us/articles/amplia/integration/orders#ssl) and issues the certificate with
key media `PC`; visitors can also bring their own CSR. 

* It checks a user's CSR before calling Amplia: at most 16 KB, PEM, Base64 or DER, an RSA key of 2048 to 4096 bits or an EC key on
  P-256 or P-384, and a signature that verifies. It sends Amplia the request re-encoded as PEM.
* What it hands back depends on who made the key: with a server key, a .pfx, the PEM private key and a .crt with the certificate and its
  issuing CA, never the root; with the user's CSR, only the .crt. It keeps keys in memory only.

## Issue an e-CPF after a face check

The face e-CPF demo checks the holder's identity before issuing: Serpro's Datavalid compares their face with the photo on their CNH,
through Lacuna Bio on [Rest PKI Core](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core). Once the face passes, it issues the e-CPF from a CSR, as in this
page's flow, with no Amplia issue page and no return trip. 
For the face capture, see Rest PKI Core's [biometric enrollment](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/bio-sessions/enrollment) and
[biometric authentication](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/bio-sessions/authentication). 

* The demo runs Datavalid's `pf-basica` check before any face capture: the name must match exactly, the birth date match, the CPF be
  regular, and a CNH exist.
* It enrolls the face with the CPF's digits as the subject identifier and `VerifyFaceIdentityExternally = true`; on
  `BioSubjectAlreadyEnrolled`, it starts a face authentication instead.
* Datavalid's result is not in the completion status: the demo reads it with `GetSessionDetailsAsync(sessionId)`, which needs an API key
  allowed to view bio sessions.

TODO(docs): no call sequence has been recorded for this demo, and no docs page covers the Datavalid check.

## See also

* [Create certificate orders](https://docs.lacunasoftware.com/en-us/articles/amplia/integration/orders)
* [Issue a PKCS #12 file](https://docs.lacunasoftware.com/en-us/articles/amplia/integration/pkcs12)
* [Errors](https://docs.lacunasoftware.com/en-us/articles/amplia/integration/errors)

**Demos and samples**

- Live demo: [Issue an e-CNPJ certificate](https://try.lacunasoftware.com/en/amplia/issue-e-cnpj-certificate)
- Live demo: [Issue an SSL/TLS certificate](https://try.lacunasoftware.com/en/amplia/issue-ssl-tls-certificate)
- Live demo: [Issue an e-CPF with your face](https://try.lacunasoftware.com/en/amplia/issue-an-ecpf-with-your-face)
- Issue a certificate with the key on the server using Amplia: [Java](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/java/springmvc/src/main/java/com/lacunasoftware/pkisuite/controller/IssueCertServerAmpliaController.java) · [Node.js](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/nodejs/expressmvc/routes/issue-cert-server-amplia.js) · [PHP](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/php/plain/public/issue-cert-server-amplia/index.php) · [Python](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/python/flask/sample/views/issue_cert_server_amplia.py)
