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