]> WPIA git - gigi.git/commitdiff
Merge changes I46ae11f8,I6d71e70e,Ie19e3229
authorBenny Baumann <BenBE1987@gmx.net>
Tue, 7 Nov 2017 19:55:40 +0000 (20:55 +0100)
committerGerrit Code Review <gigi-system@dogcraft.de>
Tue, 7 Nov 2017 19:55:40 +0000 (20:55 +0100)
* changes:
  chg: remove csr_name and crt_name columns from certs
  chg: use certificate attachment to store CRT and CSR files
  add: text-attachments for certificates

src/club/wpia/gigi/dbObjects/CACertificate.java
src/club/wpia/gigi/pages/RootCertPage.java
src/club/wpia/gigi/pages/RootCertPage.templ
tests/club/wpia/gigi/pages/account/TestCertificateAdd.java
tests/club/wpia/gigi/testUtils/ConfiguredTest.java

index 5953059f2617ff7c9e319fa4991f7008c42c16e3..1240cd896e97a902d9eb33da7b1e37e2e49d13c7 100644 (file)
@@ -8,6 +8,7 @@ import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
 import java.util.ArrayDeque;
+import java.util.Arrays;
 import java.util.Deque;
 import java.util.HashMap;
 
@@ -30,6 +31,10 @@ public class CACertificate implements IdCachable {
 
     private final String link;
 
+    private static final CACertificate[] instances;
+
+    private static ObjectCache<CACertificate> myCache = new ObjectCache<>();
+
     private CACertificate(int id) {
         this.id = id;
         int parentRoot;
@@ -78,6 +83,17 @@ public class CACertificate implements IdCachable {
     static {
         try {
             update();
+            try (GigiPreparedStatement q = new GigiPreparedStatement("SELECT `id` FROM `cacerts`", true)) {
+                GigiResultSet res = q.executeQuery();
+                res.last();
+                CACertificate[] certs = new CACertificate[res.getRow()];
+                res.beforeFirst();
+                int i = 0;
+                while (res.next()) {
+                    certs[i++] = getById(res.getInt(1));
+                }
+                instances = certs;
+            }
         } catch (CertificateException e) {
             throw new Error(e);
         } catch (FileNotFoundException e) {
@@ -161,8 +177,6 @@ public class CACertificate implements IdCachable {
         return id;
     }
 
-    private static ObjectCache<CACertificate> myCache = new ObjectCache<>();
-
     public String getKeyname() {
         return keyname;
     }
@@ -183,4 +197,8 @@ public class CACertificate implements IdCachable {
         return this == getParent();
     }
 
+    public static synchronized CACertificate[] getAll() {
+        return Arrays.copyOf(instances, instances.length);
+    }
+
 }
index c821e7c426aa6c13e757b824a229c232980282a9..9d1d9c90eff4ec01f5f7ba1fc22adac68048a25e 100644 (file)
@@ -1,29 +1,96 @@
 package club.wpia.gigi.pages;
 
 import java.io.IOException;
+import java.io.PrintWriter;
 import java.security.KeyStore;
 import java.security.KeyStoreException;
 import java.security.cert.Certificate;
 import java.security.cert.CertificateEncodingException;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
 
 import javax.servlet.ServletOutputStream;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import club.wpia.gigi.dbObjects.CACertificate;
+import club.wpia.gigi.localisation.Language;
+import club.wpia.gigi.output.template.Outputable;
+import club.wpia.gigi.util.HTMLEncoder;
 import club.wpia.gigi.util.PEM;
 
 public class RootCertPage extends Page {
 
-    private Certificate root;
+    private final Certificate root;
+
+    private final CACertificate[] cs;
+
+    private final OutputableCertificate rootP;
+
+    private class OutputableCertificate implements Outputable {
+
+        private final CACertificate target;
+
+        private final OutputableCertificate[] children;
+
+        public OutputableCertificate(CACertificate c) {
+            target = c;
+            LinkedList<OutputableCertificate> children = new LinkedList<>();
+            for (CACertificate c0 : cs) {
+                if (c0.getParent() == c && c0 != c) {
+                    children.add(new OutputableCertificate(c0));
+                }
+            }
+
+            Collections.sort(children, new Comparator<OutputableCertificate>() {
+
+                @Override
+                public int compare(OutputableCertificate o1, OutputableCertificate o2) {
+                    return o1.target.getKeyname().compareTo(o2.target.getKeyname());
+                }
+            });
+            this.children = children.toArray(new OutputableCertificate[children.size()]);
+        }
+
+        @Override
+        public void output(PrintWriter out, Language l, Map<String, Object> vars) {
+            out.println("<a href='" + HTMLEncoder.encodeHTML(target.getLink()) + "'>");
+            out.println(HTMLEncoder.encodeHTML(target.getKeyname()));
+            out.println("</a>");
+            out.println(HTMLEncoder.encodeHTML(target.getCertificate().getSubjectX500Principal().toString()));
+            out.println("<ul>");
+            for (OutputableCertificate c : children) {
+                out.print("<li>");
+                c.output(out, l, vars);
+                out.print("</li>");
+            }
+            out.println("</ul>");
+        }
+
+    }
 
     public RootCertPage(KeyStore ks) {
         super("Root Certificates");
         try {
             root = ks.getCertificate("root");
         } catch (KeyStoreException e) {
-            e.printStackTrace();
+            throw new Error(e);
+        }
+        cs = CACertificate.getAll();
+        CACertificate rootC = null;
+        for (CACertificate c : cs) {
+            if (c.isSelfsigned()) {
+                rootC = c;
+                break;
+            }
+        }
+        if (rootC == null) {
+            throw new Error();
         }
+        rootP = new OutputableCertificate(rootC);
     }
 
     @Override
@@ -52,7 +119,9 @@ public class RootCertPage extends Page {
 
     @Override
     public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
-        getDefaultTemplate().output(resp.getWriter(), getLanguage(req), new HashMap<String, Object>());
+        HashMap<String, Object> map = new HashMap<String, Object>();
+        map.put("root", rootP);
+        getDefaultTemplate().output(resp.getWriter(), getLanguage(req), map);
 
     }
 
index cd65445a075718cb0853f2ec8dc3740f93ba9ae2..060fdfd4ad509fec8a339242b58fe38dfb696e4c 100644 (file)
@@ -1,2 +1,8 @@
 <?=_The Root certificates are available for download here. Choose your preferred format:?><br/>
-<a href="?pem">PEM</a> <a href="?cer">CER</a>
+<a href="?pem">PEM</a> <a href="?cer">DER</a>
+<p>
+<?=_A full list of DER-encoded certificates is also provided below.?>
+</p>
+<div>
+<?=$root?>
+</div>
index 986442b13c3f854c9a17099fbdbd832d276a3aa8..bac62175e7bfee9bf0ddcb9c53dd895c25e929f7 100644 (file)
@@ -68,6 +68,12 @@ public class TestCertificateAdd extends ClientTest {
 
     KeyPair kp = generateKeypair();
 
+    /**
+     * This KeyPair is used for testing the KeyCheck for proper rejection of
+     * invalid keys. The generated keys suffers from small factors.
+     */
+    KeyPair kpBroken = generateBrokenKeypair();
+
     String csrf;
 
     public TestCertificateAdd() throws GeneralSecurityException, IOException {
@@ -382,4 +388,17 @@ public class TestCertificateAdd extends ClientTest {
         X509Certificate parsedLoginEnabled = createCertWithValidity("&validFrom=now&validity=1m", true);
         assertEquals(u, CertificateOwner.getByEnabledSerial(parsedLoginEnabled.getSerialNumber().toString(16).toLowerCase()));
     }
+
+    @Test
+    public void testInvalidKeyInCSR() throws IOException, GeneralSecurityException {
+        PKCS10Attributes atts = buildAtts(new ObjectIdentifier[] {
+                CertificateRequest.OID_KEY_USAGE_SSL_SERVER
+        }, new DNSName(uniq + ".tld"));
+
+        String pem = generatePEMCSR(kpBroken, "CN=a." + uniq + ".tld", atts);
+
+        HttpURLConnection huc = post(CertificateAdd.PATH, "CSR=" + URLEncoder.encode(pem, "UTF-8"));
+        assertThat(IOUtils.readURL(huc), hasError());
+    }
+
 }
index ead6ffd2c02a0fa1f563234d1fe42408ccb3842e..16abb06cf2623c36de562da1dbbba8b354e51419 100644 (file)
@@ -8,10 +8,17 @@ import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
+import java.math.BigInteger;
 import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
 import java.security.Signature;
+import java.security.spec.RSAPrivateKeySpec;
+import java.security.spec.RSAPublicKeySpec;
 import java.sql.SQLException;
 import java.text.SimpleDateFormat;
 import java.util.Calendar;
@@ -177,6 +184,96 @@ public abstract class ConfiguredTest {
         return keyPair;
     }
 
+    public static KeyPair generateBrokenKeypair() throws GeneralSecurityException {
+        KeyPair keyPair = null;
+        File f = new File("testBrokenKeypair");
+        if (f.exists()) {
+            try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(f))) {
+                keyPair = (KeyPair) ois.readObject();
+            } catch (ClassNotFoundException e) {
+                e.printStackTrace();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        } else {
+            // -----BEGIN SHAMELESSLY ADAPTED BLOCK-----
+            /**
+             * Modified original RSA key generator to use three primes with one
+             * prime set to fixed value to allow simple checking for such faulty
+             * keys.
+             *
+             * @link sun.security.rsa.RSAKeyPairGenerator#generateKeyPair
+             */
+
+            KeyFactory factory = KeyFactory.getInstance("RSA");
+            Random random = new SecureRandom();
+            int keySize = 4096;
+            long r_lv = 7331;
+
+            int lp = (keySize + 1) >> 1;
+            int lr = BigInteger.valueOf(r_lv).bitLength();
+            int lq = keySize - lp - lr;
+
+            BigInteger e = BigInteger.valueOf(7331);
+
+            keyPair = null;
+            while (keyPair == null) {
+                // generate two random primes of size lp/lq
+                BigInteger p, q, r, n;
+
+                p = BigInteger.probablePrime(lp, random);
+                r = BigInteger.valueOf(r_lv);
+                do {
+                    q = BigInteger.probablePrime(lq, random);
+
+                    // convention is for p > q > r
+                    if (p.compareTo(q) < 0) {
+                        BigInteger tmp = p;
+                        p = q;
+                        q = tmp;
+                    }
+
+                    // modulus n = p * q * r
+                    n = p.multiply(q).multiply(r);
+
+                    // even with correctly sized p, q and r, there is a chance
+                    // that n will be one bit short. re-generate the smaller
+                    // prime if so.
+                } while (n.bitLength() < keySize);
+
+                // phi = (p - 1) * (q - 1) * (r - 1) must be relative prime to e
+                // otherwise RSA just won't work ;-)
+                BigInteger p1 = p.subtract(BigInteger.ONE);
+                BigInteger q1 = q.subtract(BigInteger.ONE);
+                BigInteger r1 = r.subtract(BigInteger.ONE);
+                BigInteger phi = p1.multiply(q1).multiply(r1);
+
+                // generate new p and q until they work. typically
+                if (e.gcd(phi).equals(BigInteger.ONE) == false) {
+                    continue;
+                }
+
+                // private exponent d is the inverse of e mod phi
+                BigInteger d = e.modInverse(phi);
+
+                RSAPublicKeySpec publicSpec = new RSAPublicKeySpec(n, e);
+                RSAPrivateKeySpec privateSpec = new RSAPrivateKeySpec(n, d);
+                PublicKey publicKey = factory.generatePublic(publicSpec);
+                PrivateKey privateKey = factory.generatePrivate(privateSpec);
+                keyPair = new KeyPair(publicKey, privateKey);
+            }
+            // -----END SHAMELESSLY ADAPTED BLOCK-----
+
+            try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(f))) {
+                oos.writeObject(keyPair);
+                oos.close();
+            } catch (IOException ioe) {
+                ioe.printStackTrace();
+            }
+        }
+        return keyPair;
+    }
+
     public static String generatePEMCSR(KeyPair kp, String dn) throws GeneralSecurityException, IOException {
         return generatePEMCSR(kp, dn, new PKCS10Attributes());
     }