]> WPIA git - gigi.git/blob - src/org/cacert/gigi/dbObjects/CACertificate.java
fix: ResultSet.getDate is often wrong as it fetches day-precision times
[gigi.git] / src / org / cacert / gigi / dbObjects / CACertificate.java
1 package org.cacert.gigi.dbObjects;
2
3 import java.io.File;
4 import java.io.FileInputStream;
5 import java.io.FileNotFoundException;
6 import java.security.GeneralSecurityException;
7 import java.security.cert.CertificateException;
8 import java.security.cert.CertificateFactory;
9 import java.security.cert.X509Certificate;
10 import java.util.ArrayDeque;
11 import java.util.Deque;
12 import java.util.HashMap;
13
14 import javax.security.auth.x500.X500Principal;
15
16 import org.cacert.gigi.database.GigiPreparedStatement;
17 import org.cacert.gigi.database.GigiResultSet;
18 import org.cacert.gigi.util.ServerConstants;
19
20 public class CACertificate implements IdCachable {
21
22     private final String keyname;
23
24     private final int id;
25
26     private CACertificate parent = null;
27
28     private final X509Certificate cert;
29
30     private final String link;
31
32     private CACertificate(int id) {
33         this.id = id;
34         int parentRoot;
35         try (GigiPreparedStatement conn = new GigiPreparedStatement("SELECT `keyname`, `parentRoot`, `link` FROM `cacerts` WHERE `id`=?")) {
36             conn.setInt(1, id);
37             GigiResultSet res = conn.executeQuery();
38             if ( !res.next()) {
39                 throw new IllegalArgumentException();
40             }
41             keyname = res.getString("keyname");
42             link = res.getString("link");
43             parentRoot = res.getInt("parentRoot");
44             if (res.next()) {
45                 throw new RuntimeException("DB is broken");
46             }
47         }
48         if (parentRoot == id) {
49             parent = this;
50         } else {
51             parent = getById(parentRoot);
52         }
53         try {
54             FileInputStream fis = new FileInputStream("config/ca/" + keyname + ".crt");
55             CertificateFactory cf = CertificateFactory.getInstance("X509");
56             cert = (X509Certificate) cf.generateCertificate(fis);
57         } catch (FileNotFoundException e) {
58             throw new Error(e);
59         } catch (GeneralSecurityException e) {
60             throw new Error(e);
61         }
62     }
63
64     public CACertificate getParent() {
65         return parent;
66     }
67
68     public X509Certificate getCertificate() {
69         return cert;
70     }
71
72     @Override
73     public String toString() {
74         return "CACertificate: " + keyname;
75     }
76
77     static {
78         try {
79             update();
80         } catch (CertificateException e) {
81             throw new Error(e);
82         } catch (FileNotFoundException e) {
83             throw new Error(e);
84         }
85     }
86
87     private static void update() throws CertificateException, FileNotFoundException {
88         File scandir = new File("config/ca");
89         CertificateFactory xf = CertificateFactory.getInstance("X509");
90         HashMap<X500Principal, X509Certificate> map = new HashMap<>();
91         HashMap<X500Principal, String> names = new HashMap<>();
92         File[] scandirfiles = scandir.listFiles();
93         if (null == scandirfiles) {
94             scandirfiles = new File[0];
95         }
96         for (File f : scandirfiles) {
97             X509Certificate cert = (X509Certificate) xf.generateCertificate(new FileInputStream(f));
98             X500Principal princip = cert.getSubjectX500Principal();
99             map.put(princip, cert);
100             String name = f.getName();
101             names.put(princip, name.substring(0, name.length() - 4));
102         }
103         HashMap<X500Principal, Integer> inserted = new HashMap<>();
104         for (X509Certificate i : map.values()) {
105             if (inserted.containsKey(i.getSubjectX500Principal())) {
106                 continue;
107             }
108             Deque<X509Certificate> toInserts = new ArrayDeque<>();
109             toInserts.add(i);
110             while ( !inserted.containsKey(i.getIssuerX500Principal()) && !i.getIssuerX500Principal().equals(i.getSubjectX500Principal())) {
111                 i = map.get(i.getIssuerX500Principal());
112                 toInserts.addFirst(i);
113             }
114             for (X509Certificate toInsert : toInserts) {
115
116                 X500Principal subj = toInsert.getSubjectX500Principal();
117                 boolean self = toInsert.getIssuerX500Principal().equals(subj);
118                 try (GigiPreparedStatement q = new GigiPreparedStatement("SELECT `id`, `parentRoot` FROM `cacerts` WHERE `keyname`=?")) {
119                     q.setString(1, names.get(subj));
120                     GigiResultSet res = q.executeQuery();
121                     int id;
122                     if (res.next()) {
123                         id = res.getInt("id");
124                         if (res.getInt("parentRoot") != (self ? id : inserted.get(toInsert.getIssuerX500Principal()))) {
125                             throw new Error("Invalid DB structure: " + subj + "->" + inserted.get(toInsert.getIssuerX500Principal()) + " vs " + res.getInt("parentRoot"));
126                         }
127                     } else {
128                         String link;
129                         String keyname = names.get(subj);
130                         if ( !keyname.contains("_")) {
131                             link = "https://g2.crt." + ServerConstants.getSuffix() + "/g2/" + keyname + ".crt";
132                         } else {
133                             String[] parts = keyname.split("_");
134                             link = "https://g2.crt." + ServerConstants.getSuffix() + "/g2/" + parts[1] + "/" + parts[0] + "-" + parts[2] + ".crt";
135
136                         }
137                         try (GigiPreparedStatement q2 = new GigiPreparedStatement("INSERT INTO `cacerts` SET `parentRoot`=?, `keyname`=?, `link`=?")) {
138                             q2.setInt(1, self ? 0 : inserted.get(toInsert.getIssuerX500Principal()));
139                             q2.setString(2, keyname);
140                             q2.setString(3, link);
141                             q2.execute();
142                             id = q2.lastInsertId();
143                         }
144                         if (self) {
145                             try (GigiPreparedStatement q3 = new GigiPreparedStatement("UPDATE `cacerts` SET `parentRoot`=? WHERE `id`=?")) {
146                                 q3.setInt(1, id);
147                                 q3.setInt(2, id);
148                                 q3.execute();
149                             }
150                         }
151                     }
152                     inserted.put(subj, id);
153                 }
154             }
155         }
156     }
157
158     @Override
159     public int getId() {
160         return id;
161     }
162
163     private static ObjectCache<CACertificate> myCache = new ObjectCache<>();
164
165     public String getKeyname() {
166         return keyname;
167     }
168
169     public String getLink() {
170         return link;
171     }
172
173     public static synchronized CACertificate getById(int id) throws IllegalArgumentException {
174         CACertificate em = myCache.get(id);
175         if (em == null) {
176             myCache.put(em = new CACertificate(id));
177         }
178         return em;
179     }
180
181     public boolean isSelfsigned() {
182         return this == getParent();
183     }
184
185 }