Browser-based Key Generation and interaction with the Browser’s Key/Certificate Store
Imagine the following scenario:
You need to get a key (in the asymmetric case the user’s public key) from a user visiting your website and want the browser to remember the private part without bothering the user with lengthy import procedures. To be honest, in practice you don’t even want the user to deal with cryptographic details – details that many users are not able to know and do not know anything about it rightly. It should simply work and in the best case the user shouldn’t even notice that crypto is doing it’s magic!
Imagine for example an enterprise-wide Certification Authority where employees can apply for certificates or renew existing ones which are required to log on to the corporate webmail system. This is not part of the employee’s primary tasks, so the process has to be simple, quick and without the need to read any
documentation.Thank god – or in this case Netscape – HTML ships, since HTML 5 even browser independent, as part of the official standards, with a tag dedicated to key generation: <KeyGen />. To put it in a nutshell, the tag can force a user’s browser to create an asymmetric key pair, sign the corresponding public key and a server provided challenge and finally send it back to the server (more precisely to the location defined in the form action attribute). The private key is automatically encrypted and stored in the browser’s key store. The format used for public key, challenge and signature encapsulation is called SPKAC. If the server responds with a X.509 certificate the certificate is directly linked to the private key and stored in the browser’s certificate store.
As a result, the browser is now in possession of a server supplied (maybe newly created) certificate and the corresponding private key. And all of this with a single, simple click on the submit button. (Yeah, maybe the user should also add some details into form fields…)And here is how to do this in Java. For the sake of simplicity we will use a quick and dirty solution on server side, by directly registering as a Servlet.
At first, we will start with the initial website which presents a form where some details of the certificate requester have to be entered. Please note that these form fields are NOT protected by the signature with the newly generated private key! This signature only protects the public key and the challenge which in this case is – for the sake of simplicity – hard coded, but has to be a fresh value chosen by the server in a real world scenario. Hence, it is essential to protect both: the delivery of the website including the form (it contains security critical values, the challenge, the form action, … which must be integrity protected) and the transfer of the data back to the server. Once again, if you fail to protect the communication and ensure at least the security goal integrity, an attacker may break your whole security concept!
<form action="CreateCertificate" method="POST"> <table> <tbody> <tr> <td>Country name</td> <td>C</td> <td><input name="c" type="text" value="" /></td> </tr> <tr> <td>Common name</td> <td>CN</td> <td><input name="cn" type="text" value="" /></td> </tr> <tr> <td>Organizational unit</td> <td>OU</td> <td><input name="ou" type="text" value="" /></td> </tr> <tr> <td>Organization</td> <td>O</td> <td><input name="o" type="text" value="" /></td> </tr> <tr> <td></td> <td><keygen challenge="replaceMe" keyparams="2048" keytype="rsa" name="newSPKAC"></keygen></td> <td><input type="submit" value="Generate!" /></td> </tr> </tbody> </table> </form>
On server side it is necessary to register a processing class for the form action target. This is done in your web.xml configuration file.
<web-app version="3.0" xmlns:xsi="..." xmlns="..." xsi:schemalocation="..."> <servlet> <servlet-name>CreateCertificate</servlet-name> <servlet-class> com.blogspot.armoredbarista.examples.certificates.CreateCertificate </servlet-class> </servlet> <servlet-mapping> <servlet-name>CreateCertificate</servlet-name> <url-pattern>/CreateCertificate</url-pattern> </servlet-mapping> <session-config> <session-timeout> 30 </session-timeout> </session-config> </web-app>
This causes any call to the path /CreateCertificate to be handled by the CreateCertificate class. The CreateCertificate class in turn, does what we expect from the name: it creates a certificate including the received public key and the requester’s details. The public key and the challenge are included in the SPKAC structure, created by the KeyGen tag (which in this case is identified by the name newSPKAC).
public class CreateCertificate extends HttpServlet { /** * Processes requests for both HTTPGET
andPOST
methods. * * @param request servlet request * @param response servlet response * @throws ServletException if a servlet-specific error occurs * @throws IOException if an I/O error occurs */ protected void processRequest(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { OutputStream out = response.getOutputStream(); byte[] content = "An error occured".getBytes("UTF-8"); try { String c = request.getParameter("c"); String cn = request.getParameter("cn"); String o = request.getParameter("o"); String ou = request.getParameter("ou"); String newSPKAC = request.getParameter("newSPKAC"); X509Certificate cert = createCertificate(c, cn, ou, o, newSPKAC); content = cert.getEncoded(); response.setContentType("application/x-x509-user-cert"); response.setHeader("Pragma", "No-Cache"); response.setDateHeader("EXPIRES", -1); } catch (...) { // error processing } finally { out.write(content); out.flush(); out.close(); } } /** * Handles the HTTP *GET
method. * * @param request servlet request * @param response servlet response * @throws ServletException if a servlet-specific error occurs * @throws IOException if an I/O error occurs */ @Override protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } /** * Handles the HTTP *POST
method. * * @param request servlet request * @param response servlet response * @throws ServletException if a servlet-specific error occurs * @throws IOException if an I/O error occurs */ @Override protected void doPost(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } }
The ContentType used for the response is in this case application/x-x509-user-cert.
And that’s all. After the user has entered his details and clicked on the button, he should find a new certificate in the browser’s certificate store:
A final word on the fiddly disassembling of SPKAC: BouncyCastle may help you fighting the ASN.1 and DER beast!
hi
can you please me how to resolve this line
createCertificate(c, cn, ou, o, newSPKAC);
createCertificate method not find.
thank you.