75bb50990aebb1c7200b03f124672032659aaffb
[gigi.git] / util / org / cacert / gigi / util / SimpleSigner.java
1 package org.cacert.gigi.util;
2
3 import java.io.BufferedReader;
4 import java.io.File;
5 import java.io.FileInputStream;
6 import java.io.FileReader;
7 import java.io.IOException;
8 import java.io.InputStream;
9 import java.io.InputStreamReader;
10 import java.io.PrintWriter;
11 import java.math.BigInteger;
12 import java.security.GeneralSecurityException;
13 import java.security.cert.CertificateFactory;
14 import java.security.cert.X509Certificate;
15 import java.sql.SQLException;
16 import java.sql.Timestamp;
17 import java.text.ParseException;
18 import java.text.SimpleDateFormat;
19 import java.util.Calendar;
20 import java.util.Date;
21 import java.util.Properties;
22 import java.util.TimeZone;
23
24 import org.cacert.gigi.database.DatabaseConnection;
25 import org.cacert.gigi.database.GigiPreparedStatement;
26 import org.cacert.gigi.database.GigiResultSet;
27 import org.cacert.gigi.dbObjects.Certificate.CSRType;
28 import org.cacert.gigi.output.DateSelector;
29
30 public class SimpleSigner {
31
32     private static GigiPreparedStatement warnMail;
33
34     private static GigiPreparedStatement updateMail;
35
36     private static GigiPreparedStatement readyCerts;
37
38     private static GigiPreparedStatement getSANSs;
39
40     private static GigiPreparedStatement revoke;
41
42     private static GigiPreparedStatement revokeCompleted;
43
44     private static GigiPreparedStatement finishJob;
45
46     private static boolean running = true;
47
48     private static Thread runner;
49
50     private static SimpleDateFormat sdf = new SimpleDateFormat("YYMMddHHmmss'Z'");
51
52     static {
53         TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
54         sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
55     }
56
57     public static void main(String[] args) throws IOException, SQLException, InterruptedException {
58         Properties p = new Properties();
59         p.load(new FileReader("config/gigi.properties"));
60         DatabaseConnection.init(p);
61
62         runSigner();
63     }
64
65     public synchronized static void stopSigner() throws InterruptedException {
66         if (runner == null) {
67             throw new IllegalStateException("already stopped");
68         }
69         running = false;
70         runner.interrupt();
71         runner.join();
72         runner = null;
73     }
74
75     public synchronized static void runSigner() throws SQLException, IOException, InterruptedException {
76         if (runner != null) {
77             throw new IllegalStateException("already running");
78         }
79         running = true;
80         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, rootcert FROM jobs " + //
81                 "INNER JOIN certs ON certs.id=jobs.targetId " + //
82                 "INNER JOIN profiles ON profiles.id=certs.profile " + //
83                 "WHERE jobs.state='open' "//
84                 + "AND task='sign'");
85
86         getSANSs = DatabaseConnection.getInstance().prepare("SELECT contents, type FROM subjectAlternativeNames " + //
87                 "WHERE certId=?");
88
89         updateMail = DatabaseConnection.getInstance().prepare("UPDATE certs SET crt_name=?," + " created=NOW(), serial=? WHERE id=?");
90         warnMail = DatabaseConnection.getInstance().prepare("UPDATE jobs SET warning=warning+1, state=IF(warning<3, 'open','error') WHERE id=?");
91
92         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'");
93         revokeCompleted = DatabaseConnection.getInstance().prepare("UPDATE certs SET revoked=NOW() WHERE id=?");
94
95         finishJob = DatabaseConnection.getInstance().prepare("UPDATE jobs SET state='done' WHERE id=?");
96
97         runner = new Thread() {
98
99             @Override
100             public void run() {
101                 work();
102             }
103
104         };
105         runner.start();
106     }
107
108     private static void work() {
109         try {
110             gencrl();
111         } catch (IOException e2) {
112             e2.printStackTrace();
113         } catch (InterruptedException e2) {
114             e2.printStackTrace();
115         }
116         while (running) {
117             try {
118                 signCertificates();
119                 revokeCertificates();
120                 Thread.sleep(5000);
121             } catch (IOException e) {
122                 e.printStackTrace();
123             } catch (SQLException e) {
124                 e.printStackTrace();
125             } catch (InterruptedException e1) {
126             }
127         }
128     }
129
130     private static void revokeCertificates() throws SQLException, IOException, InterruptedException {
131         GigiResultSet rs = revoke.executeQuery();
132         boolean worked = false;
133         while (rs.next()) {
134             int id = rs.getInt(1);
135             File crt = KeyStorage.locateCrt(id);
136             String[] call = new String[] {
137                     "openssl", "ca",//
138                     "-cert",
139                     "../unassured.crt",//
140                     "-keyfile",
141                     "../unassured.key",//
142                     "-revoke",
143                     "../../" + crt.getPath(),//
144                     "-batch",//
145                     "-config",
146                     "../selfsign.config"
147
148             };
149             Process p1 = Runtime.getRuntime().exec(call, null, new File("keys/unassured.ca"));
150             System.out.println("revoking: " + crt.getPath());
151             if (p1.waitFor() == 0) {
152                 worked = true;
153                 revokeCompleted.setInt(1, id);
154                 revokeCompleted.execute();
155                 finishJob.setInt(1, rs.getInt(3));
156                 finishJob.execute();
157             } else {
158                 System.out.println("Failed");
159             }
160         }
161         if (worked) {
162             gencrl();
163         }
164     }
165
166     private static void gencrl() throws IOException, InterruptedException {
167         String[] call = new String[] {
168                 "openssl", "ca",//
169                 "-cert",
170                 "../unassured.crt",//
171                 "-keyfile",
172                 "../unassured.key",//
173                 "-gencrl",//
174                 "-crlhours",//
175                 "12",//
176                 "-out",
177                 "../unassured.crl",//
178                 "-config",
179                 "../selfsign.config"
180
181         };
182         Process p1 = Runtime.getRuntime().exec(call, null, new File("keys/unassured.ca"));
183         if (p1.waitFor() != 0) {
184             System.out.println("Error while generating crl.");
185         }
186     }
187
188     private static int counter = 0;
189
190     private static void signCertificates() throws SQLException {
191         GigiResultSet rs = readyCerts.executeQuery();
192
193         Calendar c = Calendar.getInstance();
194         c.setTimeZone(TimeZone.getTimeZone("UTC"));
195
196         while (rs.next()) {
197             String csrname = rs.getString("csr_name");
198             int id = rs.getInt("id");
199             System.out.println("sign: " + csrname);
200             try {
201                 String csrType = rs.getString("csr_type");
202                 CSRType ct = CSRType.valueOf(csrType);
203                 File crt = KeyStorage.locateCrt(id);
204
205                 String keyUsage = rs.getString("keyUsage");
206                 String ekeyUsage = rs.getString("extendedKeyUsage");
207
208                 Timestamp from = rs.getTimestamp("executeFrom");
209                 String length = rs.getString("executeTo");
210                 Date fromDate;
211                 Date toDate;
212                 if (from == null) {
213                     fromDate = new Date(System.currentTimeMillis());
214                 } else {
215                     fromDate = new Date(from.getTime());
216                 }
217                 if (length.endsWith("m") || length.endsWith("y")) {
218                     String num = length.substring(0, length.length() - 1);
219                     int inter = Integer.parseInt(num);
220                     c.setTime(fromDate);
221                     if (length.endsWith("m")) {
222                         c.add(Calendar.MONTH, inter);
223                     } else {
224                         c.add(Calendar.YEAR, inter);
225                     }
226                     toDate = c.getTime();
227                 } else {
228                     toDate = DateSelector.getDateFormat().parse(length);
229                 }
230
231                 getSANSs.setInt(1, id);
232                 GigiResultSet san = getSANSs.executeQuery();
233
234                 File f = new File("keys", "SANFile" + System.currentTimeMillis() + (counter++) + ".cfg");
235                 PrintWriter cfg = new PrintWriter(f);
236                 boolean first = true;
237                 while (san.next()) {
238                     if ( !first) {
239                         cfg.print(", ");
240                     } else {
241                         cfg.print("subjectAltName=");
242                     }
243                     first = false;
244                     cfg.print(san.getString("type"));
245                     cfg.print(":");
246                     cfg.print(san.getString("contents"));
247                 }
248                 cfg.println();
249                 cfg.println("keyUsage=critical," + keyUsage);
250                 cfg.println("extendedKeyUsage=critical," + ekeyUsage);
251                 cfg.close();
252
253                 int rootcert = rs.getInt("rootcert");
254                 String ca = "unassured";
255                 if (rootcert == 0) {
256                     ca = "unassured";
257                 } else if (rootcert == 1) {
258                     ca = "assured";
259                 }
260
261                 String[] call = new String[] {
262                         "openssl", "ca",//
263                         "-in",
264                         "../../" + csrname,//
265                         "-cert",
266                         "../" + ca + ".crt",//
267                         "-keyfile",
268                         "../" + ca + ".key",//
269                         "-out",
270                         "../../" + crt.getPath(),//
271                         "-utf8",
272                         "-startdate",
273                         sdf.format(fromDate),//
274                         "-enddate",
275                         sdf.format(toDate),//
276                         "-batch",//
277                         "-md",
278                         rs.getString("md"),//
279                         "-extfile",
280                         "../" + f.getName(),//
281
282                         "-subj",
283                         rs.getString("subject"),//
284                         "-config",
285                         "../selfsign.config"//
286
287                 };
288                 if (ct == CSRType.SPKAC) {
289                     call[2] = "-spkac";
290                 }
291                 Process p1 = Runtime.getRuntime().exec(call, null, new File("keys/unassured.ca"));
292
293                 int waitFor = p1.waitFor();
294                 f.delete();
295                 if (waitFor == 0) {
296                     try (InputStream is = new FileInputStream(crt)) {
297                         CertificateFactory cf = CertificateFactory.getInstance("X.509");
298                         X509Certificate crtp = (X509Certificate) cf.generateCertificate(is);
299                         BigInteger serial = crtp.getSerialNumber();
300                         updateMail.setString(1, crt.getPath());
301                         updateMail.setString(2, serial.toString(16));
302                         updateMail.setInt(3, id);
303                         updateMail.execute();
304
305                         finishJob.setInt(1, rs.getInt("jobid"));
306                         finishJob.execute();
307                         System.out.println("signed: " + id);
308                         continue;
309                     }
310                 } else {
311                     BufferedReader br = new BufferedReader(new InputStreamReader(p1.getErrorStream()));
312                     String s;
313                     while ((s = br.readLine()) != null) {
314                         System.out.println(s);
315                     }
316                 }
317             } catch (GeneralSecurityException e) {
318                 e.printStackTrace();
319             } catch (IOException e) {
320                 e.printStackTrace();
321             } catch (ParseException e) {
322                 e.printStackTrace();
323             } catch (InterruptedException e1) {
324                 e1.printStackTrace();
325             }
326             System.out.println("Error with: " + id);
327             warnMail.setInt(1, rs.getInt("jobid"));
328             warnMail.execute();
329
330         }
331         rs.close();
332     }
333 }