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