]> WPIA git - gigi.git/commitdiff
ADD: (most simple) api for issuing certificates first version.
authorFelix Dörre <felix@dogcraft.de>
Thu, 12 Feb 2015 02:34:01 +0000 (03:34 +0100)
committerFelix Dörre <felix@dogcraft.de>
Thu, 12 Feb 2015 13:37:17 +0000 (14:37 +0100)
src/org/cacert/gigi/Launcher.java
src/org/cacert/gigi/api/GigiAPI.java
src/org/cacert/gigi/pages/LoginPage.java
src/org/cacert/gigi/pages/account/certs/CertificateIssueForm.java
src/org/cacert/gigi/pages/account/certs/CertificateRequest.java
tests/org/cacert/gigi/api/IssueCert.java [new file with mode: 0644]
tests/org/cacert/gigi/pages/account/TestCertificateAdd.java
tests/org/cacert/gigi/testUtils/ManagedTest.java

index a7ba8a17bde3511a10b58ca5e0862315267d60ed..a3000c8778fe340ca494a3668ac1a47b087a0fa2 100644 (file)
@@ -129,6 +129,7 @@ public class Launcher {
         secureContextFactory.setNeedClientAuth(false);
         final SslContextFactory staticContextFactory = generateSSLContextFactory(conf, "static");
         final SslContextFactory apiContextFactory = generateSSLContextFactory(conf, "api");
+        apiContextFactory.setWantClientAuth(true);
         try {
             secureContextFactory.start();
             staticContextFactory.start();
index 209ffe563932be8e87971ad2d0b7a33eeddd5971..ae8aee350efa70dc7837aabaa9685aeaeab755b1 100644 (file)
@@ -2,6 +2,8 @@ package org.cacert.gigi.api;
 
 import java.io.IOException;
 import java.io.InputStreamReader;
+import java.security.GeneralSecurityException;
+import java.security.cert.X509Certificate;
 
 import javax.servlet.ServletException;
 import javax.servlet.ServletInputStream;
@@ -9,6 +11,15 @@ import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.cacert.gigi.GigiApiException;
+import org.cacert.gigi.dbObjects.Certificate;
+import org.cacert.gigi.dbObjects.Certificate.CertificateStatus;
+import org.cacert.gigi.dbObjects.User;
+import org.cacert.gigi.pages.LoginPage;
+import org.cacert.gigi.pages.account.certs.CertificateRequest;
+import org.cacert.gigi.util.Job;
+import org.cacert.gigi.util.PEM;
+
 public class GigiAPI extends HttpServlet {
 
     @Override
@@ -27,6 +38,48 @@ public class GigiAPI extends HttpServlet {
                 strB.append(buffer, 0, len);
             }
             System.out.println(strB);
+            return;
+        }
+        X509Certificate cert = LoginPage.getCertificateFromRequest(req);
+        if (cert == null) {
+            resp.sendError(403, "Error, cert authing required.");
+            return;
+        }
+        String serial = LoginPage.extractSerialFormCert(cert);
+        User u = LoginPage.fetchUserBySerial(serial);
+
+        if (pi.equals("/account/certs/new")) {
+
+            if ( !req.getMethod().equals("POST")) {
+                resp.sendError(500, "Error, POST required.");
+                return;
+            }
+            if (req.getQueryString() != null) {
+                resp.sendError(500, "Error, no query String allowed.");
+                return;
+            }
+            String csr = req.getParameter("csr");
+            if (csr == null) {
+                resp.sendError(500, "Error, no CSR found");
+                return;
+            }
+            try {
+                CertificateRequest cr = new CertificateRequest(u, csr);
+                Certificate result = cr.draft();
+                Job job = result.issue(null, "2y");
+                job.waitFor(60000);
+                if (result.getStatus() != CertificateStatus.ISSUED) {
+                    resp.sendError(510, "Error, issuing timed out");
+                    return;
+                }
+                resp.getWriter().println(PEM.encode("CERTIFICATE", result.cert().getEncoded()));
+            } catch (GeneralSecurityException e) {
+                e.printStackTrace();
+            } catch (GigiApiException e) {
+                e.printStackTrace();
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
         }
     }
 }
index ed01ceb6ccc90e9817cbe1fc64546f136ecaa08e..91b6b1b7139383ce20c1d44d61e68b8e19a65406 100644 (file)
@@ -58,9 +58,9 @@ public class LoginPage extends Page {
     public boolean beforeTemplate(HttpServletRequest req, HttpServletResponse resp) throws IOException {
         String redir = (String) req.getSession().getAttribute(LOGIN_RETURNPATH);
         if (req.getSession().getAttribute("loggedin") == null) {
-            X509Certificate[] cert = (X509Certificate[]) req.getAttribute("javax.servlet.request.X509Certificate");
-            if (cert != null && cert[0] != null) {
-                tryAuthWithCertificate(req, cert[0]);
+            X509Certificate cert = getCertificateFromRequest(req);
+            if (cert != null) {
+                tryAuthWithCertificate(req, cert);
             }
             if (req.getMethod().equals("POST")) {
                 try {
@@ -118,17 +118,40 @@ public class LoginPage extends Page {
     }
 
     private void tryAuthWithCertificate(HttpServletRequest req, X509Certificate x509Certificate) {
-        String serial = x509Certificate.getSerialNumber().toString(16).toUpperCase();
+        String serial = extractSerialFormCert(x509Certificate);
+        User user = fetchUserBySerial(serial);
+        if (user == null) {
+            return;
+        }
+        loginSession(req, user);
+        req.getSession().setAttribute(CERT_SERIAL, serial);
+        req.getSession().setAttribute(CERT_ISSUER, x509Certificate.getIssuerDN());
+        req.getSession().setAttribute(LOGIN_METHOD, "Certificate");
+    }
+
+    public static String extractSerialFormCert(X509Certificate x509Certificate) {
+        return x509Certificate.getSerialNumber().toString(16).toUpperCase();
+    }
+
+    public static User fetchUserBySerial(String serial) {
         GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("SELECT `memid` FROM `certs` WHERE `serial`=? AND `disablelogin`='0' AND `revoked` is NULL");
         ps.setString(1, serial);
         GigiResultSet rs = ps.executeQuery();
+        User user = null;
         if (rs.next()) {
-            loginSession(req, User.getById(rs.getInt(1)));
-            req.getSession().setAttribute(CERT_SERIAL, serial);
-            req.getSession().setAttribute(CERT_ISSUER, x509Certificate.getIssuerDN());
-            req.getSession().setAttribute(LOGIN_METHOD, "Certificate");
+            user = User.getById(rs.getInt(1));
         }
         rs.close();
+        return user;
+    }
+
+    public static X509Certificate getCertificateFromRequest(HttpServletRequest req) {
+        X509Certificate[] cert = (X509Certificate[]) req.getAttribute("javax.servlet.request.X509Certificate");
+        X509Certificate uc = null;
+        if (cert != null && cert[0] != null) {
+            uc = cert[0];
+        }
+        return uc;
     }
 
     private static final Group LOGIN_BLOCKED = Group.getByString("blockedlogin");
index 6f099195ee1b780c1771ca02a4a283325350daee..8a3107f0ed5a03bd8ec582d98d9807fc1b6ec29c 100644 (file)
@@ -83,16 +83,18 @@ public class CertificateIssueForm extends Form {
                     if (req.getParameter("CCA") == null) {
                         error.mergeInto(new GigiApiException("You need to accept the CCA."));
                     }
+                    Certificate result = null;
                     try {
                         result = cr.draft();
                     } catch (GigiApiException e) {
                         error.mergeInto(e);
                     }
-                    if ( !error.isEmpty()) {
+                    if ( !error.isEmpty() || result == null) {
                         error.format(out, Page.getLanguage(req));
                         return false;
                     }
                     result.issue(issueDate.getFrom(), issueDate.getTo()).waitFor(60000);
+                    this.result = result;
                     return true;
                 } else {
                     throw new GigiApiException("Error no action.");
index fe46244f33c1876cb08d45746ce1a3c7871e433b..63c129e8e3c286e857acdc230767fc990f4612a6 100644 (file)
@@ -10,6 +10,7 @@ import java.security.interfaces.RSAPublicKey;
 import java.util.Arrays;
 import java.util.Base64;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.Set;
 import java.util.TreeSet;
@@ -173,11 +174,7 @@ public class CertificateRequest {
             }
 
         }
-        GigiApiException error = new GigiApiException();
-        this.SANs = verifySANs(error, false, SANs);
-        if ( !error.isEmpty()) {
-            throw error;
-        }
+        this.SANs = SANs;
         pk = parsed.getSubjectPublicKeyInfo();
         String sign = getSignatureAlgorithm(data);
         guessDigest(sign);
@@ -197,7 +194,7 @@ public class CertificateRequest {
         pk = parsed.getPubkey();
         String sign = getSignatureAlgorithm(data);
         guessDigest(sign);
-
+        this.SANs = new HashSet<>();
         this.csr = "SPKAC=" + cleanedSPKAC;
         this.csrType = CSRType.SPKAC;
 
@@ -314,9 +311,9 @@ public class CertificateRequest {
         return true;
     }
 
-    private Set<SubjectAlternateName> verifySANs(GigiApiException error, boolean server, TreeSet<SubjectAlternateName> parseSANBox) {
+    private Set<SubjectAlternateName> verifySANs(GigiApiException error, boolean server, Set<SubjectAlternateName> sANs2) {
         Set<SubjectAlternateName> filteredSANs = new LinkedHashSet<>();
-        for (SubjectAlternateName san : parseSANBox) {
+        for (SubjectAlternateName san : sANs2) {
             if (san.getType() == SANType.DNS) {
                 if (u.isValidDomain(san.getName()) && server) {
                     if (pDNS == null) {
@@ -374,6 +371,7 @@ public class CertificateRequest {
                 subject.put("EMAIL", pMail);
             }
         }
+        this.SANs = verifySANs(error, server, SANs);
         if (org != null) {
             subject.put("O", org.getName());
             subject.put("C", org.getState());
diff --git a/tests/org/cacert/gigi/api/IssueCert.java b/tests/org/cacert/gigi/api/IssueCert.java
new file mode 100644 (file)
index 0000000..6a6f320
--- /dev/null
@@ -0,0 +1,48 @@
+package org.cacert.gigi.api;
+
+import static org.junit.Assert.*;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+
+import org.cacert.gigi.dbObjects.Certificate;
+import org.cacert.gigi.dbObjects.Certificate.CSRType;
+import org.cacert.gigi.dbObjects.CertificateProfile;
+import org.cacert.gigi.testUtils.ClientTest;
+import org.cacert.gigi.testUtils.IOUtils;
+import org.junit.Test;
+
+import sun.security.x509.X500Name;
+
+public class IssueCert extends ClientTest {
+
+    @Test
+    public void testIssueCert() throws Exception {
+        KeyPair kp = generateKeypair();
+        String key1 = generatePEMCSR(kp, "CN=testmail@example.com");
+        Certificate c = new Certificate(u, Certificate.buildDN("CN", "testmail@example.com"), "sha256", key1, CSRType.CSR, CertificateProfile.getById(1));
+        final PrivateKey pk = kp.getPrivate();
+        c.issue(null, "2y").waitFor(60000);
+        final X509Certificate ce = c.cert();
+        HttpURLConnection connection = (HttpURLConnection) new URL("https://" + getServerName().replaceFirst("^www.", "api.") + "/account/certs/new").openConnection();
+        authenticateClientCert(pk, ce, connection);
+        connection.setDoOutput(true);
+        OutputStream os = connection.getOutputStream();
+        os.write(("csr=" + URLEncoder.encode(generatePEMCSR(kp, "CN=a b"), "UTF-8")).getBytes());
+        os.flush();
+        assertEquals(connection.getResponseCode(), 200);
+        String cert = IOUtils.readURL(new InputStreamReader(connection.getInputStream(), "UTF-8"));
+        CertificateFactory cf = CertificateFactory.getInstance("X509");
+        Collection<? extends java.security.cert.Certificate> certs = cf.generateCertificates(new ByteArrayInputStream(cert.getBytes()));
+        assertEquals("a b", ((X500Name) ((X509Certificate) certs.iterator().next()).getSubjectDN()).getCommonName());
+    }
+}
index 6009095a9de0be8b9b8f59fd145c01808ab8358f..b22141eadc585d0f90261ecdf8a74cdf7c4ffa72 100644 (file)
@@ -87,7 +87,7 @@ public class TestCertificateAdd extends ClientTest {
 
         String[] res = fillOutForm("CSR=" + URLEncoder.encode(pem, "UTF-8"));
         assertArrayEquals(new String[] {
-                "mail", "a b", "dns:a." + uniq + ".tld\ndns:b." + uniq + ".tld\nemail:" + email + "\n", Digest.SHA384.toString()
+                "mail", "a b", "email:" + email + "\ndns:a." + uniq + ".tld\ndns:b." + uniq + ".tld\n", Digest.SHA384.toString()
         }, res);
     }
 
index 54cb21fb0fc4cab1709e44f88c34c6f95bbea7d7..312c351e294aee8f6668109054b7c6d0e674f42a 100644 (file)
@@ -178,6 +178,7 @@ public class ManagedTest extends ConfiguredTest {
         mainProps.setProperty("name.secure", testProps.getProperty("name.secure"));
         mainProps.setProperty("name.www", testProps.getProperty("name.www"));
         mainProps.setProperty("name.static", testProps.getProperty("name.static"));
+        mainProps.setProperty("name.api", testProps.getProperty("name.api"));
 
         mainProps.setProperty("https.port", testProps.getProperty("serverPort.https"));
         mainProps.setProperty("http.port", testProps.getProperty("serverPort.http"));