]> WPIA git - gigi.git/commitdiff
Implement fixed-time signing.
authorFelix Dörre <felix@dogcraft.de>
Sat, 23 Aug 2014 11:20:40 +0000 (13:20 +0200)
committerFelix Dörre <felix@dogcraft.de>
Sat, 23 Aug 2014 13:07:05 +0000 (15:07 +0200)
doc/tableStructure.sql
src/org/cacert/gigi/Certificate.java
src/org/cacert/gigi/Launcher.java
src/org/cacert/gigi/output/CertificateValiditySelector.java
src/org/cacert/gigi/pages/account/CertificateIssueForm.java
src/org/cacert/gigi/util/Job.java
tests/org/cacert/gigi/TestCertificate.java
tests/org/cacert/gigi/TestSeparateSessionScope.java
util/org/cacert/gigi/util/SimpleSigner.java

index 69f85347fb6d914a8a97decce98beb3dd9e78b1a..3fe84bb065a28c86ec61c1861134699807552ce5 100644 (file)
@@ -173,6 +173,8 @@ CREATE TABLE `jobs` (
   `task` enum('sign','revoke') NOT NULL,
   `state` enum('open', 'done', 'error') NOT NULL DEFAULT 'open',
   `warning` int(2) NOT NULL DEFAULT '0',
+  `executeFrom` DATE,
+  `executeTo` VARCHAR(11),
   PRIMARY KEY (`id`),
   KEY `state` (`state`)
 ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1;
index 433e60e49b62a5bc6a97444d3ff7f1c104622c59..8094419ba3473f2c95f43bc0f3ac461519ca2e52 100644 (file)
@@ -8,6 +8,7 @@ import java.io.InputStream;
 import java.security.GeneralSecurityException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
+import java.sql.Date;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
@@ -18,7 +19,6 @@ import java.util.List;
 
 import org.cacert.gigi.database.DatabaseConnection;
 import org.cacert.gigi.util.Job;
-import org.cacert.gigi.util.Job.JobType;
 import org.cacert.gigi.util.KeyStorage;
 import org.cacert.gigi.util.Notary;
 
@@ -219,7 +219,23 @@ public class Certificate {
         return CertificateStatus.REVOKED;
     }
 
-    public Job issue() throws IOException, SQLException {
+    /**
+     * @param start
+     *            the date from which on the certificate should be valid. (or
+     *            null if it should be valid instantly)
+     * @param period
+     *            the period for which the date should be valid. (a
+     *            <code>yyyy-mm-dd</code> or a "2y" (2 calendar years), "6m" (6
+     *            months)
+     * @return A job which can be used to monitor the progress of this task.
+     * @throws IOException
+     *             for problems with writing the CSR/SPKAC
+     * @throws SQLException
+     *             for problems with writing to the DB
+     * @throws GigiApiException
+     *             if the period is bogus
+     */
+    public Job issue(Date start, String period) throws IOException, SQLException, GigiApiException {
         if (getStatus() != CertificateStatus.DRAFT) {
             throw new IllegalStateException();
         }
@@ -252,7 +268,7 @@ public class Certificate {
         updater.setString(1, csrName);
         updater.setInt(2, id);
         updater.execute();
-        return Job.submit(this, JobType.SIGN);
+        return Job.sign(this, start, period);
 
     }
 
@@ -260,7 +276,7 @@ public class Certificate {
         if (getStatus() != CertificateStatus.ISSUED) {
             throw new IllegalStateException();
         }
-        return Job.submit(this, JobType.REVOKE);
+        return Job.revoke(this);
 
     }
 
index f056be96f8a61ead04617cf8eee78afacd701e7b..10488f0544ed3120430e92cf63690ba336c1d480 100644 (file)
@@ -10,6 +10,7 @@ import java.security.UnrecoverableKeyException;
 import java.security.cert.Certificate;
 import java.util.List;
 import java.util.Properties;
+import java.util.TimeZone;
 
 import javax.net.ssl.ExtendedSSLSession;
 import javax.net.ssl.SNIHostName;
@@ -46,6 +47,7 @@ import org.eclipse.jetty.util.ssl.SslContextFactory;
 public class Launcher {
 
     public static void main(String[] args) throws Exception {
+        TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
         GigiConfig conf = GigiConfig.parse(System.in);
         ServerConstants.init(conf.getMainProps());
         initEmails(conf);
index 95eaa09e53b6d970d68f5bfa0c71ae74383b6106..a58ca87302424a1769f896abb22eff2fafaea530 100644 (file)
@@ -3,31 +3,40 @@ package org.cacert.gigi.output;
 import java.io.PrintWriter;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.GregorianCalendar;
+import java.sql.Date;
 import java.util.Map;
 import java.util.TimeZone;
 
 import javax.servlet.http.HttpServletRequest;
 
+import org.cacert.gigi.GigiApiException;
 import org.cacert.gigi.localisation.Language;
+import org.cacert.gigi.util.HTMLEncoder;
 
 public class CertificateValiditySelector implements Outputable {
 
-    SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
+    private static ThreadLocal<SimpleDateFormat> fmt = new ThreadLocal<>();
 
     private static final int DAY = 1000 * 60 * 60 * 24;
 
-    Date from;
+    private Date from;
 
-    String val;
+    private String val = "2y";
 
     public CertificateValiditySelector() {
-        fmt.setTimeZone(TimeZone.getTimeZone("UTC"));
 
     }
 
+    public static SimpleDateFormat getDateFormat() {
+        SimpleDateFormat local = fmt.get();
+        if (local == null) {
+            local = new SimpleDateFormat("yyyy-MM-dd");
+            local.setTimeZone(TimeZone.getTimeZone("UTC"));
+            fmt.set(local);
+        }
+        return local;
+    }
+
     @Override
     public void output(PrintWriter out, Language l, Map<String, Object> vars) {
         out.print("<select name='validFrom'><option value='now'");
@@ -40,7 +49,7 @@ public class CertificateValiditySelector implements Outputable {
         long base = getCurrentDayBase();
         for (int i = 0; i < 14; i++) {
             long date = base + DAY * i;
-            String d = fmt.format(new Date(date));
+            String d = getDateFormat().format(new Date(date));
             out.print("<option value='");
             out.print(d);
             out.print("'");
@@ -53,41 +62,14 @@ public class CertificateValiditySelector implements Outputable {
         }
         out.println("</select>");
 
-        out.print("<select name='validity'>");
-        out.print("<option value='6m'");
-        if ("0.5m".equals(val)) {
-            out.print(" selected='selected'");
-        }
-        out.println(">6 months</option>");
-
-        out.print("<option value='1y'");
-        if ("1y".equals(val)) {
-            out.print(" selected='selected'");
-        }
-        out.println(">1 year</option>");
-
-        out.print("<option value='2y'");
-        if ("2y".equals(val)) {
-            out.print(" selected='selected'");
-        }
-        out.println(">2 years</option>");
-        out.println("</select>");
+        out.print("<input type='text' name='validity' value='");
+        out.print(HTMLEncoder.encodeHTML(val));
+        out.println("'>");
 
         if (from == null) {
             return;
         }
-        // debug dummy output
-        Calendar c = GregorianCalendar.getInstance();
-        c.setTime(from);
-        if ("6m".equals(val)) {
-            c.add(Calendar.MONTH, 6);
-        } else if ("1y".equals(val)) {
-            c.add(Calendar.YEAR, 1);
-        } else if ("2y".equals(val)) {
-            c.add(Calendar.YEAR, 2);
-        }
-        out.println("From: " + fmt.format(from));
-        out.println("To: " + fmt.format(c.getTime()));
+
     }
 
     private long getCurrentDayBase() {
@@ -97,19 +79,71 @@ public class CertificateValiditySelector implements Outputable {
         return base;
     }
 
-    public void update(HttpServletRequest r) {
+    public void update(HttpServletRequest r) throws GigiApiException {
         String from = r.getParameter("validFrom");
+
+        GigiApiException gae = new GigiApiException();
+        try {
+            saveStartDate(from);
+        } catch (GigiApiException e) {
+            gae.mergeInto(e);
+        }
+        try {
+            String validity = r.getParameter("validity");
+            if (validity != null) {
+                checkValidityLength(validity);
+                val = validity;
+            }
+        } catch (GigiApiException e) {
+            gae.mergeInto(e);
+        }
+        if ( !gae.isEmpty()) {
+            throw gae;
+        }
+
+    }
+
+    public static void checkValidityLength(String newval) throws GigiApiException {
+        if (newval.endsWith("y") || newval.endsWith("m")) {
+            if (newval.length() > 10) { // for database
+                throw new GigiApiException("The validity interval entered is invalid.");
+            }
+            String num = newval.substring(0, newval.length() - 1);
+            try {
+                int len = Integer.parseInt(num);
+                if (len <= 0) {
+                    throw new GigiApiException("The validity interval entered is invalid.");
+                }
+            } catch (NumberFormatException e) {
+                throw new GigiApiException("The validity interval entered is invalid.");
+            }
+        } else {
+            try {
+                getDateFormat().parse(newval);
+            } catch (ParseException e) {
+                throw new GigiApiException("The validity interval entered is invalid.");
+            }
+        }
+    }
+
+    private void saveStartDate(String from) throws GigiApiException {
         if (from == null || "now".equals(from)) {
             this.from = null;
         } else {
             try {
-                this.from = fmt.parse(from);
+                this.from = new Date(getDateFormat().parse(from).getTime());
             } catch (ParseException e) {
-                e.printStackTrace();
+                throw new GigiApiException("The validity start date entered is invalid.");
             }
         }
-        val = r.getParameter("validity");
+    }
+
+    public Date getFrom() {
+        return from;
+    }
 
+    public String getTo() {
+        return val;
     }
 
 }
index 4c56dbdf1737e11dac45e50ef7fcea786783f265..086d51a3133c76cdf9f38d75e6e5766df42ebed5 100644 (file)
@@ -300,7 +300,7 @@ public class CertificateIssueForm extends Form {
 
                     result = new Certificate(LoginPage.getUser(req).getId(), subject.toString(), selectedDigest.toString(), //
                             this.csr, this.csrType, profile, SANs.toArray(new SubjectAlternateName[SANs.size()]));
-                    result.issue().waitFor(60000);
+                    result.issue(issueDate.getFrom(), issueDate.getTo()).waitFor(60000);
                     return true;
                 }
             } catch (IOException e) {
index 13e6c7e0c1c5c768f0888ee7c3c977b4a016a8e4..959c14f5ef09185d9c7288169c370160b74f71d6 100644 (file)
@@ -1,11 +1,14 @@
 package org.cacert.gigi.util;
 
+import java.sql.Date;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 
 import org.cacert.gigi.Certificate;
+import org.cacert.gigi.GigiApiException;
 import org.cacert.gigi.database.DatabaseConnection;
+import org.cacert.gigi.output.CertificateValiditySelector;
 
 public class Job {
 
@@ -29,10 +32,22 @@ public class Job {
         }
     }
 
-    public static Job submit(Certificate targetId, JobType type) throws SQLException {
+    public static Job sign(Certificate targetId, Date start, String period) throws SQLException, GigiApiException {
+        CertificateValiditySelector.checkValidityLength(period);
+        PreparedStatement ps = DatabaseConnection.getInstance().prepare("INSERT INTO `jobs` SET targetId=?, task=?, executeFrom=?, executeTo=?");
+        ps.setInt(1, targetId.getId());
+        ps.setString(2, JobType.SIGN.getName());
+        ps.setDate(3, start);
+        ps.setString(4, period);
+        ps.execute();
+        return new Job(DatabaseConnection.lastInsertId(ps));
+    }
+
+    public static Job revoke(Certificate targetId) throws SQLException {
+
         PreparedStatement ps = DatabaseConnection.getInstance().prepare("INSERT INTO `jobs` SET targetId=?, task=?");
         ps.setInt(1, targetId.getId());
-        ps.setString(2, type.getName());
+        ps.setString(2, JobType.REVOKE.getName());
         ps.execute();
         return new Job(DatabaseConnection.lastInsertId(ps));
     }
index d14d0487f435616b67a180974f6a1c2e43c9fc1a..a1972304e95cc55f7b4157481498b4b10febeeea 100644 (file)
@@ -22,25 +22,25 @@ import static org.junit.Assert.*;
 public class TestCertificate extends ManagedTest {
 
     @Test
-    public void testClientCertLoginStates() throws IOException, GeneralSecurityException, SQLException, InterruptedException {
+    public void testClientCertLoginStates() throws IOException, GeneralSecurityException, SQLException, InterruptedException, GigiApiException {
         KeyPair kp = generateKeypair();
         String key1 = generatePEMCSR(kp, "CN=testmail@example.com");
         Certificate c = new Certificate(1, "/CN=testmail@example.com", "sha256", key1, CSRType.CSR, CertificateProfile.getById(1));
         final PrivateKey pk = kp.getPrivate();
-        c.issue().waitFor(60000);
+        c.issue(null, "2y").waitFor(60000);
         final X509Certificate ce = c.cert();
         assertNotNull(login(pk, ce));
     }
 
     @Test
-    public void testSANs() throws IOException, GeneralSecurityException, SQLException, InterruptedException {
+    public void testSANs() throws IOException, GeneralSecurityException, SQLException, InterruptedException, GigiApiException {
         KeyPair kp = generateKeypair();
         String key = generatePEMCSR(kp, "CN=testmail@example.com");
         Certificate c = new Certificate(1, "/CN=testmail@example.com", "sha256", key, CSRType.CSR, CertificateProfile.getById(1),//
                 new SubjectAlternateName(SANType.EMAIL, "testmail@example.com"), new SubjectAlternateName(SANType.DNS, "testmail.example.com"));
 
         testFails(CertificateStatus.DRAFT, c);
-        c.issue().waitFor(60000);
+        c.issue(null, "2y").waitFor(60000);
         X509Certificate cert = c.cert();
         Collection<List<?>> sans = cert.getSubjectAlternativeNames();
         assertEquals(2, sans.size());
@@ -84,14 +84,14 @@ public class TestCertificate extends ManagedTest {
     }
 
     @Test
-    public void testCertLifeCycle() throws IOException, GeneralSecurityException, SQLException, InterruptedException {
+    public void testCertLifeCycle() throws IOException, GeneralSecurityException, SQLException, InterruptedException, GigiApiException {
         KeyPair kp = generateKeypair();
         String key = generatePEMCSR(kp, "CN=testmail@example.com");
         Certificate c = new Certificate(1, "/CN=testmail@example.com", "sha256", key, CSRType.CSR, CertificateProfile.getById(1));
         final PrivateKey pk = kp.getPrivate();
 
         testFails(CertificateStatus.DRAFT, c);
-        c.issue().waitFor(60000);
+        c.issue(null, "2y").waitFor(60000);
 
         testFails(CertificateStatus.ISSUED, c);
         X509Certificate cert = c.cert();
@@ -103,7 +103,7 @@ public class TestCertificate extends ManagedTest {
 
     }
 
-    private void testFails(CertificateStatus status, Certificate c) throws IOException, GeneralSecurityException, SQLException {
+    private void testFails(CertificateStatus status, Certificate c) throws IOException, GeneralSecurityException, SQLException, GigiApiException {
         assertEquals(status, c.getStatus());
         if (status != CertificateStatus.ISSUED) {
             try {
@@ -115,7 +115,7 @@ public class TestCertificate extends ManagedTest {
         }
         if (status != CertificateStatus.DRAFT) {
             try {
-                c.issue();
+                c.issue(null, "2y");
                 fail(status + " is in invalid state");
             } catch (IllegalStateException ise) {
 
index 840249f447a2a518950239e0460a1d488b424e62..961b3e32c5d80dbc6657c27a875d8a0cd29403e2 100644 (file)
@@ -18,7 +18,7 @@ import org.junit.Test;
 public class TestSeparateSessionScope extends ManagedTest {
 
     @Test
-    public void testSeparateScope() throws IOException, GeneralSecurityException, SQLException, InterruptedException {
+    public void testSeparateScope() throws IOException, GeneralSecurityException, SQLException, InterruptedException, GigiApiException {
         String mail = "thisgo" + createUniqueName() + "@example.com";
         int user = createAssuranceUser("test", "tugo", mail, TEST_PASSWORD);
         String cookie = login(mail, TEST_PASSWORD);
@@ -26,7 +26,7 @@ public class TestSeparateSessionScope extends ManagedTest {
         String csr = generatePEMCSR(kp, "CN=felix@dogcraft.de");
         Certificate c = new Certificate(user, "/CN=testmail@example.com", "sha256", csr, CSRType.CSR, CertificateProfile.getById(1));
         final PrivateKey pk = kp.getPrivate();
-        c.issue().waitFor(60000);
+        c.issue(null, "2y").waitFor(60000);
         final X509Certificate ce = c.cert();
         String scookie = login(pk, ce);
 
index f0c559ac8bcb9224303853017380e68aaed45e4c..2f0f6f6bca6479087039766ca1d1edebd1e6dddd 100644 (file)
@@ -12,14 +12,19 @@ import java.math.BigInteger;
 import java.security.GeneralSecurityException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
+import java.util.Date;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
-import java.util.Arrays;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
 import java.util.Properties;
+import java.util.TimeZone;
 
 import org.cacert.gigi.Certificate.CSRType;
 import org.cacert.gigi.database.DatabaseConnection;
+import org.cacert.gigi.output.CertificateValiditySelector;
 
 public class SimpleSigner {
 
@@ -41,6 +46,11 @@ public class SimpleSigner {
 
     private static Thread runner;
 
+    private static SimpleDateFormat sdf = new SimpleDateFormat("YYMMddHHmmss'Z'");
+    static {
+        sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
+    }
+
     public static void main(String[] args) throws IOException, SQLException, InterruptedException {
         Properties p = new Properties();
         p.load(new FileReader("config/gigi.properties"));
@@ -64,7 +74,7 @@ public class SimpleSigner {
             throw new IllegalStateException("already running");
         }
         running = true;
-        readyCerts = DatabaseConnection.getInstance().prepare("SELECT certs.id AS id, certs.csr_name, certs.subject, jobs.id AS jobid, csr_type, md, keyUsage, extendedKeyUsage FROM jobs " + //
+        readyCerts = DatabaseConnection.getInstance().prepare("SELECT certs.id AS id, certs.csr_name, certs.subject, jobs.id AS jobid, csr_type, md, keyUsage, extendedKeyUsage, executeFrom, executeTo FROM jobs " + //
                 "INNER JOIN certs ON certs.id=jobs.targetId " + //
                 "INNER JOIN profiles ON profiles.id=certs.profile " + //
                 "WHERE jobs.state='open' "//
@@ -174,103 +184,138 @@ public class SimpleSigner {
 
     private static int counter = 0;
 
-    private static void signCertificates() throws SQLException, IOException, InterruptedException {
+    private static void signCertificates() throws SQLException {
         ResultSet rs = readyCerts.executeQuery();
         while (rs.next()) {
             String csrname = rs.getString("csr_name");
-            System.out.println("sign: " + csrname);
             int id = rs.getInt("id");
-            String csrType = rs.getString("csr_type");
-            CSRType ct = CSRType.valueOf(csrType);
-            File crt = KeyStorage.locateCrt(id);
-
-            String keyUsage = rs.getString("keyUsage");
-            String ekeyUsage = rs.getString("extendedKeyUsage");
-            getSANSs.setInt(1, id);
-            ResultSet san = getSANSs.executeQuery();
-
-            File f = new File("keys", "SANFile" + System.currentTimeMillis() + (counter++) + ".cfg");
-            PrintWriter cfg = new PrintWriter(f);
-            boolean first = true;
-            while (san.next()) {
-                if ( !first) {
-                    cfg.print(", ");
+            System.out.println("sign: " + csrname);
+            try {
+                String csrType = rs.getString("csr_type");
+                CSRType ct = CSRType.valueOf(csrType);
+                File crt = KeyStorage.locateCrt(id);
+
+                String keyUsage = rs.getString("keyUsage");
+                String ekeyUsage = rs.getString("extendedKeyUsage");
+                java.sql.Date from = rs.getDate("executeFrom");
+                String length = rs.getString("executeTo");
+                Date fromDate;
+                Date toDate;
+                if (from == null) {
+                    fromDate = new Date(System.currentTimeMillis());
                 } else {
-                    cfg.print("subjectAltName=");
+                    fromDate = new Date(from.getTime());
                 }
-                first = false;
-                cfg.print(san.getString("type"));
-                cfg.print(":");
-                cfg.print(san.getString("contents"));
-            }
-            cfg.println();
-            cfg.println("keyUsage=" + keyUsage);
-            cfg.println("extendedKeyUsage=" + ekeyUsage);
-            cfg.close();
-
-            String[] call = new String[] {
-                    "openssl", "ca",//
-                    "-in",
-                    "../../" + csrname,//
-                    "-cert",
-                    "../unassured.crt",//
-                    "-keyfile",
-                    "../unassured.key",//
-                    "-out",
-                    "../../" + crt.getPath(),//
-                    "-utf8",
-                    "-days",
-                    "356",//
-                    "-batch",//
-                    "-md",
-                    rs.getString("md"),//
-                    "-extfile",
-                    "../" + f.getName(),//
-
-                    "-subj",
-                    rs.getString("subject"),//
-                    "-config",
-                    "../selfsign.config"//
-
-            };
-            if (ct == CSRType.SPKAC) {
-                call[2] = "-spkac";
-            }
-            Process p1 = Runtime.getRuntime().exec(call, null, new File("keys/unassured.ca"));
-
-            int waitFor = p1.waitFor();
-            f.delete();
-            if (waitFor == 0) {
-                try (InputStream is = new FileInputStream(crt)) {
-                    CertificateFactory cf = CertificateFactory.getInstance("X.509");
-                    X509Certificate crtp = (X509Certificate) cf.generateCertificate(is);
-                    BigInteger serial = crtp.getSerialNumber();
-                    updateMail.setString(1, crt.getPath());
-                    updateMail.setString(2, serial.toString(16));
-                    updateMail.setInt(3, id);
-                    updateMail.execute();
-
-                    finishJob.setInt(1, rs.getInt("jobid"));
-                    finishJob.execute();
-                    System.out.println("signed: " + id);
-                    continue;
-                } catch (GeneralSecurityException e) {
-                    e.printStackTrace();
+                if (length.endsWith("m") || length.endsWith("y")) {
+                    String num = length.substring(0, length.length() - 1);
+                    int inter = Integer.parseInt(num);
+                    Calendar c = Calendar.getInstance();
+                    c.setTimeZone(TimeZone.getTimeZone("UTC"));
+                    c.setTime(fromDate);
+                    if (length.endsWith("m")) {
+                        c.add(Calendar.MONTH, inter);
+                    } else {
+                        c.add(Calendar.YEAR, inter);
+                    }
+                    toDate = c.getTime();
+                } else {
+                    toDate = CertificateValiditySelector.getDateFormat().parse(length);
                 }
-                System.out.println("ERROR Afterwards: " + id);
-                warnMail.setInt(1, rs.getInt("jobid"));
-                warnMail.execute();
-            } else {
-                BufferedReader br = new BufferedReader(new InputStreamReader(p1.getErrorStream()));
-                String s;
-                while ((s = br.readLine()) != null) {
-                    System.out.println(s);
+                System.out.println(from);
+                System.out.println(sdf.format(fromDate));
+
+                getSANSs.setInt(1, id);
+                ResultSet san = getSANSs.executeQuery();
+
+                File f = new File("keys", "SANFile" + System.currentTimeMillis() + (counter++) + ".cfg");
+                PrintWriter cfg = new PrintWriter(f);
+                boolean first = true;
+                while (san.next()) {
+                    if ( !first) {
+                        cfg.print(", ");
+                    } else {
+                        cfg.print("subjectAltName=");
+                    }
+                    first = false;
+                    cfg.print(san.getString("type"));
+                    cfg.print(":");
+                    cfg.print(san.getString("contents"));
+                }
+                cfg.println();
+                cfg.println("keyUsage=" + keyUsage);
+                cfg.println("extendedKeyUsage=" + ekeyUsage);
+                cfg.close();
+
+                String[] call = new String[] {
+                        "openssl", "ca",//
+                        "-in",
+                        "../../" + csrname,//
+                        "-cert",
+                        "../unassured.crt",//
+                        "-keyfile",
+                        "../unassured.key",//
+                        "-out",
+                        "../../" + crt.getPath(),//
+                        "-utf8",
+                        "-startdate",
+                        sdf.format(fromDate),//
+                        "-enddate",
+                        sdf.format(toDate),//
+                        "-batch",//
+                        "-md",
+                        rs.getString("md"),//
+                        "-extfile",
+                        "../" + f.getName(),//
+
+                        "-subj",
+                        rs.getString("subject"),//
+                        "-config",
+                        "../selfsign.config"//
+
+                };
+                if (ct == CSRType.SPKAC) {
+                    call[2] = "-spkac";
                 }
-                System.out.println(Arrays.toString(call));
-                System.out.println("ERROR: " + id);
-                warnMail.setInt(1, rs.getInt("jobid"));
-                warnMail.execute();
+                Process p1 = Runtime.getRuntime().exec(call, null, new File("keys/unassured.ca"));
+
+                int waitFor = p1.waitFor();
+                f.delete();
+                if (waitFor == 0) {
+                    try (InputStream is = new FileInputStream(crt)) {
+                        CertificateFactory cf = CertificateFactory.getInstance("X.509");
+                        X509Certificate crtp = (X509Certificate) cf.generateCertificate(is);
+                        BigInteger serial = crtp.getSerialNumber();
+                        updateMail.setString(1, crt.getPath());
+                        updateMail.setString(2, serial.toString(16));
+                        updateMail.setInt(3, id);
+                        updateMail.execute();
+
+                        finishJob.setInt(1, rs.getInt("jobid"));
+                        finishJob.execute();
+                        System.out.println("signed: " + id);
+                        continue;
+                    }
+                } else {
+                    BufferedReader br = new BufferedReader(new InputStreamReader(p1.getErrorStream()));
+                    String s;
+                    while ((s = br.readLine()) != null) {
+                        System.out.println(s);
+                    }
+                }
+            } catch (GeneralSecurityException e) {
+                e.printStackTrace();
+            } catch (IOException e) {
+                e.printStackTrace();
+            } catch (SQLException e) {
+                e.printStackTrace();
+            } catch (ParseException e) {
+                e.printStackTrace();
+            } catch (InterruptedException e1) {
+                e1.printStackTrace();
             }
+            System.out.println("Error with: " + id);
+            warnMail.setInt(1, rs.getInt("jobid"));
+            warnMail.execute();
 
         }
         rs.close();