X-Git-Url: https://code.wpia.club/?p=gigi.git;a=blobdiff_plain;f=src%2Forg%2Fcacert%2Fgigi%2FdbObjects%2FCertificate.java;fp=src%2Forg%2Fcacert%2Fgigi%2FdbObjects%2FCertificate.java;h=0000000000000000000000000000000000000000;hp=2aecedf75e295f4a6af95fec61af7006bfafd3ac;hb=bccd4cc0dba0f89aa045b113bac46eb8cc1dab4e;hpb=c9ed09f0007fc2c813815be927a5a24b23dab83c diff --git a/src/org/cacert/gigi/dbObjects/Certificate.java b/src/org/cacert/gigi/dbObjects/Certificate.java deleted file mode 100644 index 2aecedf7..00000000 --- a/src/org/cacert/gigi/dbObjects/Certificate.java +++ /dev/null @@ -1,530 +0,0 @@ -package org.cacert.gigi.dbObjects; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.security.GeneralSecurityException; -import java.security.cert.CertificateFactory; -import java.security.cert.X509Certificate; -import java.sql.Date; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map.Entry; - -import org.cacert.gigi.GigiApiException; -import org.cacert.gigi.database.DBEnum; -import org.cacert.gigi.database.GigiPreparedStatement; -import org.cacert.gigi.database.GigiResultSet; -import org.cacert.gigi.output.template.Outputable; -import org.cacert.gigi.output.template.TranslateCommand; -import org.cacert.gigi.pages.account.certs.CertificateRequest; -import org.cacert.gigi.util.KeyStorage; - -public class Certificate implements IdCachable { - - public enum SANType implements DBEnum { - EMAIL("email"), DNS("DNS"); - - private final String opensslName; - - private SANType(String opensslName) { - this.opensslName = opensslName; - } - - public String getOpensslName() { - return opensslName; - } - - @Override - public String getDBName() { - return opensslName; - } - } - - public static class SubjectAlternateName implements Comparable { - - private SANType type; - - private String name; - - public SubjectAlternateName(SANType type, String name) { - this.type = type; - this.name = name; - } - - public String getName() { - return name; - } - - public SANType getType() { - return type; - } - - @Override - public int compareTo(SubjectAlternateName o) { - int i = type.compareTo(o.type); - if (i != 0) { - return i; - } - return name.compareTo(o.name); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((name == null) ? 0 : name.hashCode()); - result = prime * result + ((type == null) ? 0 : type.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - SubjectAlternateName other = (SubjectAlternateName) obj; - if (name == null) { - if (other.name != null) { - return false; - } - } else if ( !name.equals(other.name)) { - return false; - } - if (type != other.type) { - return false; - } - return true; - } - - } - - public enum CSRType { - CSR, SPKAC; - } - - private int id; - - private CertificateOwner owner; - - private String serial; - - private Digest md; - - private String csrName; - - private String crtName; - - private String csr = null; - - private CSRType csrType; - - private List sans; - - private CertificateProfile profile; - - private HashMap dn; - - private String dnString; - - private CACertificate ca; - - /** - * Creates a new Certificate. WARNING: this is an internal API. Creating - * certificates for users must be done using the {@link CertificateRequest} - * -API. - * - * @param owner - * the owner for whom the certificate should be created. - * @param actor - * the acting user that creates the certificate - * @param dn - * the distinguished name of the subject of this certificate (as - * Map using OpenSSL-Style keys) - * @param md - * the {@link Digest} to sign the certificate with - * @param csr - * the CSR/SPKAC-Request containing the public key in question - * @param csrType - * the type of the csr parameter - * @param profile - * the profile under which this certificate is to be issued - * @param sans - * additional subject alternative names - * @throws GigiApiException - * in case the request is malformed or internal errors occur - * @throws IOException - * when the request cannot be written. - */ - public Certificate(CertificateOwner owner, User actor, HashMap dn, Digest md, String csr, CSRType csrType, CertificateProfile profile, SubjectAlternateName... sans) throws GigiApiException, IOException { - if ( !profile.canBeIssuedBy(owner, actor)) { - throw new GigiApiException("You are not allowed to issue these certificates."); - } - this.owner = owner; - this.dn = dn; - if (dn.size() == 0) { - throw new GigiApiException("DN must not be empty."); - } - dnString = stringifyDN(dn); - this.md = md; - this.csr = csr; - this.csrType = csrType; - this.profile = profile; - this.sans = Arrays.asList(sans); - synchronized (Certificate.class) { - - try (GigiPreparedStatement inserter = new GigiPreparedStatement("INSERT INTO certs SET md=?::`mdType`, csr_type=?::`csrType`, crt_name='', memid=?, profile=?")) { - inserter.setString(1, md.toString().toLowerCase()); - inserter.setString(2, this.csrType.toString()); - inserter.setInt(3, owner.getId()); - inserter.setInt(4, profile.getId()); - inserter.execute(); - id = inserter.lastInsertId(); - } - - try (GigiPreparedStatement san = new GigiPreparedStatement("INSERT INTO `subjectAlternativeNames` SET `certId`=?, contents=?, type=?::`SANType`")) { - for (SubjectAlternateName subjectAlternateName : sans) { - san.setInt(1, id); - san.setString(2, subjectAlternateName.getName()); - san.setString(3, subjectAlternateName.getType().getOpensslName()); - san.execute(); - } - } - - try (GigiPreparedStatement insertAVA = new GigiPreparedStatement("INSERT INTO `certAvas` SET `certId`=?, name=?, value=?")) { - insertAVA.setInt(1, id); - for (Entry e : this.dn.entrySet()) { - insertAVA.setString(2, e.getKey()); - insertAVA.setString(3, e.getValue()); - insertAVA.execute(); - } - } - File csrFile = KeyStorage.locateCsr(id); - csrName = csrFile.getPath(); - try (FileOutputStream fos = new FileOutputStream(csrFile)) { - fos.write(this.csr.getBytes("UTF-8")); - } - try (GigiPreparedStatement updater = new GigiPreparedStatement("UPDATE `certs` SET `csr_name`=? WHERE id=?")) { - updater.setString(1, csrName); - updater.setInt(2, id); - updater.execute(); - } - - cache.put(this); - } - } - - private Certificate(GigiResultSet rs) { - this.id = rs.getInt("id"); - dnString = rs.getString("subject"); - md = Digest.valueOf(rs.getString("md").toUpperCase()); - csrName = rs.getString("csr_name"); - crtName = rs.getString("crt_name"); - owner = CertificateOwner.getById(rs.getInt("memid")); - profile = CertificateProfile.getById(rs.getInt("profile")); - this.serial = rs.getString("serial"); - - try (GigiPreparedStatement ps2 = new GigiPreparedStatement("SELECT `contents`, `type` FROM `subjectAlternativeNames` WHERE `certId`=?")) { - ps2.setInt(1, id); - GigiResultSet rs2 = ps2.executeQuery(); - sans = new LinkedList<>(); - while (rs2.next()) { - sans.add(new SubjectAlternateName(SANType.valueOf(rs2.getString("type").toUpperCase()), rs2.getString("contents"))); - } - } - } - - public enum CertificateStatus { - /** - * This certificate is not in the database, has no id and only exists as - * this java object. - */ - DRAFT("draft"), - /** - * The certificate has been signed. It is stored in the database. - * {@link Certificate#cert()} is valid. - */ - ISSUED("issued"), - - /** - * The certificate has been revoked. - */ - REVOKED("revoked"), - - /** - * If this certificate cannot be updated because an error happened in - * the signer. - */ - ERROR("error"); - - private final Outputable name; - - private CertificateStatus(String codename) { - this.name = new TranslateCommand(codename); - - } - - public Outputable getName() { - return name; - } - - } - - public synchronized CertificateStatus getStatus() { - try (GigiPreparedStatement searcher = new GigiPreparedStatement("SELECT crt_name, created, revoked, serial, caid FROM certs WHERE id=?")) { - searcher.setInt(1, id); - GigiResultSet rs = searcher.executeQuery(); - if ( !rs.next()) { - throw new IllegalStateException("Certificate not in Database"); - } - - crtName = rs.getString(1); - serial = rs.getString(4); - if (rs.getTimestamp(2) == null) { - return CertificateStatus.DRAFT; - } - ca = CACertificate.getById(rs.getInt("caid")); - if (rs.getTimestamp(2) != null && rs.getTimestamp(3) == null) { - return CertificateStatus.ISSUED; - } - return CertificateStatus.REVOKED; - } - } - - /** - * @param start - * the date from which on the certificate should be valid. (or - * null if it should be valid instantly) - * @param period - * the period for which the date should be valid. (a - * yyyy-mm-dd or a "2y" (2 calendar years), "6m" (6 - * months) - * @return A job which can be used to monitor the progress of this task. - * @throws IOException - * for problems with writing the CSR/SPKAC - * @throws GigiApiException - * if the period is bogus - */ - public Job issue(Date start, String period, User actor) throws IOException, GigiApiException { - if (getStatus() != CertificateStatus.DRAFT) { - throw new IllegalStateException(); - } - - return Job.sign(this, start, period); - - } - - public Job revoke() { - if (getStatus() != CertificateStatus.ISSUED) { - throw new IllegalStateException(); - } - return Job.revoke(this); - - } - - public CACertificate getParent() { - CertificateStatus status = getStatus(); - if (status != CertificateStatus.REVOKED && status != CertificateStatus.ISSUED) { - throw new IllegalStateException(status + " is not wanted here."); - } - return ca; - } - - public X509Certificate cert() throws IOException, GeneralSecurityException { - CertificateStatus status = getStatus(); - if (status != CertificateStatus.REVOKED && status != CertificateStatus.ISSUED) { - throw new IllegalStateException(status + " is not wanted here."); - } - InputStream is = null; - X509Certificate crt = null; - try { - is = new FileInputStream(crtName); - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - crt = (X509Certificate) cf.generateCertificate(is); - } finally { - if (is != null) { - is.close(); - } - } - return crt; - } - - public Certificate renew() { - return null; - } - - public int getId() { - return id; - } - - public String getSerial() { - getStatus(); - // poll changes - return serial; - } - - public String getDistinguishedName() { - return dnString; - } - - public Digest getMessageDigest() { - return md; - } - - public CertificateOwner getOwner() { - return owner; - } - - public List getSANs() { - return Collections.unmodifiableList(sans); - } - - public CertificateProfile getProfile() { - return profile; - } - - public synchronized static Certificate getBySerial(String serial) { - if (serial == null || "".equals(serial)) { - return null; - } - String concat = "string_agg(concat('/', `name`, '=', REPLACE(REPLACE(value, '\\\\', '\\\\\\\\'), '/', '\\\\/')), '')"; - try (GigiPreparedStatement ps = new GigiPreparedStatement("SELECT certs.id, " + concat + " as `subject`, `md`, `csr_name`, `crt_name`,`memid`, `profile`, `certs`.`serial` FROM `certs` LEFT JOIN `certAvas` ON `certAvas`.`certId`=`certs`.`id` WHERE `serial`=? GROUP BY `certs`.`id`")) { - ps.setString(1, serial); - GigiResultSet rs = ps.executeQuery(); - if ( !rs.next()) { - return null; - } - int id = rs.getInt(1); - Certificate c1 = cache.get(id); - if (c1 != null) { - return c1; - } - Certificate certificate = new Certificate(rs); - cache.put(certificate); - return certificate; - } - } - - private static ObjectCache cache = new ObjectCache<>(); - - public synchronized static Certificate getById(int id) { - Certificate cacheRes = cache.get(id); - if (cacheRes != null) { - return cacheRes; - } - - try { - String concat = "string_agg(concat('/', `name`, '=', REPLACE(REPLACE(value, '\\\\', '\\\\\\\\'), '/', '\\\\/')), '')"; - try (GigiPreparedStatement ps = new GigiPreparedStatement("SELECT certs.id, " + concat + " as subject, md, csr_name, crt_name,memid, profile, certs.serial FROM `certs` LEFT JOIN `certAvas` ON `certAvas`.`certId`=certs.id WHERE certs.id=? GROUP BY certs.id")) { - ps.setInt(1, id); - GigiResultSet rs = ps.executeQuery(); - if ( !rs.next()) { - return null; - } - - Certificate c = new Certificate(rs); - cache.put(c); - return c; - } - } catch (IllegalArgumentException e) { - - } - return null; - } - - public static String escapeAVA(String value) { - - return value.replace("\\", "\\\\").replace("/", "\\/"); - } - - public static String stringifyDN(HashMap contents) { - StringBuffer res = new StringBuffer(); - for (Entry i : contents.entrySet()) { - res.append("/" + i.getKey() + "="); - res.append(escapeAVA(i.getValue())); - } - return res.toString(); - } - - public static HashMap buildDN(String... contents) { - HashMap res = new HashMap<>(); - for (int i = 0; i + 1 < contents.length; i += 2) { - res.put(contents[i], contents[i + 1]); - } - return res; - } - - public java.util.Date getRevocationDate() { - if (getStatus() == CertificateStatus.REVOKED) { - try (GigiPreparedStatement prep = new GigiPreparedStatement("SELECT revoked FROM certs WHERE id=?")) { - prep.setInt(1, getId()); - GigiResultSet res = prep.executeQuery(); - if (res.next()) { - return new java.util.Date(res.getTimestamp("revoked").getTime()); - } - } - } - return null; - } - - public void setLoginEnabled(boolean activate) { - if (activate) { - if ( !isLoginEnabled()) { - try (GigiPreparedStatement prep = new GigiPreparedStatement("INSERT INTO `logincerts` SET `id`=?")) { - prep.setInt(1, id); - prep.execute(); - } - } - } else { - try (GigiPreparedStatement prep = new GigiPreparedStatement("DELETE FROM `logincerts` WHERE `id`=?")) { - prep.setInt(1, id); - prep.execute(); - } - } - } - - public boolean isLoginEnabled() { - try (GigiPreparedStatement prep = new GigiPreparedStatement("SELECT 1 FROM `logincerts` WHERE `id`=?")) { - prep.setInt(1, id); - GigiResultSet res = prep.executeQuery(); - return res.next(); - } - } - - public static Certificate[] findBySerialPattern(String serial) { - try (GigiPreparedStatement prep = new GigiPreparedStatement("SELECT `id` FROM `certs` WHERE `serial` LIKE ? GROUP BY `id` LIMIT 100", true)) { - prep.setString(1, serial); - return fetchCertsToArray(prep); - } - } - - public static Certificate[] findBySANPattern(String request, SANType type) { - try (GigiPreparedStatement prep = new GigiPreparedStatement("SELECT `certId` FROM `subjectAlternativeNames` WHERE `contents` LIKE ? and `type`=?::`SANType` GROUP BY `certId` LIMIT 100", true)) { - prep.setString(1, request); - prep.setEnum(2, type); - return fetchCertsToArray(prep); - } - } - - private static Certificate[] fetchCertsToArray(GigiPreparedStatement prep) { - GigiResultSet res = prep.executeQuery(); - res.last(); - Certificate[] certs = new Certificate[res.getRow()]; - res.beforeFirst(); - for (int i = 0; res.next(); i++) { - certs[i] = Certificate.getById(res.getInt(1)); - } - return certs; - } -}