From 1f0cb48a6ba9c978881a0384ddad29024acae67a Mon Sep 17 00:00:00 2001 From: David Hook Date: Sun, 1 Sep 2024 09:23:48 +1000 Subject: [PATCH] added equals/hashCode for OCSP CertID,changed algID check in revocation checker - relates to github #1789 --- .../org/bouncycastle/asn1/ocsp/CertID.java | 70 ++++++++++++++++++- .../bouncycastle/asn1/test/CertIDTest.java | 54 ++++++++++++++ .../asn1/test/RegressionTest.java | 3 +- .../provider/ProvOcspRevocationChecker.java | 37 +++++++++- 4 files changed, 161 insertions(+), 3 deletions(-) create mode 100644 core/src/test/java/org/bouncycastle/asn1/test/CertIDTest.java diff --git a/core/src/main/java/org/bouncycastle/asn1/ocsp/CertID.java b/core/src/main/java/org/bouncycastle/asn1/ocsp/CertID.java index 001b6e361c..80d33f85d5 100644 --- a/core/src/main/java/org/bouncycastle/asn1/ocsp/CertID.java +++ b/core/src/main/java/org/bouncycastle/asn1/ocsp/CertID.java @@ -1,5 +1,6 @@ package org.bouncycastle.asn1.ocsp; +import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1EncodableVector; import org.bouncycastle.asn1.ASN1Integer; import org.bouncycastle.asn1.ASN1Object; @@ -7,6 +8,7 @@ import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.ASN1TaggedObject; +import org.bouncycastle.asn1.DERNull; import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; @@ -16,7 +18,7 @@ public class CertID AlgorithmIdentifier hashAlgorithm; ASN1OctetString issuerNameHash; ASN1OctetString issuerKeyHash; - ASN1Integer serialNumber; + ASN1Integer serialNumber; public CertID( AlgorithmIdentifier hashAlgorithm, @@ -81,6 +83,72 @@ public ASN1Integer getSerialNumber() return serialNumber; } + public boolean equals(Object o) + { + if (this == o) + { + return true; + } + + if (o instanceof ASN1Encodable) + { + try + { + CertID other = CertID.getInstance(o); + + if (!this.hashAlgorithm.getAlgorithm().equals(other.hashAlgorithm.getAlgorithm())) + { + return false; + } + if (!isEqual(this.hashAlgorithm.getParameters(), other.hashAlgorithm.getParameters())) + { + return false; + } + + return issuerNameHash.equals(other.issuerNameHash) + && issuerKeyHash.equals(other.issuerKeyHash) + && serialNumber.equals(other.serialNumber); + } + catch (Exception e) + { + return false; + } + } + + return false; + } + + public int hashCode() + { + ASN1Encodable params = hashAlgorithm.getParameters(); + int hashCode = (params == null || DERNull.INSTANCE.equals(params)) ? 0 : params.hashCode(); + + return hashCode + 7 * (hashAlgorithm.getAlgorithm().hashCode() + + 7 * (issuerNameHash.hashCode() + 7 * (issuerKeyHash.hashCode() + 7 * serialNumber.hashCode()))); + } + + private boolean isEqual(ASN1Encodable a, ASN1Encodable b) + { + if (a == b) + { + return true; + } + + if (a == null) + { + return DERNull.INSTANCE.equals(b); + } + else + { + if (DERNull.INSTANCE.equals(a) && b == null) + { + return true; + } + + return a.equals(b); + } + } + /** * Produce an object suitable for an ASN1OutputStream. *
diff --git a/core/src/test/java/org/bouncycastle/asn1/test/CertIDTest.java b/core/src/test/java/org/bouncycastle/asn1/test/CertIDTest.java
new file mode 100644
index 0000000000..e162f0e9ac
--- /dev/null
+++ b/core/src/test/java/org/bouncycastle/asn1/test/CertIDTest.java
@@ -0,0 +1,54 @@
+package org.bouncycastle.asn1.test;
+
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.ocsp.CertID;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.internal.asn1.oiw.OIWObjectIdentifiers;
+import org.bouncycastle.util.BigIntegers;
+import org.bouncycastle.util.Strings;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class CertIDTest
+    extends SimpleTest
+{
+    public String getName()
+    {
+        return "CertID";
+    }
+
+    public void performTest()
+        throws Exception
+    {
+        DEROctetString issuerAHash = new DEROctetString(Strings.toByteArray("IssuerAHash"));
+        DEROctetString issuerBHash = new DEROctetString(Strings.toByteArray("IssuerBHash"));
+        DEROctetString issuerAKeyHash = new DEROctetString(Strings.toByteArray("IssuerAKeyHash"));
+        DEROctetString issuerBKeyHash = new DEROctetString(Strings.toByteArray("IssuerBKeyHash"));
+
+        CertID a = new CertID(new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1), issuerAHash, issuerAKeyHash, new ASN1Integer(BigIntegers.ONE));
+        CertID b = new CertID(new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, DERNull.INSTANCE), issuerAHash, issuerAKeyHash, new ASN1Integer(BigIntegers.ONE));
+        CertID c = new CertID(new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, new DEROctetString(new byte[1])), issuerAHash, issuerAKeyHash, new ASN1Integer(BigIntegers.ONE));
+
+        isTrue(a.equals(a));
+        isTrue(a.equals(b));
+        isTrue(a.hashCode() == b.hashCode());
+        isTrue(!a.equals(c));
+        isTrue(a.hashCode() != c.hashCode());
+
+        b = new CertID(new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1), issuerAHash, issuerAKeyHash, new ASN1Integer(BigIntegers.TWO));
+        isTrue(!a.equals(b));
+        b = new CertID(new AlgorithmIdentifier(OIWObjectIdentifiers.elGamalAlgorithm), issuerAHash, issuerAKeyHash, new ASN1Integer(BigIntegers.ONE));
+        isTrue(!a.equals(b));
+        b = new CertID(new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1), issuerBHash, issuerAKeyHash, new ASN1Integer(BigIntegers.ONE));
+        isTrue(!a.equals(b));
+        b = new CertID(new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1), issuerAHash, issuerBKeyHash, new ASN1Integer(BigIntegers.ONE));
+        isTrue(!a.equals(b));
+    }
+
+    public static void main(
+        String[] args)
+    {
+        runTest(new CertIDTest());
+    }
+}
diff --git a/core/src/test/java/org/bouncycastle/asn1/test/RegressionTest.java b/core/src/test/java/org/bouncycastle/asn1/test/RegressionTest.java
index b42a20f200..0cdcfbf37a 100644
--- a/core/src/test/java/org/bouncycastle/asn1/test/RegressionTest.java
+++ b/core/src/test/java/org/bouncycastle/asn1/test/RegressionTest.java
@@ -55,7 +55,8 @@ public class RegressionTest
         new DLExternalTest(),
         new KMACParamsTest(),
         new DERPrivateTest(),
