Using Web PKI with Rest PKI
With Rest PKI, your backend never handles the data to sign: it starts the signature on Rest PKI and
gets a token, your page passes that token to Web PKI's signWithRestPki(), and Web PKI exchanges the signature data
with Rest PKI directly. Your backend then finishes the signature with the same token and gets the signed document.
Certificate authentication works the same way.
Rest PKI Core serves these Rest PKI APIs too, so the same page works with it once it knows where the instance is (see The Rest PKI address). Rest PKI Core also has newer APIs, which sign with Web PKI in other ways: see With Rest PKI Core's newer APIs.
Rest PKI is a legacy product: it still receives bug and security fixes, but new cloud integrations should use Rest PKI Core.
How it works
| # | Who | What |
|---|---|---|
| 1 | Your backend → Rest PKI | Starts the signature (for instance PadesSignatureStarter.StartWithWebPkiAsync() in .NET), authenticated with your access token or API key. Rest PKI returns a token. |
| 2 | Your backend → page | Renders the signature page with the token. |
| 3 | Page | Calls init(), lists the user's certificates with listCertificates() and, once the user picks one, calls signWithRestPki() with the token and the certificate's thumbprint. |
| 4 | Web PKI → Rest PKI | From the user's computer, gets the pending signature (GET Api/PendingSignatures/{token}) and sends the certificate (POST Api/PendingSignatures/{token}/Certificate). Rest PKI validates the certificate against the security context of the signature and returns the hash to sign. |
| 5 | Web PKI → Rest PKI | Signs the hash with the certificate's private key and sends the signature (POST Api/PendingSignatures/{token}). |
| 6 | Page → your backend → Rest PKI | The page submits the token to your backend, which finishes the signature (for instance PadesSignatureFinisher2.FinishAsync()) and gets the signed document and the signer's certificate. |
This means that:
- Your access token stays on your server. The page only sees the token of that signature, which works once.
- The routes Web PKI calls take no access token and accept calls from any origin: the token identifies the signature.
- The user's computer must reach Rest PKI, not only your server. See Network requirements.
- If Rest PKI does not accept the certificate (for instance, it is not trusted by the security context), the error comes back
to the page, in
signWithRestPki().
The page code
Initialize Web PKI, list the certificates and, when the user clicks Sign, call signWithRestPki() with the token your
backend rendered on the page. When it succeeds, submit the token back to your backend:
var pki = new LacunaWebPKI(webPkiLicense); // not needed with Rest PKI in the cloud, see "License" below
pki.init({
restPkiUrl: restPkiUrl, // only for an on-premises Rest PKI or Rest PKI Core, see below
ready: loadCertificates,
defaultFail: onWebPkiFail
});
function loadCertificates() {
pki.listCertificates({ selectId: 'certificateSelect' });
}
function sign() {
pki.signWithRestPki({
token: token, // returned by Rest PKI when your backend started the signature
thumbprint: document.getElementById('certificateSelect').value
}).success(function () {
document.getElementById('signForm').submit(); // your backend finishes the signature with the token
});
}
function onWebPkiFail(ex) {
alert(ex.userMessage + ' (' + ex.code + ')');
}
The samples have the backend code for each language, and you can try the page in JSFiddle.
The Rest PKI address
Web PKI needs to know which Rest PKI instance to call. Set it in the restPkiUrl option of
init():
| Rest PKI | restPkiUrl |
|---|---|
| Rest PKI in the cloud (https://pki.rest) | Leave it out |
| Rest PKI on-premises | The address of your instance, such as 'https://restpki.yourcompany.com/' |
| Rest PKI Core, in the cloud or on-premises | The endpoint of your instance, such as 'https://core.pki.rest/' |
pki.init({
restPkiUrl: 'https://restpki.yourcompany.com/',
ready: onWebPkiReady
});
Without restPkiUrl, Web PKI calls the cloud instance through https://restpki.lacunasoftware.com/, an alternative
address of https://pki.rest that, being more conventional, is less likely to be blocked by the firewalls of the users'
networks.
If Web PKI is not used with Rest PKI, restPkiUrl is not needed.
Network requirements
Since Web PKI calls Rest PKI from the user's computer:
- With Rest PKI in the cloud, the users' networks must allow the domain
restpki.lacunasoftware.comover HTTPS. - With your own instance (Rest PKI on-premises or Rest PKI Core), it must be reachable from the users' computers, not only
from your server. To avoid being blocked, its address should:
- use a hostname, not an IP address;
- use HTTPS with a valid SSL certificate, issued by a Certification Authority that the operating systems trust by default (with an invalid certificate, the calls fail silently);
- use a standard port (443).
To check, open /Api/System/Info on the instance in a browser on the user's computer, such as
https://restpki.lacunasoftware.com/Api/System/Info. See Error "Could not get pending signature".
License
| Rest PKI | Web PKI license |
|---|---|
| Rest PKI in the cloud (https://pki.rest) | Not needed, even in production |
| Rest PKI on-premises | Needed outside localhost |
Rest PKI Core, including in the cloud (core.pki.rest) | Needed outside localhost |
See Licensing.
Try it in JSFiddle
The example below is the page of a Rest PKI signature: paste a token, choose a certificate and sign. To get a token without
writing a backend, start a CAdES signature of the text "Hello, World!" with curl, using your Rest PKI access token:
curl -X POST https://pki.rest/Api/CadesSignatures \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"contentToSign": "SGVsbG8sIFdvcmxkIQ==", "signaturePolicyId": "a4522485-c9e5-46c3-950b-0d6e951e17d1", "securityContextId": "803517ad-3bbc-4169-b085-60053a8f6dbf"}'
The response has the token. The policy is CAdES-BES, and the security context, Lacuna Test PKI, also accepts the
test certificates, so you can sign with one of them. Then open the example, paste the
token and click Sign with Rest PKI:
Once Web PKI has signed, finish the signature to get the CMS (.p7s file):
curl -X POST https://pki.rest/Api/CadesSignatures/THE_TOKEN/Finalize \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-d ""
The response has the signature in Base64 in cms, and the signer's certificate in certificate.
The example's code is in demo.html,
demo.js and demo.css. To try
it with your own Rest PKI instance or with Rest PKI Core, set restPkiUrl at the top of the JavaScript, and use that
instance's address and credentials (an API key, on Rest PKI Core) in the curl calls.
Errors
Besides the other Web PKI errors, signWithRestPki() can fail with:
| Code | Meaning |
|---|---|
rest_pki_get_pending_signature | Web PKI could not get the pending signature from Rest PKI. Usually Web PKI cannot reach the instance: check restPkiUrl and the network requirements. See Error "Could not get pending signature". |
rest_pki_post_signature | Web PKI could not send the signature to Rest PKI. |
rest_pki_invalid_certificate | Rest PKI did not accept the selected certificate, for instance because the security context of the signature does not trust it. |
A token works once: to sign again, start a new signature on your backend.
With Rest PKI Core's newer APIs
Rest PKI Core keeps serving the Rest PKI APIs that signWithRestPki() uses, and adds newer APIs that use Web PKI differently:
- Signature sessions: you redirect the user to a signing page hosted by Rest PKI Core, which uses Web PKI itself. Your pages have no Web PKI code.
- Two-step signatures: your backend prepares the
signature, your page signs the hash with Web PKI's
signHash(), and your backend completes the signature.
See Moving from Rest PKI to Rest PKI Core.
Samples
The Rest PKI samples, in each language below, sign with signWithRestPki(). See also
Rest PKI.