1 package club.wpia.gigi.dbObjects;
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;
17 import javax.security.auth.x500.X500Principal;
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;
24 public class CACertificate implements IdCachable {
26 private final String keyname;
30 private CACertificate parent = null;
32 private final X509Certificate cert;
34 private final String link;
36 private static final CACertificate[] instances;
38 private static ObjectCache<CACertificate> myCache = new ObjectCache<>();
40 private CACertificate(int id) {
43 try (GigiPreparedStatement conn = new GigiPreparedStatement("SELECT `keyname`, `parentRoot`, `link` FROM `cacerts` WHERE `id`=?")) {
45 GigiResultSet res = conn.executeQuery();
47 throw new IllegalArgumentException();
49 keyname = res.getString("keyname");
50 link = res.getString("link");
51 parentRoot = res.getInt("parentRoot");
53 throw new RuntimeException("DB is broken");
56 if (parentRoot == id) {
59 parent = getById(parentRoot);
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) {
67 } catch (GeneralSecurityException e) {
72 public CACertificate getParent() {
76 public X509Certificate getCertificate() {
81 public String toString() {
82 return "CACertificate: " + keyname;
88 try (GigiPreparedStatement q = new GigiPreparedStatement("SELECT `id` FROM `cacerts`", true)) {
89 GigiResultSet res = q.executeQuery();
91 CACertificate[] certs = new CACertificate[res.getRow()];
95 certs[i++] = getById(res.getInt(1));
99 } catch (CertificateException e) {
101 } catch (FileNotFoundException e) {
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];
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));
122 HashMap<X500Principal, Integer> inserted = new HashMap<>();
123 for (X509Certificate i : map.values()) {
124 if (inserted.containsKey(i.getSubjectX500Principal())) {
127 Deque<X509Certificate> toInserts = new ArrayDeque<>();
129 while ( !inserted.containsKey(i.getIssuerX500Principal()) && !i.getIssuerX500Principal().equals(i.getSubjectX500Principal())) {
130 i = map.get(i.getIssuerX500Principal());
131 toInserts.addFirst(i);
133 for (X509Certificate toInsert : toInserts) {
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();
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"));
148 String keyname = names.get(subj);
149 if ( !keyname.contains("_")) {
150 link = "https://" + ServerConstants.getHostNamePortSecure(Host.CRT_REPO) + "/g2/" + keyname + ".crt";
152 String[] parts = keyname.split("_");
153 link = "https://" + ServerConstants.getHostNamePortSecure(Host.CRT_REPO) + "/g2/" + parts[1] + "/" + parts[0] + "-" + parts[2] + ".crt";
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);
161 id = q2.lastInsertId();
164 try (GigiPreparedStatement q3 = new GigiPreparedStatement("UPDATE `cacerts` SET `parentRoot`=? WHERE `id`=?")) {
171 inserted.put(subj, id);
182 public String getKeyname() {
186 public String getLink() {
190 public static synchronized CACertificate getById(int id) throws IllegalArgumentException {
191 CACertificate em = myCache.get(id);
193 myCache.put(em = new CACertificate(id));
198 public boolean isSelfsigned() {
199 return this == getParent();
202 public String getFingerprint(String algorithm) throws CertificateEncodingException, NoSuchAlgorithmException {
203 return Certificate.getFingerprint(cert, algorithm);
206 public static synchronized CACertificate[] getAll() {
207 return Arrays.copyOf(instances, instances.length);