diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/Certificate.java b/core/src/main/java/org/bouncycastle/asn1/x509/Certificate.java index e64a197572..2f85f2cf0a 100644 --- a/core/src/main/java/org/bouncycastle/asn1/x509/Certificate.java +++ b/core/src/main/java/org/bouncycastle/asn1/x509/Certificate.java @@ -7,6 +7,7 @@ import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.ASN1TaggedObject; import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.util.IllegalArgumentWarningException; /** * an X509Certificate structure. @@ -53,12 +54,19 @@ private Certificate( { this.seq = seq; + IllegalArgumentWarningException exception = null; + // // correct x509 certficate // if (seq.size() == 3) { - tbsCert = TBSCertificate.getInstance(seq.getObjectAt(0)); + try { + tbsCert = TBSCertificate.getInstance(seq.getObjectAt(0)); + } catch (IllegalArgumentWarningException ex) { + tbsCert = (TBSCertificate) ex.getObject(TBSCertificate.class); + exception = ex; + } sigAlgId = AlgorithmIdentifier.getInstance(seq.getObjectAt(1)); sig = ASN1BitString.getInstance(seq.getObjectAt(2)); @@ -67,6 +75,10 @@ private Certificate( { throw new IllegalArgumentException("sequence wrong size for a certificate"); } + + if (exception != null) { + throw new IllegalArgumentWarningException(this, exception); + } } public TBSCertificate getTBSCertificate() @@ -124,6 +136,7 @@ public ASN1BitString getSignature() return sig; } + @Override public ASN1Primitive toASN1Primitive() { return seq; diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/Extensions.java b/core/src/main/java/org/bouncycastle/asn1/x509/Extensions.java index d25187aac5..e015d475e8 100644 --- a/core/src/main/java/org/bouncycastle/asn1/x509/Extensions.java +++ b/core/src/main/java/org/bouncycastle/asn1/x509/Extensions.java @@ -12,6 +12,7 @@ import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.ASN1TaggedObject; import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.util.IllegalArgumentWarningException; import org.bouncycastle.util.Properties; /** @@ -77,6 +78,7 @@ private Extensions( } Enumeration e = seq.getObjects(); + String error = null; while (e.hasMoreElements()) { @@ -86,13 +88,18 @@ private Extensions( { if (!Properties.isOverrideSet("org.bouncycastle.x509.ignore_repeated_extensions")) { - throw new IllegalArgumentException("repeated extension found: " + ext.getExtnId()); + error = "repeated extension found: " + ext.getExtnId(); + continue; } } extensions.put(ext.getExtnId(), ext); ordering.addElement(ext.getExtnId()); } + + if (error != null) { + throw new IllegalArgumentWarningException(error, this); + } } /** @@ -177,6 +184,7 @@ public ASN1Encodable getExtensionParsedValue(ASN1ObjectIdentifier oid) * extnValue OCTET STRING } * */ + @Override public ASN1Primitive toASN1Primitive() { ASN1EncodableVector vec = new ASN1EncodableVector(ordering.size()); diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/TBSCertificate.java b/core/src/main/java/org/bouncycastle/asn1/x509/TBSCertificate.java index 76763291b6..c97da4240e 100644 --- a/core/src/main/java/org/bouncycastle/asn1/x509/TBSCertificate.java +++ b/core/src/main/java/org/bouncycastle/asn1/x509/TBSCertificate.java @@ -10,8 +10,13 @@ import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.asn1.DERTaggedObject; import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.util.IllegalArgumentWarningException; import org.bouncycastle.util.Properties; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + /** * The TBSCertificate object. *
@@ -47,6 +52,7 @@ public class TBSCertificate
ASN1BitString issuerUniqueId;
ASN1BitString subjectUniqueId;
Extensions extensions;
+ List errors;
public static TBSCertificate getInstance(
ASN1TaggedObject obj,
@@ -92,7 +98,7 @@ private TBSCertificate(
boolean isV1 = false;
boolean isV2 = false;
-
+
if (version.hasValue(0))
{
isV1 = true;
@@ -103,7 +109,8 @@ else if (version.hasValue(1))
}
else if (!version.hasValue(2))
{
- throw new IllegalArgumentException("version number not recognised");
+ addError(
+ String.format("Certificate version number value %d not 0, 1 or 2", version.getValue()));
}
serialNumber = ASN1Integer.getInstance(seq.getObjectAt(seqStart + 1));
@@ -129,9 +136,10 @@ else if (!version.hasValue(2))
int extras = seq.size() - (seqStart + 6) - 1;
if (extras != 0 && isV1)
{
- throw new IllegalArgumentException("version 1 certificate contains extra data");
+ addError("version 1 certificate contains extra data");
+ extras = 0; // Ignore the extra data
}
-
+
while (extras > 0)
{
ASN1TaggedObject extra = (ASN1TaggedObject)seq.getObjectAt(seqStart + 6 + extras);
@@ -147,15 +155,43 @@ else if (!version.hasValue(2))
case 3:
if (isV2)
{
- throw new IllegalArgumentException("version 2 certificate cannot contain extensions");
+ addError("version 2 certificate cannot contain extensions");
+ throw new IllegalArgumentWarningException(errors, this);
+ }
+ try {
+ extensions = Extensions.getInstance(ASN1Sequence.getInstance(extra, true));
+ } catch (IllegalArgumentWarningException ex) {
+ extensions = ex.getObject(Extensions.class);
+ addErrors(ex.getMessages());
}
- extensions = Extensions.getInstance(ASN1Sequence.getInstance(extra, true));
break;
default:
- throw new IllegalArgumentException("Unknown tag encountered in structure: " + extra.getTagNo());
+ addError("Unknown tag encountered in structure: " + extra.getTagNo());
+ throw new IllegalArgumentWarningException(errors, this);
}
extras--;
}
+
+ if (errors != null) {
+ throw new IllegalArgumentWarningException(errors, this);
+ }
+ }
+
+ private void addError(String error) {
+ if (errors == null) {
+ errors = new ArrayList<>();
+ }
+ errors.add(error);
+ }
+
+ private void addErrors(List errors) {
+ for (String error : errors) {
+ addError(error);
+ }
+ }
+
+ public Collection getErrors() {
+ return errors;
}
public int getVersionNumber()
@@ -218,6 +254,7 @@ public Extensions getExtensions()
return extensions;
}
+ @Override
public ASN1Primitive toASN1Primitive()
{
if (Properties.getPropertyValue("org.bouncycastle.x509.allow_non-der_tbscert") != null)
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/RSAKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/RSAKeyParameters.java
index 65b0b84ee4..1e2a3e89c8 100644
--- a/core/src/main/java/org/bouncycastle/crypto/params/RSAKeyParameters.java
+++ b/core/src/main/java/org/bouncycastle/crypto/params/RSAKeyParameters.java
@@ -1,10 +1,13 @@
package org.bouncycastle.crypto.params;
import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
import org.bouncycastle.crypto.CryptoServicesRegistrar;
import org.bouncycastle.math.Primes;
import org.bouncycastle.util.BigIntegers;
+import org.bouncycastle.util.IllegalArgumentWarningException;
import org.bouncycastle.util.Properties;
public class RSAKeyParameters
@@ -41,16 +44,20 @@ public RSAKeyParameters(
{
super(isPrivate);
+ this.modulus = modulus;
+ this.exponent = exponent;
+
if (!isPrivate)
{
if ((exponent.intValue() & 1) == 0)
{
- throw new IllegalArgumentException("RSA publicExponent is even");
+ throw new IllegalArgumentWarningException("RSA publicExponent is even", this);
}
}
- this.modulus = validated.contains(modulus) ? modulus : validate(modulus, isInternal);
- this.exponent = exponent;
+ if (!validated.contains(modulus)) {
+ validate(modulus, isInternal);
+ }
}
private BigInteger validate(BigInteger modulus, boolean isInternal)
@@ -62,44 +69,48 @@ private BigInteger validate(BigInteger modulus, boolean isInternal)
return modulus;
}
+ List issues = new ArrayList<>();
+
if ((modulus.intValue() & 1) == 0)
{
- throw new IllegalArgumentException("RSA modulus is even");
+ issues.add("RSA modulus is even");
}
// If you need to set this you need to have a serious word to whoever is generating
// your keys.
- if (Properties.isOverrideSet("org.bouncycastle.rsa.allow_unsafe_mod"))
+ if (!Properties.isOverrideSet("org.bouncycastle.rsa.allow_unsafe_mod"))
{
- return modulus;
- }
-
- int maxBitLength = Properties.asInteger("org.bouncycastle.rsa.max_size", 15360);
+ int maxBitLength = Properties.asInteger("org.bouncycastle.rsa.max_size", 15360);
- int modBitLength = modulus.bitLength();
- if (maxBitLength < modBitLength)
- {
- throw new IllegalArgumentException("modulus value out of range");
- }
+ int modBitLength = modulus.bitLength();
+ if (maxBitLength < modBitLength)
+ {
+ issues.add("modulus value out of range");
+ }
- if (!modulus.gcd(SMALL_PRIMES_PRODUCT).equals(ONE))
- {
- throw new IllegalArgumentException("RSA modulus has a small prime factor");
- }
+ if (!modulus.gcd(SMALL_PRIMES_PRODUCT).equals(ONE))
+ {
+ issues.add("RSA modulus has a small prime factor");
+ }
- int bits = modulus.bitLength() / 2;
- int iterations = Properties.asInteger("org.bouncycastle.rsa.max_mr_tests", getMRIterations(bits));
+ int bits = modulus.bitLength() / 2;
+ int iterations = Properties.asInteger("org.bouncycastle.rsa.max_mr_tests", getMRIterations(bits));
- if (iterations > 0)
- {
- Primes.MROutput mr = Primes.enhancedMRProbablePrimeTest(modulus, CryptoServicesRegistrar.getSecureRandom(), iterations);
- if (!mr.isProvablyComposite())
+ if (iterations > 0)
{
- throw new IllegalArgumentException("RSA modulus is not composite");
+ Primes.MROutput mr = Primes.enhancedMRProbablePrimeTest(modulus, CryptoServicesRegistrar.getSecureRandom(), iterations);
+ if (!mr.isProvablyComposite())
+ {
+ issues.add("RSA modulus is not composite");
+ }
+ }
+
+ if (!issues.isEmpty()) {
+ throw new IllegalArgumentWarningException(issues, this);
}
- }
- validated.add(modulus);
+ validated.add(modulus);
+ }
return modulus;
}
diff --git a/core/src/main/java/org/bouncycastle/util/IllegalArgumentWarningException.java b/core/src/main/java/org/bouncycastle/util/IllegalArgumentWarningException.java
new file mode 100644
index 0000000000..521a5efce0
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/util/IllegalArgumentWarningException.java
@@ -0,0 +1,79 @@
+package org.bouncycastle.util;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Exception which is thrown when parsing a certificate when errors in the
+ * parsing are detected. This provides a list of the errors that were
+ * detected along with the (mostly) parsed object.
+ */
+public class IllegalArgumentWarningException extends IllegalArgumentException {
+
+ private static final long serialVersionUID = 5735291408274180892L;
+ List messages;
+ Object object;
+
+ /**
+ * Basic constructor.
+ *
+ * @param messages Non empty list of messages to associate with this Exception.
+ * @param object The partially parsed object.
+ * @param cause The underlying exception (if any).
+ */
+ public IllegalArgumentWarningException(List messages, Object object, Throwable cause) {
+ super(messages.get(0), cause);
+ this.messages = messages;
+ this.object = object;
+ }
+
+ /**
+ * Basic constructor.
+ *
+ * @param messages Non empty list of messages to associate with this Exception.
+ * @param object The partially parsed object.
+ */
+ public IllegalArgumentWarningException(List messages, Object object) {
+ this(messages, object, null);
+ }
+
+ /**
+ * Basic constructor.
+ *
+ * @param message Single message to be associated with this Exception.
+ * @param object The partially parsed object.
+ */
+ public IllegalArgumentWarningException(String message, Object object) {
+ this(Collections.singletonList(message), object, null);
+ }
+
+ /**
+ * Basic constructor.
+ *
+ * @param object The partially parsed object.
+ * @param cause The underlying exception.
+ */
+ public IllegalArgumentWarningException(Object object, Throwable cause) {
+ this(Collections.singletonList(cause.getMessage()), object, cause);
+ }
+
+ /**
+ * Gets the list of error messages.
+ */
+ public List getMessages() {
+ return messages;
+ }
+
+ /**
+ * This gets the partially parsed object -- but only if you provide the correct
+ * class!
+ *
+ * @param clazz The class of the object that you are expecting.
+ */
+ public T getObject(Class clazz) {
+ if (clazz.isInstance(object)) {
+ return (T) object;
+ }
+ throw new IllegalArgumentException(messages.get(0), this);
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/util/test/SimpleTest.java b/core/src/main/java/org/bouncycastle/util/test/SimpleTest.java
index 66ce33bd79..c429cb29cc 100644
--- a/core/src/main/java/org/bouncycastle/util/test/SimpleTest.java
+++ b/core/src/main/java/org/bouncycastle/util/test/SimpleTest.java
@@ -47,7 +47,7 @@ protected void isEquals(
{
if (!a.equals(b))
{
- throw new TestFailedException(SimpleTestResult.failed(this, "no message"));
+ throw new TestFailedException(SimpleTestResult.failed(this, String.format("isEqual fail: %s != %s", a, b)));
}
}
@@ -57,7 +57,7 @@ protected void isEquals(
{
if (a != b)
{
- throw new TestFailedException(SimpleTestResult.failed(this, "no message"));
+ throw new TestFailedException(SimpleTestResult.failed(this, String.format("isEqual fail: %s != %s", a, b)));
}
}
diff --git a/prov/src/test/java/org/bouncycastle/jce/provider/test/CertPathValidatorTest.java b/prov/src/test/java/org/bouncycastle/jce/provider/test/CertPathValidatorTest.java
index 1989c5f2e3..2e3dc17da4 100644
--- a/prov/src/test/java/org/bouncycastle/jce/provider/test/CertPathValidatorTest.java
+++ b/prov/src/test/java/org/bouncycastle/jce/provider/test/CertPathValidatorTest.java
@@ -582,7 +582,7 @@ private void checkInvalidCertPath()
{
checkInvalidPath(extInvTrust, extInvCA, extInvEE, "version 1 certificate contains extra data");
checkInvalidPath(extInvV2Trust, extInvV2CA, extInvV2EE, "version 2 certificate cannot contain extensions");
- checkInvalidPath(extInvVersionTrust, extInvVersionCA, extInvVersionEE, "version number not recognised");
+ checkInvalidPath(extInvVersionTrust, extInvVersionCA, extInvVersionEE, "Certificate version number value 5 not 0, 1 or 2");
checkInvalidPath(extInvExtTrust, extInvExtCA, extInvExtEE, "repeated extension found: 2.5.29.15");
}
@@ -621,7 +621,7 @@ private void checkInvalidPath(byte[] root, byte[] inter, byte[] ee, String expec
}
catch (CertPathValidatorException e)
{
- isTrue(e.getMessage().equals(expected));
+ isEquals(e.getMessage(), expected);
}
// check that our cert factory also rejects - the EE is always the invalid one
@@ -633,7 +633,7 @@ private void checkInvalidPath(byte[] root, byte[] inter, byte[] ee, String expec
}
catch (CertificateException e)
{
- isTrue(e.getMessage().equals("parsing issue: " + expected));
+ isEquals(e.getMessage(), "parsing issue: " + expected);
}
}