1 package org.cacert.gigi.util;
3 import java.io.BufferedReader;
5 import java.io.FileInputStream;
6 import java.io.FileOutputStream;
7 import java.io.IOException;
8 import java.io.InputStream;
9 import java.io.InputStreamReader;
10 import java.io.OutputStreamWriter;
11 import java.io.PrintWriter;
12 import java.io.Reader;
13 import java.math.BigInteger;
14 import java.security.GeneralSecurityException;
15 import java.security.cert.CertificateFactory;
16 import java.security.cert.X509Certificate;
17 import java.sql.SQLException;
18 import java.sql.Timestamp;
19 import java.text.ParseException;
20 import java.text.SimpleDateFormat;
21 import java.util.Calendar;
22 import java.util.Date;
23 import java.util.HashMap;
24 import java.util.Properties;
25 import java.util.TimeZone;
27 import org.cacert.gigi.database.DatabaseConnection;
28 import org.cacert.gigi.database.GigiPreparedStatement;
29 import org.cacert.gigi.database.GigiResultSet;
30 import org.cacert.gigi.dbObjects.Certificate;
31 import org.cacert.gigi.dbObjects.Certificate.CSRType;
32 import org.cacert.gigi.output.DateSelector;
34 public class SimpleSigner {
36 private static GigiPreparedStatement warnMail;
38 private static GigiPreparedStatement updateMail;
40 private static GigiPreparedStatement readyCerts;
42 private static GigiPreparedStatement getSANSs;
44 private static GigiPreparedStatement revoke;
46 private static GigiPreparedStatement revokeCompleted;
48 private static GigiPreparedStatement finishJob;
50 private static volatile boolean running = true;
52 private static Thread runner;
54 private static SimpleDateFormat sdf = new SimpleDateFormat("yyMMddHHmmss'Z'");
57 TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
58 sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
61 public static void main(String[] args) throws IOException, SQLException, InterruptedException {
62 Properties p = new Properties();
63 try (Reader reader = new InputStreamReader(new FileInputStream("config/gigi.properties"), "UTF-8")) {
66 DatabaseConnection.init(p);
71 public static void stopSigner() throws InterruptedException {
72 Thread capturedRunner;
73 synchronized (SimpleSigner.class) {
75 throw new IllegalStateException("already stopped");
77 capturedRunner = runner;
79 SimpleSigner.class.notifyAll();
81 capturedRunner.join();
84 public synchronized static void runSigner() throws SQLException, IOException, InterruptedException {
86 throw new IllegalStateException("already running");
89 readyCerts = DatabaseConnection.getInstance().prepare("SELECT certs.id AS id, certs.csr_name, jobs.id AS jobid, csr_type, md, executeFrom, executeTo, profile FROM jobs " + //
90 "INNER JOIN certs ON certs.id=jobs.targetId " + //
91 "INNER JOIN profiles ON profiles.id=certs.profile " + //
92 "WHERE jobs.state='open' "//
95 getSANSs = DatabaseConnection.getInstance().prepare("SELECT contents, type FROM subjectAlternativeNames " + //
98 updateMail = DatabaseConnection.getInstance().prepare("UPDATE certs SET crt_name=?," + " created=NOW(), serial=?, caid=1 WHERE id=?");
99 warnMail = DatabaseConnection.getInstance().prepare("UPDATE jobs SET warning=warning+1, state=IF(warning<3, 'open','error') WHERE id=?");
101 revoke = DatabaseConnection.getInstance().prepare("SELECT certs.id, certs.csr_name,jobs.id FROM jobs INNER JOIN certs ON jobs.targetId=certs.id" + " WHERE jobs.state='open' AND task='revoke'");
102 revokeCompleted = DatabaseConnection.getInstance().prepare("UPDATE certs SET revoked=NOW() WHERE id=?");
104 finishJob = DatabaseConnection.getInstance().prepare("UPDATE jobs SET state='done' WHERE id=?");
106 runner = new Thread() {
117 private synchronized static void work() {
120 } catch (IOException e2) {
121 e2.printStackTrace();
122 } catch (InterruptedException e2) {
123 e2.printStackTrace();
129 revokeCertificates();
131 SimpleSigner.class.wait(5000);
132 } catch (IOException e) {
134 } catch (SQLException e) {
136 } catch (InterruptedException e1) {
142 private static void revokeCertificates() throws SQLException, IOException, InterruptedException {
143 GigiResultSet rs = revoke.executeQuery();
144 boolean worked = false;
146 int id = rs.getInt(1);
147 File crt = KeyStorage.locateCrt(id);
148 String[] call = new String[] {
151 "../unassured.crt",//
153 "../unassured.key",//
155 "../../" + crt.getPath(),//
161 Process p1 = Runtime.getRuntime().exec(call, null, new File("keys/unassured.ca"));
162 System.out.println("revoking: " + crt.getPath());
163 if (p1.waitFor() == 0) {
165 revokeCompleted.setInt(1, id);
166 revokeCompleted.execute();
167 finishJob.setInt(1, rs.getInt(3));
170 System.out.println("Failed");
178 private static void gencrl() throws IOException, InterruptedException {
179 String[] call = new String[] {
182 "../unassured.crt",//
184 "../unassured.key",//
189 "../unassured.crl",//
194 Process p1 = Runtime.getRuntime().exec(call, null, new File("keys/unassured.ca"));
195 if (p1.waitFor() != 0) {
196 System.out.println("Error while generating crl.");
200 private static int counter = 0;
202 private static void signCertificates() throws SQLException {
203 System.out.println("Checking...");
204 GigiResultSet rs = readyCerts.executeQuery();
206 Calendar c = Calendar.getInstance();
207 c.setTimeZone(TimeZone.getTimeZone("UTC"));
210 System.out.println("Task");
211 String csrname = rs.getString("csr_name");
212 int id = rs.getInt("id");
213 System.out.println("sign: " + csrname);
215 String csrType = rs.getString("csr_type");
216 CSRType ct = CSRType.valueOf(csrType);
217 File crt = KeyStorage.locateCrt(id);
219 Timestamp from = rs.getTimestamp("executeFrom");
220 String length = rs.getString("executeTo");
224 fromDate = new Date(System.currentTimeMillis());
226 fromDate = new Date(from.getTime());
228 if (length.endsWith("m") || length.endsWith("y")) {
229 String num = length.substring(0, length.length() - 1);
230 int inter = Integer.parseInt(num);
232 if (length.endsWith("m")) {
233 c.add(Calendar.MONTH, inter);
235 c.add(Calendar.YEAR, inter);
237 toDate = c.getTime();
239 toDate = DateSelector.getDateFormat().parse(length);
242 getSANSs.setInt(1, id);
243 GigiResultSet san = getSANSs.executeQuery();
245 File f = new File("keys", "SANFile" + System.currentTimeMillis() + (counter++) + ".cfg");
246 PrintWriter cfg = new PrintWriter(new OutputStreamWriter(new FileOutputStream(f), "UTF-8"));
247 boolean first = true;
252 cfg.print("subjectAltName=");
255 cfg.print(san.getString("type"));
257 cfg.print(san.getString("contents"));
260 // TODO look them up!
261 cfg.println("keyUsage=critical," + "digitalSignature, keyEncipherment, keyAgreement");
262 cfg.println("extendedKeyUsage=critical," + "clientAuth");
265 int profile = rs.getInt("profile");
266 String ca = "unassured";
269 } else if (profile != 1) {
272 HashMap<String, String> subj = new HashMap<>();
273 GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("SELECT name, value FROM certAvas WHERE certId=?");
274 ps.setInt(1, rs.getInt("id"));
275 GigiResultSet rs2 = ps.executeQuery();
277 String name = rs2.getString("name");
278 if (name.equals("EMAIL")) {
279 name = "emailAddress";
281 subj.put(name, rs2.getString("value"));
283 if (subj.size() == 0) {
284 subj.put("CN", "<empty>");
285 System.out.println("WARNING: DN was empty");
287 System.out.println(subj);
290 call = new String[] {
293 "../../" + csrname,//
295 "../" + ca + ".crt",//
297 "../" + ca + ".key",//
299 "../../" + crt.getPath(),//
302 sdf.format(fromDate),//
304 sdf.format(toDate),//
307 rs.getString("md"),//
309 "../" + f.getName(),//
312 Certificate.stringifyDN(subj),//
314 "../selfsign.config"//
316 for (String string : call) {
317 System.out.print(" " + string);
319 System.out.println();
322 if (ct == CSRType.SPKAC) {
326 Process p1 = Runtime.getRuntime().exec(call, null, new File("keys/unassured.ca"));
328 int waitFor = p1.waitFor();
330 * if ( !f.delete()) {
331 * System.err.println("Could not delete SAN-File " +
332 * f.getAbsolutePath()); }
335 try (InputStream is = new FileInputStream(crt)) {
336 CertificateFactory cf = CertificateFactory.getInstance("X.509");
337 X509Certificate crtp = (X509Certificate) cf.generateCertificate(is);
338 BigInteger serial = crtp.getSerialNumber();
339 updateMail.setString(1, crt.getPath());
340 updateMail.setString(2, serial.toString(16));
341 updateMail.setInt(3, id);
342 updateMail.execute();
344 finishJob.setInt(1, rs.getInt("jobid"));
346 System.out.println("signed: " + id);
350 BufferedReader br = new BufferedReader(new InputStreamReader(p1.getErrorStream(), "UTF-8"));
352 while ((s = br.readLine()) != null) {
353 System.out.println(s);
356 } catch (GeneralSecurityException e) {
358 } catch (IOException e) {
360 } catch (ParseException e) {
362 } catch (InterruptedException e1) {
363 e1.printStackTrace();
365 System.out.println("Error with: " + id);
366 warnMail.setInt(1, rs.getInt("jobid"));