Certificate authentication - PKI Express
Users sign in to your application with their digital certificate: PKI Express issues a nonce (a random value), Web PKI
signs it with the user's certificate in the browser, and PKI Express validates the signature and the certificate.
PKI Express added this in 1.4.0, with the start-auth and complete-auth commands.
The code is Java, from the PKI Suite samples, with com.lacunasoftware.pkiexpress:pki-express
(1.20.0); the browser script is the same in the Node.js sample.
For the method itself, see Certificate authentication in the PKI guide.
Flow
- Server:
start()returns the nonce and the digest algorithm; the page receives both in hidden fields. - Browser: Web PKI lists the user's certificates, reads the one they pick and signs the nonce with
signData(); the script fills in the certificate and the signature and submits the form. - Server:
complete()with the nonce, the certificate and the signature returns the validation results and the certificate. - Server: if the results are valid, find your user by a field of the certificate and sign them in.
1. Start the authentication
Authentication auth = new Authentication();
Util.setPkiDefaults(auth);
AuthStartResult result = auth.start();
Util.setNoCacheHeaders(response);
model.addAttribute("nonce", result.getNonce());
model.addAttribute("digestAlgorithm", result.getDigestAlgorithm());
- The nonce works for a single authentication attempt. The sample sends headers that keep the browser from caching the page
(
Cache-Control: no-cache, no-store, must-revalidate,Pragma: no-cache,Expires: -1), so that the back button doesn't show an old nonce. Util.setPkiDefaults()applies the trusted roots, which decide which certificates are accepted (see Which certificates are trusted).- PKI Express keeps the nonces it issued in a nonce store, the
transferDataFolder(by default the temporary folder), to check them oncomplete(). To keep the nonces in your own store and check them yourself, turn on external storage. The Python library (1.10.4) inverts this option: it passes the nonce store only when external storage is on. TODO(docs): whether PKI Express checks nonce reuse when no nonce store is given.
2. Sign the nonce in the browser
The page puts nonce and digestAlgorithm in hidden fields, with fields for the certificate (certContent) and the signature
(signature).
When the user signs in, the script reads the chosen certificate, signs the nonce with signData() and submits the form:
var selectedCertThumbprint = formElements.certificateSelect.val();
pki.readCertificate(selectedCertThumbprint).success(function (certEncoded) {
pki.signData({
thumbprint: formElements.certificateSelect.val(),
data: formElements.nonceField.val(),
digestAlgorithm: formElements.digestAlgorithmField.val()
}).success(function (signature) {
formElements.signatureField.val(signature);
formElements.certContentField.val(certEncoded);
formElements.form.submit();
});
});
- The nonce is the data to sign, not a hash, so the script uses
signData, notsignHash. See the difference between signData and signHash. - Before this, the script initializes Web PKI with
pki.init()and lists the certificates withlistCertificates(), marking expired ones.
3. Complete the authentication
Authentication auth = new Authentication();
Util.setPkiDefaults(auth);
auth.setNonce(nonce);
auth.setCertificateBase64(certContent);
auth.setSignature(signature);
AuthCompleteResult result = auth.complete();
ValidationResults vr = result.getValidationResults();
if (!vr.isValid()) {
String vrHtml = vr.toString()
.replaceAll("\n", "<br>")
.replaceAll("\t", " ");
model.addAttribute("vrHtml", vrHtml);
return "authentication-express/failed";
}
model.addAttribute("userCert", result.getCertificate());
- Apply the same trust settings as at the start: the sample calls
Util.setPkiDefaults()in both steps. - The other libraries name the inputs differently: Node.js
setNonce,setCertificateFromBase64(returns a Promise) andsetSignature; PHPsetNonce,setCertificateBase64andsetSignature; Python the propertiesnonce_base64,certificate_base64andsignature_base64. - An unknown nonce fails with return code 28 (
NonceNotFound), and a bad signature with 08 (BadSignature). See Return codes.
4. Sign the user in
When the results are valid, look up your user by one of the certificate's fields, such as getEmailAddress() or, for ICP-Brasil
certificates, getPkiBrazil().getCpf(); which field depends on your application.
The sample's success page shows the subject's common name, the email, the ICP-Brasil certificate type, CPF, responsável, company, CNPJ, RG and
OAB.
See also
- Validate signatures
- Certificate authentication in the PKI guide
- PKI Express quick reference