1 package org.cacert.gigi.util;
3 import java.io.BufferedReader;
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.HashMap;
22 import java.util.Properties;
23 import java.util.TimeZone;
25 import org.cacert.gigi.database.DatabaseConnection;
26 import org.cacert.gigi.database.GigiPreparedStatement;
27 import org.cacert.gigi.database.GigiResultSet;
28 import org.cacert.gigi.dbObjects.Certificate;
29 import org.cacert.gigi.dbObjects.Certificate.CSRType;
30 import org.cacert.gigi.output.DateSelector;
32 public class SimpleSigner {
34 private static GigiPreparedStatement warnMail;
36 private static GigiPreparedStatement updateMail;
38 private static GigiPreparedStatement readyCerts;
40 private static GigiPreparedStatement getSANSs;
42 private static GigiPreparedStatement revoke;
44 private static GigiPreparedStatement revokeCompleted;
46 private static GigiPreparedStatement finishJob;
48 private static boolean running = true;
50 private static Thread runner;
52 private static SimpleDateFormat sdf = new SimpleDateFormat("yyMMddHHmmss'Z'");
55 TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
56 sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
59 public static void main(String[] args) throws IOException, SQLException, InterruptedException {
60 Properties p = new Properties();
61 try (FileReader reader = new FileReader("config/gigi.properties")) {
64 DatabaseConnection.init(p);
69 public synchronized static void stopSigner() throws InterruptedException {
71 throw new IllegalStateException("already stopped");
79 public synchronized static void runSigner() throws SQLException, IOException, InterruptedException {
81 throw new IllegalStateException("already running");
84 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 " + //
85 "INNER JOIN certs ON certs.id=jobs.targetId " + //
86 "INNER JOIN profiles ON profiles.id=certs.profile " + //
87 "WHERE jobs.state='open' "//
90 getSANSs = DatabaseConnection.getInstance().prepare("SELECT contents, type FROM subjectAlternativeNames " + //
93 updateMail = DatabaseConnection.getInstance().prepare("UPDATE certs SET crt_name=?," + " created=NOW(), serial=? WHERE id=?");
94 warnMail = DatabaseConnection.getInstance().prepare("UPDATE jobs SET warning=warning+1, state=IF(warning<3, 'open','error') WHERE id=?");
96 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'");
97 revokeCompleted = DatabaseConnection.getInstance().prepare("UPDATE certs SET revoked=NOW() WHERE id=?");
99 finishJob = DatabaseConnection.getInstance().prepare("UPDATE jobs SET state='done' WHERE id=?");
101 runner = new Thread() {
112 private static void work() {
115 } catch (IOException e2) {
116 e2.printStackTrace();
117 } catch (InterruptedException e2) {
118 e2.printStackTrace();
123 revokeCertificates();
125 } catch (IOException e) {
127 } catch (SQLException e) {
129 } catch (InterruptedException e1) {
134 private static void revokeCertificates() throws SQLException, IOException, InterruptedException {
135 GigiResultSet rs = revoke.executeQuery();
136 boolean worked = false;
138 int id = rs.getInt(1);
139 File crt = KeyStorage.locateCrt(id);
140 String[] call = new String[] {
143 "../unassured.crt",//
145 "../unassured.key",//
147 "../../" + crt.getPath(),//
153 Process p1 = Runtime.getRuntime().exec(call, null, new File("keys/unassured.ca"));
154 System.out.println("revoking: " + crt.getPath());
155 if (p1.waitFor() == 0) {
157 revokeCompleted.setInt(1, id);
158 revokeCompleted.execute();
159 finishJob.setInt(1, rs.getInt(3));
162 System.out.println("Failed");
170 private static void gencrl() throws IOException, InterruptedException {
171 String[] call = new String[] {
174 "../unassured.crt",//
176 "../unassured.key",//
181 "../unassured.crl",//
186 Process p1 = Runtime.getRuntime().exec(call, null, new File("keys/unassured.ca"));
187 if (p1.waitFor() != 0) {
188 System.out.println("Error while generating crl.");
192 private static int counter = 0;
194 private static void signCertificates() throws SQLException {
195 GigiResultSet rs = readyCerts.executeQuery();
197 Calendar c = Calendar.getInstance();
198 c.setTimeZone(TimeZone.getTimeZone("UTC"));
201 String csrname = rs.getString("csr_name");
202 int id = rs.getInt("id");
203 System.out.println("sign: " + csrname);
205 String csrType = rs.getString("csr_type");
206 CSRType ct = CSRType.valueOf(csrType);
207 File crt = KeyStorage.locateCrt(id);
209 String keyUsage = rs.getString("keyUsage");
210 String ekeyUsage = rs.getString("extendedKeyUsage");
212 Timestamp from = rs.getTimestamp("executeFrom");
213 String length = rs.getString("executeTo");
217 fromDate = new Date(System.currentTimeMillis());
219 fromDate = new Date(from.getTime());
221 if (length.endsWith("m") || length.endsWith("y")) {
222 String num = length.substring(0, length.length() - 1);
223 int inter = Integer.parseInt(num);
225 if (length.endsWith("m")) {
226 c.add(Calendar.MONTH, inter);
228 c.add(Calendar.YEAR, inter);
230 toDate = c.getTime();
232 toDate = DateSelector.getDateFormat().parse(length);
235 getSANSs.setInt(1, id);
236 GigiResultSet san = getSANSs.executeQuery();
238 File f = new File("keys", "SANFile" + System.currentTimeMillis() + (counter++) + ".cfg");
239 PrintWriter cfg = new PrintWriter(f);
240 boolean first = true;
245 cfg.print("subjectAltName=");
248 cfg.print(san.getString("type"));
250 cfg.print(san.getString("contents"));
253 cfg.println("keyUsage=critical," + keyUsage);
254 cfg.println("extendedKeyUsage=critical," + ekeyUsage);
257 int rootcert = rs.getInt("rootcert");
258 String ca = "unassured";
261 } else if (rootcert == 1) {
264 HashMap<String, String> subj = new HashMap<>();
265 GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("SELECT name, value FROM certAvas WHERE certId=?");
266 ps.setInt(1, rs.getInt("id"));
267 GigiResultSet rs2 = ps.executeQuery();
269 subj.put(rs2.getString("name"), rs2.getString("value"));
271 if (subj.size() == 0) {
272 subj.put("CN", "<empty>");
273 System.out.println("WARNING: DN was empty");
275 String[] call = new String[] {
278 "../../" + csrname,//
280 "../" + ca + ".crt",//
282 "../" + ca + ".key",//
284 "../../" + crt.getPath(),//
287 sdf.format(fromDate),//
289 sdf.format(toDate),//
292 rs.getString("md"),//
294 "../" + f.getName(),//
297 Certificate.stringifyDN(subj),//
299 "../selfsign.config"//
302 if (ct == CSRType.SPKAC) {
305 Process p1 = Runtime.getRuntime().exec(call, null, new File("keys/unassured.ca"));
307 int waitFor = p1.waitFor();
310 try (InputStream is = new FileInputStream(crt)) {
311 CertificateFactory cf = CertificateFactory.getInstance("X.509");
312 X509Certificate crtp = (X509Certificate) cf.generateCertificate(is);
313 BigInteger serial = crtp.getSerialNumber();
314 updateMail.setString(1, crt.getPath());
315 updateMail.setString(2, serial.toString(16));
316 updateMail.setInt(3, id);
317 updateMail.execute();
319 finishJob.setInt(1, rs.getInt("jobid"));
321 System.out.println("signed: " + id);
325 BufferedReader br = new BufferedReader(new InputStreamReader(p1.getErrorStream()));
327 while ((s = br.readLine()) != null) {
328 System.out.println(s);
331 } catch (GeneralSecurityException e) {
333 } catch (IOException e) {
335 } catch (ParseException e) {
337 } catch (InterruptedException e1) {
338 e1.printStackTrace();
340 System.out.println("Error with: " + id);
341 warnMail.setInt(1, rs.getInt("jobid"));