package io.vertx.ext.auth.impl;
import io.vertx.core.impl.logging.Logger;
import io.vertx.core.impl.logging.LoggerFactory;
import javax.naming.InvalidNameException;
import javax.naming.ldap.LdapName;
import javax.security.auth.x500.X500Principal;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SignatureException;
import java.security.cert.CertificateException;
import java.security.cert.X509CRL;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public final class CertificateHelper {
private static final Logger LOG = LoggerFactory.getLogger(CertificateHelper.class);
public final static class CertInfo {
private final Map<String, String> subject;
private final int version;
private final int basicConstraintsCA;
private CertInfo(Map<String, String> subject, int version, int basicConstraintsCA) {
this.subject = subject;
this.version = version;
this.basicConstraintsCA = basicConstraintsCA;
}
public boolean subjectHas(String key) {
if (subject != null) {
return subject.containsKey(key);
}
return false;
}
public String subject(String key) {
if (subject != null) {
return subject.get(key);
}
return null;
}
public int version() {
return version;
}
public int basicConstraintsCA() {
return basicConstraintsCA;
}
public boolean isEmpty() {
if (subject != null) {
return subject.isEmpty();
} else {
return true;
}
}
}
private CertificateHelper() {
}
public static void checkValidity(List<X509Certificate> certificates, List<X509CRL> crls) throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, SignatureException, NoSuchProviderException {
checkValidity(certificates, true, crls);
}
public static void checkValidity(List<X509Certificate> certificates, boolean withRootCA, List<X509CRL> crls) throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, SignatureException, NoSuchProviderException {
if (certificates == null || certificates.size() == 0) {
throw new CertificateException("empty chain");
}
final long now = System.currentTimeMillis();
for (int i = 0; i < certificates.size(); i++) {
final X509Certificate subjectCert = certificates.get(i);
subjectCert.checkValidity();
if (crls != null) {
for (X509CRL crl : crls) {
if (crl.getNextUpdate().getTime() < now) {
LOG.warn("CRL is out of date nextUpdate < now");
}
if (crl.isRevoked(subjectCert)) {
throw new CertificateException("Certificate is revoked");
}
}
}
if (certificates.size() == 1) {
return;
}
final X509Certificate issuerCert;
if (i + 1 < certificates.size()) {
issuerCert = certificates.get(i + 1);
if (!subjectCert.getIssuerX500Principal().equals(issuerCert.getSubjectX500Principal())) {
throw new CertificateException("Certificate path issuers dont match: [" + subjectCert.getIssuerX500Principal() + "] != [" + issuerCert.getSubjectX500Principal() + "]");
}
subjectCert.verify(issuerCert.getPublicKey());
}
}
if (withRootCA) {
X509Certificate root = certificates.get(certificates.size() - 1);
root.verify(root.getPublicKey());
}
}
public static CertInfo getCertInfo(X509Certificate cert) {
final String subject = cert.getSubjectX500Principal().getName(X500Principal.RFC2253);
Map<String, String> sub = null;
if (subject != null && !"".equals(subject)) {
try {
LdapName rfc2253 = new LdapName(subject);
sub = new HashMap<>();
for (int i = 0; i < rfc2253.size(); i++) {
String value = rfc2253.get(i);
int idx = value.indexOf('=');
if (idx != -1) {
sub.put(value.substring(0, idx), value.substring(idx + 1));
} else {
sub.put(value, null);
}
}
} catch (InvalidNameException e) {
}
}
return new CertInfo(sub, cert.getVersion(), cert.getBasicConstraints());
}
}