http://demo.webpki.org/mozkeygen
The 230 line on-line CA service is as follows: import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.security.SecureRandom; import java.security.PublicKey; import java.security.PrivateKey; import java.security.GeneralSecurityException; import java.security.KeyFactory; import java.security.cert.X509Certificate; import java.security.KeyStore; import java.security.Signature; import java.security.interfaces.RSAPublicKey; import java.security.spec.RSAPublicKeySpec; import java.util.GregorianCalendar; import java.util.Date; import java.math.BigInteger; import org.webpki.util.Base64; import org.webpki.asn1.BaseASN1Object; import org.webpki.asn1.ASN1Sequence; import org.webpki.asn1.ASN1BitString; import org.webpki.asn1.DerDecoder; import org.webpki.asn1.ParseUtil; import org.webpki.asn1.cert.DistinguishedName; import org.webpki.crypto.AsymEncryptionAlgorithms; import org.webpki.crypto.SignatureAlgorithms; import org.webpki.crypto.AsymKeySignerInterface; import org.webpki.crypto.CertificateExtensions; import org.webpki.crypto.KeyUsageBits; import org.webpki.crypto.test.DemoKeyStore; import org.webpki.ca.CertSpec; import org.webpki.ca.CA; @SuppressWarnings("serial") public class mozkeygen extends HttpServlet { static final String SUBJECT_DN = "CN=Just Me,C=US"; class CASignature implements AsymKeySignerInterface { KeyStore ks; CASignature () throws IOException { ks = DemoKeyStore.getSubCAKeyStore (); } public byte[] signData (byte[] data, SignatureAlgorithms sign_alg) throws IOException, GeneralSecurityException { Signature s = Signature.getInstance (sign_alg.getJCEName ()); s.initSign ((PrivateKey)ks.getKey ("mykey", DemoKeyStore.getSignerPassword ().toCharArray ())); s.update (data); return s.sign (); } public PublicKey getPublicKey () throws IOException, GeneralSecurityException { return ks.getCertificate ("mykey").getPublicKey (); } } void print (HttpServletRequest request, HttpServletResponse response, boolean error) throws IOException, ServletException { StringBuffer s = new StringBuffer (); s.append ("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\">" + "<html><head><title>Mozilla Firefox key generation demo</title>" + "<style type=\"text/css\">html, body {margin:0px;padding:0px;height:100%}" + "body {font-size:8pt;color:#000000;font-family:verdana,arial;background-color:white}" + "h2 {font-weight:bold;font-size:12pt;color:#000000;font-family:arial,verdana,helvetica}" + "h3 {font-weight:bold;font-size:11pt;color:#000000;font-family:arial,verdana,helvetica}" + "a:link {font-weight:bold;font-size:8pt;color:blue;font-family:arial,verdana;text-decoration:none}" + "a:visited {font-weight:bold;font-size:8pt;color:blue;font-family:arial,verdana;text-decoration:none}" + "a:active {font-weight:bold;font-size:8pt;color:blue;font-family:arial,verdana}" + "input {font-weight:normal; font-size:8pt;font-family:verdana,arial}" + "td {font-size:8pt;font-family:verdana,arial}" + ".smalltext {font-size:6pt;font-family:verdana,arial}" + "button {font-weight:normal;font-size:8pt;font-family:verdana,arial;padding-top:2px;padding-bottom:2px}" + ".headline {font-weight:bolder;font-size:11pt;font-family:arial,verdana}" + "</style><script language=\"javascript\">\n" + "function doit ()\n" + " {\n" + " if (window.crypto == null || window.crypto.generateCRMFRequest == null)\n" + " {\n" + " alert ('This doesn\\'t appear to be a Mozilla Firefox browser...');\n" + " }\n" + " else\n" + " {\n" + " var crmfObject = window.crypto.generateCRMFRequest ('CN=Just me', 'anythinggoes', 'somethingelse', " + "null, '', 1024, null, 'rsa-dual-use');\n" + " document.shoot.request.value = crmfObject.request;\n" + " document.shoot.submit ();\n" + " }\n" + " }\n" + "</script></head><body><table cellpadding=\"0\" cellspacing=\"0\" width=\"100%\" height=\"100%\">" + "<tr><td align=\"center\" valign=\"middle\"><table width=\"450\"><form method=\"POST\" name=\"shoot\" action=\""). append (request.getRequestURL ().toString ()). append ("\"><input type=\"hidden\" name=\"request\" value=\"hu\">" + "<tr><td class=\"headline\" align=\"center\">Mozilla Firefox Key-Generation Demo</td></tr>" + "<tr><td> </td></tr>"); if (error) { s.append ("<tr><td align=\"center\"><font color=\"red\"><b>There was some kind of error...<br> </b></font></td></tr>"); } s.append ("<tr><td align=\"left\">This web-application holds a minimalistic demo "+ "showing how you with one click on button can create a key-pair as well as " + "getting the public key certified by a CA and then installed in the browser key-store. " + "The demo uses Mozilla's proprietary <code>generateCRMFRequest()</code> JavaScript function. "+ "Other browsers have similar (but different) methods for performing such tasks.</td></tr>" + "<tr><td height=\"4\"></td></tr>" + "<tr><td align=\"left\">The subject DN of the issued certificate will be "" + SUBJECT_DN + "".</td></tr>" + "<tr><td height=\"4\"></td></tr>" + "<tr><td align=\"left\">Note: <i>If you have multiple cryptographic providers installed, you should select the one named " + ""Software Security Device" (you will be prompted)</i>.</td></tr>" + "<tr><td> </td></tr>" + "<tr><td align=\"center\"><input type=\"button\" value=\"Give me a certificate please!\" onclick=\"doit()\"></td></tr>" + "<tr><td height=\"20\"></td></tr>" + "<tr><td align=\"left\">After successfully obtaining a certificate you may immediately try it out at:<br>" + "<a href=\"https://www.apache-ssl.org/cgi/cert-export\">https://www.apache-ssl.org/cgi/cert-export</a></td></tr>" + "</td></tr></table></td></tr></form></table></body></html>"); response.setContentType ("text/html; charset=utf-8"); response.setHeader ("Pragma", "No-Cache"); response.setDateHeader ("EXPIRES", -1); response.getOutputStream ().print (s.toString ()); } public void doGet (HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { print (request, response, false); } public void doPost (HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { String crmf64 = request.getParameter ("request"); if (crmf64 == null || crmf64.startsWith ("error:") || !crmf64.startsWith ("MII")) { print (request, response, true); return; } byte[] crmf = new Base64 ().getBinaryFromBase64String (crmf64); ASN1Sequence o = ParseUtil.sequence (ParseUtil.sequence (ParseUtil.sequence (ParseUtil.sequence (DerDecoder.decode (crmf)).get (0)).get (0)).get (1)); PublicKey pub_key = null; KeyUsageBits[] key_usage = null; try { for (int i = 0; i < o.size (); i++) { if (ParseUtil.isCompositeContext (o.get (i), 6)) { BaseASN1Object pub_key_cc = ParseUtil.compositeContext (o.get (i), 6, 2); ParseUtil.seqOIDNull (pub_key_cc.get (0), AsymEncryptionAlgorithms.RSA_PKCS_1.getOID ()); ASN1Sequence pub_key_asn1 = ParseUtil.sequence (DerDecoder.decode (ParseUtil.bitstring (pub_key_cc.get (1)))); pub_key = KeyFactory.getInstance ("RSA").generatePublic ( new RSAPublicKeySpec (ParseUtil.integer (pub_key_asn1.get (0)).value (), ParseUtil.integer (pub_key_asn1.get (1)).value ())); break; } } if (pub_key == null) { throw new IOException ("No public key found!"); } for (int i = 0; i < o.size (); i++) { if (ParseUtil.isCompositeContext (o.get (i), 9)) { ASN1Sequence key_usage_asn1 = ParseUtil.sequence (ParseUtil.compositeContext (o.get (i), 9, 1).get (0), 3); ParseUtil.oid (key_usage_asn1.get (0), CertificateExtensions.KEY_USAGE.getOID ()); ASN1BitString ku = (ASN1BitString) DerDecoder.decode (ParseUtil.octet (key_usage_asn1.get (2))); byte[] value = ku.value (); if (value.length != 1 || (value[0] & 0xFF) != 0xE0) { throw new IOException ("Unexpected key usage!"); } key_usage = new KeyUsageBits[] {KeyUsageBits.digitalSignature, KeyUsageBits.nonRepudiation, KeyUsageBits.keyEncipherment}; break; } } if (key_usage == null) { throw new IOException ("No key usage found!"); } CertSpec cert_spec = new CertSpec (); for (KeyUsageBits kub : key_usage) { cert_spec.setKeyUsageBit (kub); } cert_spec.setEndEntityConstraint (); cert_spec.setSubject (SUBJECT_DN); GregorianCalendar start = new GregorianCalendar (); start.set (GregorianCalendar.DAY_OF_YEAR, 1); start.set (GregorianCalendar.HOUR_OF_DAY, 0); start.set (GregorianCalendar.HOUR, 0); start.set (GregorianCalendar.SECOND, 0); start.set (GregorianCalendar.MINUTE, 0); GregorianCalendar end = (GregorianCalendar) start.clone (); end.set (GregorianCalendar.YEAR, end.get (GregorianCalendar.YEAR) + 5); X509Certificate signer_cert = new CA ().createCert (cert_spec, DistinguishedName.subjectDN ((X509Certificate) DemoKeyStore.getSubCAKeyStore ().getCertificate ("mykey")), new BigInteger (String.valueOf (new Date ().getTime ())), start.getTime (), end.getTime (), SignatureAlgorithms.RSA_SHA1, new CASignature (), pub_key); response.setContentType ("application/x-x509-user-cert"); response.setHeader ("Pragma", "No-Cache"); response.setDateHeader ("EXPIRES", -1); response.getOutputStream ().write (signer_cert.getEncoded ()); } catch (GeneralSecurityException gse) { throw new IOException (gse.getMessage ()); } } } _______________________________________________ dev-tech-crypto mailing list dev-tech-crypto@lists.mozilla.org https://lists.mozilla.org/listinfo/dev-tech-crypto