-        new X509AltTest()
+        new X509AltTest(),
+        new CertIDTest()
     };
 
     public static void main(String[] args)
diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/ProvOcspRevocationChecker.java b/prov/src/main/java/org/bouncycastle/jce/provider/ProvOcspRevocationChecker.java
index 1d697023f0..234e967276 100644
--- a/prov/src/main/java/org/bouncycastle/jce/provider/ProvOcspRevocationChecker.java
+++ b/prov/src/main/java/org/bouncycastle/jce/provider/ProvOcspRevocationChecker.java
@@ -279,7 +279,7 @@ public void check(Certificate certificate)
                                         {
                                             throw new ExtCertPathValidatorException("OCSP response expired");
                                         }
-                                        if (certID == null || !certID.getHashAlgorithm().equals(resp.getCertID().getHashAlgorithm()))
+                                        if (certID == null || !isEqualAlgId(certID.getHashAlgorithm(), resp.getCertID().getHashAlgorithm()))
                                         {
                                             org.bouncycastle.asn1.x509.Certificate issuer = extractCert();
 
@@ -340,6 +340,41 @@ public void check(Certificate certificate)
         }
     }
 
+    private static boolean isEqualAlgId(AlgorithmIdentifier a, AlgorithmIdentifier b)
+    {
+        if (a == b || a.equals(b))
+        {
+            return true;
+        }
+
+        if (a.getAlgorithm().equals(b.getAlgorithm()))
+        {
+            ASN1Encodable aParam = a.getParameters();
+            ASN1Encodable bParam = b.getParameters();
+
+            if (aParam == bParam)
+            {
+                return true;
+            }
+
+            if (aParam == null)
+            {
+                return DERNull.INSTANCE.equals(bParam);
+            }
+            else
+            {
+                if (DERNull.INSTANCE.equals(aParam) && bParam == null)
+                {
+                    return true;
+                }
+
+                return aParam.equals(bParam);
+            }
+        }
+
+        return false;
+    }
+
     static URI getOcspResponderURI(X509Certificate cert)
     {
         byte[] extValue = cert.getExtensionValue(org.bouncycastle.asn1.x509.Extension.authorityInfoAccess.getId());