From 238ebc23e2720c7088e01d7da2d70d01b7b92d19 Mon Sep 17 00:00:00 2001
From: zzz <zzz@mail.i2p>
Date: Fri, 19 Feb 2016 01:37:41 +0000
Subject: [PATCH] Crypto: Check for revocation when reading in certificates

---
 core/java/src/net/i2p/crypto/CertUtil.java    | 35 ++++++++++++++++++-
 core/java/src/net/i2p/crypto/DirKeyRing.java  |  5 +++
 .../java/src/net/i2p/crypto/KeyStoreUtil.java | 26 +++++++++++++-
 3 files changed, 64 insertions(+), 2 deletions(-)

diff --git a/core/java/src/net/i2p/crypto/CertUtil.java b/core/java/src/net/i2p/crypto/CertUtil.java
index 3bee6d2d3a..ae6667c8be 100644
--- a/core/java/src/net/i2p/crypto/CertUtil.java
+++ b/core/java/src/net/i2p/crypto/CertUtil.java
@@ -226,17 +226,24 @@ public final class CertUtil {
      *  Get the Java public key from a X.509 certificate file.
      *  Throws if the certificate is invalid (e.g. expired).
      *
+     *  This DOES check for revocation.
+     *
      *  @return non-null, throws on all errors including certificate invalid
      *  @since 0.9.24 moved from SU3File private method
      */
     public static PublicKey loadKey(File kd) throws IOException, GeneralSecurityException {
-        return loadCert(kd).getPublicKey();
+        X509Certificate cert = loadCert(kd);
+        if (isRevoked(cert))
+            throw new CRLException("Certificate is revoked");
+        return cert.getPublicKey();
     }
 
     /**
      *  Get the certificate from a X.509 certificate file.
      *  Throws if the certificate is invalid (e.g. expired).
      *
+     *  This does NOT check for revocation.
+     *
      *  @return non-null, throws on all errors including certificate invalid
      *  @since 0.9.24 adapted from SU3File private method
      */
@@ -314,6 +321,8 @@ public final class CertUtil {
      *  Throws if any certificate is invalid (e.g. expired).
      *  Does NOT close the stream.
      *
+     *  This does NOT check for revocation.
+     *
      *  @return non-null, non-empty, throws on all errors including certificate invalid
      *  @since 0.9.25
      */
@@ -380,6 +389,19 @@ public final class CertUtil {
 
     /**
      *  Is the certificate revoked?
+     *  This loads the CRLs from disk.
+     *  For efficiency, call loadCRLs() and then pass to isRevoked().
+     *
+     *  @since 0.9.25
+     */
+    public static boolean isRevoked(Certificate cert) {
+        return isRevoked(I2PAppContext.getGlobalContext(), cert);
+    }
+
+    /**
+     *  Is the certificate revoked?
+     *  This loads the CRLs from disk.
+     *  For efficiency, call loadCRLs() and then pass to isRevoked().
      *
      *  @since 0.9.25
      */
@@ -403,6 +425,16 @@ public final class CertUtil {
         return false;
     }
 
+    /**
+     *  Load CRLs from standard locations.
+     *
+     *  @return non-null, possibly empty
+     *  @since 0.9.25
+     */
+    public static CertStore loadCRLs() {
+        return loadCRLs(I2PAppContext.getGlobalContext());
+    }
+
     /**
      *  Load CRLs from standard locations.
      *
@@ -423,6 +455,7 @@ public final class CertUtil {
             dir2 = new File(dir2, REVOCATION_DIR);
             loadCRLs(crls, dir2);
         }
+        //System.out.println("Loaded " + crls.size() + " CRLs");
         CollectionCertStoreParameters ccsp = new CollectionCertStoreParameters(crls);
         try {
             CertStore store = CertStore.getInstance("Collection", ccsp);
diff --git a/core/java/src/net/i2p/crypto/DirKeyRing.java b/core/java/src/net/i2p/crypto/DirKeyRing.java
index 899f41847d..4728379fbb 100644
--- a/core/java/src/net/i2p/crypto/DirKeyRing.java
+++ b/core/java/src/net/i2p/crypto/DirKeyRing.java
@@ -12,6 +12,7 @@ import java.io.IOException;
 import java.security.GeneralSecurityException;
 import java.security.PublicKey;
 import java.security.cert.CertificateFactory;
+import java.security.cert.CRLException;
 import java.security.cert.X509Certificate;
 
 import net.i2p.util.SystemVersion;
@@ -34,6 +35,8 @@ class DirKeyRing implements KeyRing {
      *  Cert must be in the file (escaped keyName).crt,
      *  and have a CN == keyName.
      *
+     *  This DOES do a revocation check.
+     *
      *  CN check unsupported on Android.
      *
      *  @return null if file doesn't exist, throws on all other errors
@@ -50,6 +53,8 @@ class DirKeyRing implements KeyRing {
         if (!kd.exists())
             return null;
         X509Certificate cert = CertUtil.loadCert(kd);
+        if (CertUtil.isRevoked(cert))
+            throw new CRLException("Certificate is revoked");
         if (!SystemVersion.isAndroid()) {
             // getSubjectValue() unsupported on Android.
             // Any cert problems will be caught in non-Android testing.
diff --git a/core/java/src/net/i2p/crypto/KeyStoreUtil.java b/core/java/src/net/i2p/crypto/KeyStoreUtil.java
index 68fcc50c7d..91999d6648 100644
--- a/core/java/src/net/i2p/crypto/KeyStoreUtil.java
+++ b/core/java/src/net/i2p/crypto/KeyStoreUtil.java
@@ -13,6 +13,7 @@ import java.security.PublicKey;
 import java.security.cert.Certificate;
 import java.security.cert.CertificateExpiredException;
 import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.CertStore;
 import java.security.cert.X509Certificate;
 import java.security.cert.X509CRL;
 import java.util.ArrayList;
@@ -332,6 +333,8 @@ public final class KeyStoreUtil {
      *  Load all X509 Certs from a directory and add them to the
      *  trusted set of certificates in the key store
      *
+     *  This DOES check for revocation.
+     *
      *  @return number successfully added
      *  @since 0.8.2, moved from SSLEepGet in 0.9.9
      */
@@ -341,6 +344,7 @@ public final class KeyStoreUtil {
         if (dir.exists() && dir.isDirectory()) {
             File[] files = dir.listFiles();
             if (files != null) {
+                CertStore cs = CertUtil.loadCRLs();
                 for (int i = 0; i < files.length; i++) {
                     File f = files[i];
                     if (!f.isFile())
@@ -354,7 +358,7 @@ public final class KeyStoreUtil {
                         alias.endsWith(".p7c") || alias.endsWith(".pfx") || alias.endsWith(".p12") ||
                         alias.endsWith(".cer"))
                         alias = alias.substring(0, alias.length() - 4);
-                    boolean success = addCert(f, alias, ks);
+                    boolean success = addCert(f, alias, ks, cs);
                     if (success)
                         added++;
                 }
@@ -367,10 +371,26 @@ public final class KeyStoreUtil {
      *  Load an X509 Cert from a file and add it to the
      *  trusted set of certificates in the key store
      *
+     *  This does NOT check for revocation.
+     *
      *  @return success
      *  @since 0.8.2, moved from SSLEepGet in 0.9.9
      */
     public static boolean addCert(File file, String alias, KeyStore ks) {
+        return addCert(file, alias, ks, null);
+    }
+
+    /**
+     *  Load an X509 Cert from a file and add it to the
+     *  trusted set of certificates in the key store
+     *
+     *  This DOES check for revocation, IF cs is non-null.
+     *
+     *  @param cs may be null; if non-null, check for revocation
+     *  @return success
+     *  @since 0.9.25
+     */
+    public static boolean addCert(File file, String alias, KeyStore ks, CertStore cs) {
         try {
             X509Certificate cert = CertUtil.loadCert(file);
             info("Read X509 Certificate from " + file.getAbsolutePath() +
@@ -378,6 +398,10 @@ public final class KeyStoreUtil {
                           " Serial: " + cert.getSerialNumber().toString(16) +
                           "; Valid From: " + cert.getNotBefore() +
                           " To: " + cert.getNotAfter());
+            if (cs != null && CertUtil.isRevoked(cs, cert)) {
+                error("Certificate is revoked: " + file, new Exception());
+                return false;
+            }
             ks.setCertificateEntry(alias, cert);
             info("Now trusting X509 Certificate, Issuer: " + cert.getIssuerX500Principal());
         } catch (CertificateExpiredException cee) {
-- 
GitLab