]> WPIA git - gigi.git/blob - src/org/cacert/gigi/dbObjects/CACertificate.java
fix: SQL change database call pattern
[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         for (File f : scandir.listFiles()) {
93             X509Certificate cert = (X509Certificate) xf.generateCertificate(new FileInputStream(f));
94             X500Principal princip = cert.getSubjectX500Principal();
95             map.put(princip, cert);
96             String name = f.getName();
97             names.put(princip, name.substring(0, name.length() - 4));
98         }
99         HashMap<X500Principal, Integer> inserted = new HashMap<>();
100         for (X509Certificate i : map.values()) {
101             if (inserted.containsKey(i.getSubjectX500Principal())) {
102                 continue;
103             }
104             Deque<X509Certificate> toInserts = new ArrayDeque<>();
105             toInserts.add(i);
106             while ( !inserted.containsKey(i.getIssuerX500Principal()) && !i.getIssuerX500Principal().equals(i.getSubjectX500Principal())) {
107                 i = map.get(i.getIssuerX500Principal());
108                 toInserts.addFirst(i);
109             }
110             for (X509Certificate toInsert : toInserts) {
111
112                 X500Principal subj = toInsert.getSubjectX500Principal();
113                 boolean self = toInsert.getIssuerX500Principal().equals(subj);
114                 try (GigiPreparedStatement q = new GigiPreparedStatement("SELECT `id`, `parentRoot` FROM `cacerts` WHERE `keyname`=?")) {
115                     q.setString(1, names.get(subj));
116                     GigiResultSet res = q.executeQuery();
117                     int id;
118                     if (res.next()) {
119                         id = res.getInt("id");
120                         if (res.getInt("parentRoot") != (self ? id : inserted.get(toInsert.getIssuerX500Principal()))) {
121                             throw new Error("Invalid DB structure: " + subj + "->" + inserted.get(toInsert.getIssuerX500Principal()) + " vs " + res.getInt("parentRoot"));
122                         }
123                     } else {
124                         String link;
125                         String keyname = names.get(subj);
126                         if ( !keyname.contains("_")) {
127                             link = "https://g2.crt." + ServerConstants.getSuffix() + "/g2/" + keyname + ".crt";
128                         } else {
129                             String[] parts = keyname.split("_");
130                             link = "https://g2.crt." + ServerConstants.getSuffix() + "/g2/" + parts[1] + "/" + parts[0] + "-" + parts[2] + ".crt";
131
132                         }
133                         try (GigiPreparedStatement q2 = new GigiPreparedStatement("INSERT INTO `cacerts` SET `parentRoot`=?, `keyname`=?, `link`=?")) {
134                             q2.setInt(1, self ? 0 : inserted.get(toInsert.getIssuerX500Principal()));
135                             q2.setString(2, keyname);
136                             q2.setString(3, link);
137                             q2.execute();
138                             id = q2.lastInsertId();
139                         }
140                         if (self) {
141                             try (GigiPreparedStatement q3 = new GigiPreparedStatement("UPDATE `cacerts` SET `parentRoot`=?, `id`=?")) {
142                                 q3.setInt(1, id);
143                                 q3.setInt(2, id);
144                                 q3.execute();
145                             }
146                         }
147                     }
148                     inserted.put(subj, id);
149                 }
150             }
151         }
152     }
153
154     @Override
155     public int getId() {
156         return id;
157     }
158
159     private static ObjectCache<CACertificate> myCache = new ObjectCache<>();
160
161     public String getKeyname() {
162         return keyname;
163     }
164
165     public String getLink() {
166         return link;
167     }
168
169     public static synchronized CACertificate getById(int id) throws IllegalArgumentException {
170         CACertificate em = myCache.get(id);
171         if (em == null) {
172             myCache.put(em = new CACertificate(id));
173         }
174         return em;
175     }
176
177     public boolean isSelfsigned() {
178         return this == getParent();
179     }
180
181 }