Build out certificate issuing.
authorFelix Dörre <felix@dogcraft.de>
Mon, 14 Jul 2014 20:13:45 +0000 (22:13 +0200)
committerFelix Dörre <felix@dogcraft.de>
Tue, 15 Jul 2014 22:55:51 +0000 (00:55 +0200)
doc/exoticKeys.sh [new file with mode: 0644]
src/org/cacert/gigi/Digest.java [new file with mode: 0644]
src/org/cacert/gigi/output/Form.java
src/org/cacert/gigi/output/template/HashAlgorithms.java [new file with mode: 0644]
src/org/cacert/gigi/pages/account/IssueCertificateForm.java [new file with mode: 0644]
src/org/cacert/gigi/pages/account/IssueCertificateForm.templ [moved from src/org/cacert/gigi/pages/account/MailCertificateAdd.templ with 85% similarity]
src/org/cacert/gigi/pages/account/MailCertificateAdd.java
src/org/cacert/gigi/pages/account/RequestCertificate.templ [new file with mode: 0644]
static/static/default.css

diff --git a/doc/exoticKeys.sh b/doc/exoticKeys.sh
new file mode 100644 (file)
index 0000000..caa4c76
--- /dev/null
@@ -0,0 +1,7 @@
+#!/bin/bash
+openssl ecparam -out ec.pem -name secp521r1 -genkey
+openssl req -new -key ec.pem -out ec.csr -subj "/CN=bla"
+
+openssl dsaparam -genkey 1024 -out dsa.pem
+openssl req -new -key dsa.pem -out dsa.csr -subj "/CN=bla"
+
diff --git a/src/org/cacert/gigi/Digest.java b/src/org/cacert/gigi/Digest.java
new file mode 100644 (file)
index 0000000..e183be7
--- /dev/null
@@ -0,0 +1,21 @@
+package org.cacert.gigi;
+
+public enum Digest {
+       SHA256("Currently recommended, because the other algorithms"
+               + " might break on some older versions of the GnuTLS library"
+               + " (older than 3.x) still shipped in Debian for example."), SHA384(null), SHA512(
+               "Highest protection against hash collision attacks of the algorithms offered here.");
+       final String exp;
+
+       private Digest(String explanation) {
+               exp = explanation;
+       }
+
+       public String getExp() {
+               return exp;
+       }
+
+       public static Digest getDefault() {
+               return SHA256;
+       }
+}
index 11209ca51146769f15ec27654556db9343a80260..2ffb873171216efa91148d7798ab3b69e7029638 100644 (file)
@@ -12,6 +12,7 @@ import org.cacert.gigi.pages.Page;
 import org.cacert.gigi.util.RandomToken;
 
 public abstract class Form implements Outputable {
+       public static final String CSRF_FIELD = "csrf";
        String csrf;
 
        public Form(HttpServletRequest hsr) {
@@ -27,7 +28,7 @@ public abstract class Form implements Outputable {
        public final void output(PrintWriter out, Language l, Map<String, Object> vars) {
                out.println("<form method='POST' autocomplete='off'>");
                outputContent(out, l, vars);
-               out.print("<input type='hidden' name='csrf' value='");
+               out.print("<input type='hidden' name='" + CSRF_FIELD + "' value='");
                out.print(getCSRFToken());
                out.println("'></form>");
        }
@@ -45,13 +46,13 @@ public abstract class Form implements Outputable {
        }
 
        protected void checkCSRF(HttpServletRequest req) {
-               if (!csrf.equals(req.getParameter("csrf"))) {
+               if (!csrf.equals(req.getParameter(CSRF_FIELD))) {
                        throw new CSRFError();
                }
        }
 
        public static <T extends Form> T getForm(HttpServletRequest req, Class<T> target) {
-               String csrf = req.getParameter("csrf");
+               String csrf = req.getParameter(CSRF_FIELD);
                if (csrf == null) {
                        throw new CSRFError();
                }
diff --git a/src/org/cacert/gigi/output/template/HashAlgorithms.java b/src/org/cacert/gigi/output/template/HashAlgorithms.java
new file mode 100644 (file)
index 0000000..453e0d0
--- /dev/null
@@ -0,0 +1,30 @@
+package org.cacert.gigi.output.template;
+
+import java.util.Map;
+
+import org.cacert.gigi.Digest;
+import org.cacert.gigi.Language;
+
+public class HashAlgorithms implements IterableDataset {
+
+       int i = 0;
+       Digest selected;
+
+       public HashAlgorithms(Digest selected) {
+               this.selected = selected;
+       }
+
+       @Override
+       public boolean next(Language l, Map<String, Object> vars) {
+               Digest[] length = Digest.values();
+               if (i >= length.length) {
+                       return false;
+               }
+               Digest d = length[i++];
+               vars.put("algorithm", d.toString());
+               vars.put("name", d.toString());
+               vars.put("info", l.getTranslation(d.getExp()));
+               vars.put("checked", selected == d ? " checked='checked'" : "");
+               return true;
+       }
+}
diff --git a/src/org/cacert/gigi/pages/account/IssueCertificateForm.java b/src/org/cacert/gigi/pages/account/IssueCertificateForm.java
new file mode 100644 (file)
index 0000000..e8e5000
--- /dev/null
@@ -0,0 +1,143 @@
+package org.cacert.gigi.pages.account;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.security.GeneralSecurityException;
+import java.security.PublicKey;
+import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAPublicKey;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Base64;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.cacert.gigi.Certificate;
+import org.cacert.gigi.Digest;
+import org.cacert.gigi.Language;
+import org.cacert.gigi.User;
+import org.cacert.gigi.database.DatabaseConnection;
+import org.cacert.gigi.output.Form;
+import org.cacert.gigi.output.template.HashAlgorithms;
+import org.cacert.gigi.output.template.IterableDataset;
+import org.cacert.gigi.output.template.Template;
+import org.cacert.gigi.pages.LoginPage;
+
+import sun.security.pkcs10.PKCS10;
+
+/**
+ * This class represents a form that is used for issuing certificates.
+ * 
+ * This class uses "sun.security" and therefore needs "-XDignore.symbol.file"
+ * 
+ */
+public class IssueCertificateForm extends Form {
+       User u;
+       Digest selectedDigest = Digest.getDefault();
+
+       private final static Template t = new Template(IssueCertificateForm.class.getResource("IssueCertificateForm.templ"));
+
+       public IssueCertificateForm(HttpServletRequest hsr) {
+               super(hsr);
+               u = LoginPage.getUser(hsr);
+       }
+
+       @Override
+       public boolean submit(PrintWriter out, HttpServletRequest req) {
+               String csr = req.getParameter("CSR");
+               String spkac = req.getParameter("spkac");
+               try {
+                       if (csr != null && !"".equals(csr)) {
+                               PKCS10 parsed = parseCSR(csr);
+                               out.println(parsed.getSubjectName().getCommonName());
+                               out.println(parsed.getSubjectName().getCountry());
+                               out.println("CSR DN: " + parsed.getSubjectName() + "<br/>");
+                               PublicKey pk = parsed.getSubjectPublicKeyInfo();
+                               out.println("Type: " + pk.getAlgorithm() + "<br/>");
+                               if (pk instanceof RSAPublicKey) {
+                                       out.println("Exponent: " + ((RSAPublicKey) pk).getPublicExponent() + "<br/>");
+                                       out.println("Length: " + ((RSAPublicKey) pk).getModulus().bitLength());
+                               } else if (pk instanceof DSAPublicKey) {
+                                       DSAPublicKey dpk = (DSAPublicKey) pk;
+                                       out.println("Length: " + dpk.getY().bitLength() + "<br/>");
+                                       out.println(dpk.getParams());
+                               } else if (pk instanceof ECPublicKey) {
+                                       ECPublicKey epk = (ECPublicKey) pk;
+                                       out.println("Length-x: " + epk.getW().getAffineX().bitLength() + "<br/>");
+                                       out.println("Length-y: " + epk.getW().getAffineY().bitLength() + "<br/>");
+                                       out.println(epk.getParams().getCurve());
+                               }
+                               out.println("<br/>digest: sha256<br/>");
+
+                       }
+               } catch (IOException e) {
+                       e.printStackTrace();
+               } catch (GeneralSecurityException e) {
+                       e.printStackTrace();
+               }
+               return false;
+       }
+
+       private void issue(HttpServletRequest req, HttpServletResponse resp, String csr) throws IOException {
+               Certificate c = new Certificate(LoginPage.getUser(req).getId(), "/commonName=CAcert WoT User", "sha256", csr);
+               c.issue();
+               try {
+                       c.waitFor(60000);
+                       resp.sendRedirect(MailCertificates.PATH + "/" + c.getSerial());
+               } catch (SQLException e) {
+                       e.printStackTrace();
+               } catch (InterruptedException e) {
+                       e.printStackTrace();
+               }
+       }
+
+       private PKCS10 parseCSR(String csr) throws IOException, GeneralSecurityException {
+               csr = csr.replaceFirst("-----BEGIN (NEW )?CERTIFICATE REQUEST-----", "");
+               csr = csr.replaceFirst("-----END (NEW )?CERTIFICATE REQUEST-----", "");
+               csr = csr.replace("\r", "");
+               csr = csr.replace("\n", "");
+               byte[] b = Base64.getDecoder().decode(csr);
+               // Also checks signature validity
+               return new PKCS10(b);
+       }
+
+       @Override
+       protected void outputContent(PrintWriter out, Language l, Map<String, Object> vars) {
+               HashMap<String, Object> vars2 = new HashMap<String, Object>(vars);
+               vars2.put("CCA", "<a href='/policy/CAcertCommunityAgreement.html'>CCA</a>");
+
+               try {
+                       PreparedStatement ps = DatabaseConnection.getInstance().prepare(
+                               "SELECT `id`,`email` from `email` WHERE `memid`=? AND `deleted`=0");
+                       ps.setInt(1, u.getId());
+                       final ResultSet rs = ps.executeQuery();
+                       vars2.put("emails", new IterableDataset() {
+
+                               @Override
+                               public boolean next(Language l, Map<String, Object> vars) {
+                                       try {
+                                               if (!rs.next()) {
+                                                       return false;
+                                               }
+                                               vars.put("id", rs.getString(1));
+                                               vars.put("value", rs.getString(2));
+                                               return true;
+                                       } catch (SQLException e) {
+                                               e.printStackTrace();
+                                       }
+                                       return false;
+                               }
+                       });
+                       vars2.put("hashs", new HashAlgorithms(selectedDigest));
+                       t.output(out, l, vars2);
+               } catch (SQLException e) {
+                       e.printStackTrace();
+               }
+       }
+
+}
similarity index 85%
rename from src/org/cacert/gigi/pages/account/MailCertificateAdd.templ
rename to src/org/cacert/gigi/pages/account/IssueCertificateForm.templ
index 6ce1b15b1597fe104570f4b62d3cc3f4300cfe09..575066fb130e42ebec0bdb7512392f5d389da479 100644 (file)
   <tr class="expert">
     <td colspan="2" align="left">
       <?=_Hash algorithm used when signing the certificate:?><br />
-      <!-- Hash algs!!! -->
+      <? foreach($hashs) { ?>
+        <input type="radio" id="hash_alg_<?=$algorithm?>" name="hash_alg" value="<?=$algorithm?>"<?=$checked?>/>
+        <label for="hash_alg_<?=$algorithm?>"><?=$name?><? if($info) { ?> - <?=$info?><? } ?></label><br />
+      <? } ?>
     </td>
   </tr>
 
   </tr>
 <? } ?>
 
-  <tr class="expert">
-    <td>
-      <input type="checkbox" id="SSO" name="SSO" value="1" />
-    </td>
-    <td align="left">
-      <label for="SSO"><?=_Add Single Sign On ID Information?><br />
-      <?=_By adding Single Sign On (SSO) ID information to your certificates this could be used to track you, you can also issue certificates with no email addresses that are useful only for Authentication. Please see a more detailed description on our WIKI about it.?>
-      <a href="http://wiki.cacert.org/wiki/SSO"><?=_SSO WIKI Entry?></a></label>
-    </td>
-  </tr>
-
-  <tr class="expert">
-    <td colspan="2">
-      <label for="optionalCSR"><?=_Optional Client CSR, no information on the certificate will be used?></label><br />
-      <textarea id="optionalCSR" name="optionalCSR" cols="80" rows="5"></textarea>
-    </td>
-  </tr>
-
-
   <tr>
     <td>
       <input type="checkbox" id="CCA" name="CCA" />
index 5a343954fb2b719032d331f9a7cd720881621212..4a9a2c826398f29f39c36d420d524e3cb251dce3 100644 (file)
@@ -1,27 +1,28 @@
 package org.cacert.gigi.pages.account;
 
 import java.io.IOException;
-import java.io.PrintWriter;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import org.cacert.gigi.Certificate;
 import org.cacert.gigi.Language;
 import org.cacert.gigi.User;
 import org.cacert.gigi.database.DatabaseConnection;
-import org.cacert.gigi.output.ClientCSRGenerate;
 import org.cacert.gigi.output.template.IterableDataset;
 import org.cacert.gigi.pages.LoginPage;
+import org.cacert.gigi.output.Form;
+import org.cacert.gigi.output.template.Template;
 import org.cacert.gigi.pages.Page;
 
 public class MailCertificateAdd extends Page {
        public static final String PATH = "/account/certs/email/new";
+       Template t = new Template(MailCertificateAdd.class.getResource("RequestCertificate.templ"));
 
        public MailCertificateAdd() {
                super("Create Email certificate");
@@ -64,27 +65,14 @@ public class MailCertificateAdd extends Page {
 
        @Override
        public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
-               PrintWriter out = resp.getWriter();
-               if (req.getParameter("optionalCSR") == null || req.getParameter("optionalCSR").equals("")) {
-                       out.println("csr missing");
-                       ClientCSRGenerate.output(req, resp);
-               }
-               String csr = req.getParameter("optionalCSR");
-               if (!"on".equals(req.getParameter("CCA"))) {
-                       // Error.
-                       return;
-               }
-               Certificate c = new Certificate(LoginPage.getUser(req).getId(), "/commonName=CAcert WoT User", "sha256", csr);
-               c.issue();
-               try {
-                       c.waitFor(60000);
-                       resp.sendRedirect(MailCertificates.PATH + "/" + c.getSerial());
-               } catch (SQLException e) {
-                       e.printStackTrace();
-               } catch (InterruptedException e) {
-                       e.printStackTrace();
+               IssueCertificateForm f;
+               if (req.getParameter(Form.CSRF_FIELD) != null) {
+                       f = Form.getForm(req, IssueCertificateForm.class);
+               } else {
+                       f = new IssueCertificateForm(req);
+                       f.submit(resp.getWriter(), req);
                }
+               f.output(resp.getWriter(), getLanguage(req), Collections.<String, Object> emptyMap());
 
        }
-
 }
diff --git a/src/org/cacert/gigi/pages/account/RequestCertificate.templ b/src/org/cacert/gigi/pages/account/RequestCertificate.templ
new file mode 100644 (file)
index 0000000..2a32963
--- /dev/null
@@ -0,0 +1,24 @@
+
+<table class="wrapper dataTable">
+  <thead>
+  <tr>
+    <th colspan="2" class="title"><?=_New Certificate?></th>
+  </tr>
+  </thead>
+  <tbody>
+  <tr>
+    <td><?=_CSR?></td>
+    <td><?=_SPKAC?></td>
+  </tr>
+
+  <tr>
+    <td>
+     <form method="post"> <textarea name="CSR"></textarea><br/><input type="submit" name="process" value="<?=_Next?>" /></form>
+    </td>
+    <td align="left">
+     <form method="post"> <keygen name="SPKAC"/><br/><input type="submit" name="process" value="<?=_Next?>" /></form>
+      
+    </td>
+  </tr>
+  </tbody>
+</table>
index 20aeac425d9bca7faf808eb3f9acf674afc11d7c..2f0bf528be20a935904be26083b301b141273d40 100644 (file)
@@ -758,3 +758,6 @@ pre{
 .dataTable select, .dataTable option {
        font-size: 92%;
 }
+.dataTable textarea{
+       width: 100%;
+}