summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKenny Root <kroot@google.com>2012-12-26 08:44:32 -0800
committerKenny Root <kroot@google.com>2013-01-01 21:50:10 -0800
commit9ca3d0733e7f93c140fdc693ffb0aaaa21de7a19 (patch)
tree9b6fe024be737bd535136301c8dc897ee3c86ce3
parentf0f80b7be6fac9482d06126d5b06357d35fc431d (diff)
downloadlibcore-9ca3d0733e7f93c140fdc693ffb0aaaa21de7a19.zip
libcore-9ca3d0733e7f93c140fdc693ffb0aaaa21de7a19.tar.gz
libcore-9ca3d0733e7f93c140fdc693ffb0aaaa21de7a19.tar.bz2
Add X509CertificateTest in libcore tests
Change-Id: I678a8b89d0514c270ce0ee57057cfaadf3b47543
-rw-r--r--luni/src/test/java/libcore/java/security/cert/X509CertificateTest.java1081
-rw-r--r--support/src/test/java/tests/resources/x509/cert-alt-dirname.derbin0 -> 673 bytes
-rw-r--r--support/src/test/java/tests/resources/x509/cert-alt-dns.derbin0 -> 609 bytes
-rw-r--r--support/src/test/java/tests/resources/x509/cert-alt-email.derbin0 -> 609 bytes
-rw-r--r--support/src/test/java/tests/resources/x509/cert-alt-other.derbin0 -> 607 bytes
-rw-r--r--support/src/test/java/tests/resources/x509/cert-alt-rid.derbin0 -> 596 bytes
-rw-r--r--support/src/test/java/tests/resources/x509/cert-alt-uri.derbin0 -> 630 bytes
-rw-r--r--support/src/test/java/tests/resources/x509/cert-ca.derbin0 -> 594 bytes
-rw-r--r--support/src/test/java/tests/resources/x509/cert-caWithPathLen.derbin0 -> 597 bytes
-rw-r--r--support/src/test/java/tests/resources/x509/cert-dsa.derbin0 -> 793 bytes
-rw-r--r--support/src/test/java/tests/resources/x509/cert-ec.derbin0 -> 483 bytes
-rw-r--r--support/src/test/java/tests/resources/x509/cert-extendedKeyUsage.derbin0 -> 596 bytes
-rw-r--r--support/src/test/java/tests/resources/x509/cert-invalidip.derbin0 -> 594 bytes
-rw-r--r--support/src/test/java/tests/resources/x509/cert-ipv6.derbin0 -> 638 bytes
-rw-r--r--support/src/test/java/tests/resources/x509/cert-keyUsage-extraLong.derbin0 -> 594 bytes
-rw-r--r--support/src/test/java/tests/resources/x509/cert-rsa-dates.txt2
-rw-r--r--support/src/test/java/tests/resources/x509/cert-rsa-pubkey.derbin0 -> 162 bytes
-rw-r--r--support/src/test/java/tests/resources/x509/cert-rsa-serial.txt1
-rw-r--r--support/src/test/java/tests/resources/x509/cert-rsa-sig.der2
-rw-r--r--support/src/test/java/tests/resources/x509/cert-rsa-tbs.derbin0 -> 1065 bytes
-rw-r--r--support/src/test/java/tests/resources/x509/cert-rsa.derbin0 -> 1216 bytes
-rw-r--r--support/src/test/java/tests/resources/x509/cert-unsupported.derbin0 -> 603 bytes
-rw-r--r--support/src/test/java/tests/resources/x509/cert-userWithPathLen.derbin0 -> 594 bytes
-rwxr-xr-xsupport/src/test/java/tests/resources/x509/create.sh72
-rw-r--r--support/src/test/java/tests/resources/x509/default.cnf292
25 files changed, 1450 insertions, 0 deletions
diff --git a/luni/src/test/java/libcore/java/security/cert/X509CertificateTest.java b/luni/src/test/java/libcore/java/security/cert/X509CertificateTest.java
new file mode 100644
index 0000000..e86036b
--- /dev/null
+++ b/luni/src/test/java/libcore/java/security/cert/X509CertificateTest.java
@@ -0,0 +1,1081 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.java.security.cert;
+
+import tests.support.resource.Support_Resources;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.math.BigInteger;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.Principal;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.Security;
+import java.security.SignatureException;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.CertificateParsingException;
+import java.security.cert.X509Certificate;
+import java.security.spec.X509EncodedKeySpec;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+
+import javax.security.auth.x500.X500Principal;
+
+import junit.framework.TestCase;
+import libcore.java.security.StandardNames;
+
+public class X509CertificateTest extends TestCase {
+ private Provider[] mX509Providers;
+
+ private static final String CERT_RSA = "x509/cert-rsa.der";
+
+ private static final String CERT_DSA = "x509/cert-dsa.der";
+
+ private static final String CERT_EC = "x509/cert-ec.der";
+
+ private static final String CERT_KEYUSAGE_EXTRALONG = "x509/cert-keyUsage-extraLong.der";
+
+ private static final String CERT_EXTENDEDKEYUSAGE = "x509/cert-extendedKeyUsage.der";
+
+ private final static String CERT_RSA_TBS = "x509/cert-rsa-tbs.der";
+
+ private final static String CERT_RSA_SIGNATURE = "x509/cert-rsa-sig.der";
+
+ private static final String CERT_USERWITHPATHLEN = "x509/cert-userWithPathLen.der";
+
+ private static final String CERT_CA = "x509/cert-ca.der";
+
+ private static final String CERT_CAWITHPATHLEN = "x509/cert-caWithPathLen.der";
+
+ private static final String CERT_INVALIDIP = "x509/cert-invalidip.der";
+
+ private static final String CERT_IPV6 = "x509/cert-ipv6.der";
+
+ private static final String CERT_ALT_OTHER = "x509/cert-alt-other.der";
+
+ private static final String CERT_ALT_EMAIL = "x509/cert-alt-email.der";
+
+ private static final String CERT_ALT_DNS = "x509/cert-alt-dns.der";
+
+ private static final String CERT_ALT_DIRNAME = "x509/cert-alt-dirname.der";
+
+ private static final String CERT_ALT_URI = "x509/cert-alt-uri.der";
+
+ private static final String CERT_ALT_RID = "x509/cert-alt-rid.der";
+
+ private static final String CERT_UNSUPPORTED = "x509/cert-unsupported.der";
+
+ private final X509Certificate getCertificate(CertificateFactory f, String name)
+ throws Exception {
+ final InputStream is = Support_Resources.getStream(name);
+ assertNotNull("File does not exist: " + name, is);
+ try {
+ return (X509Certificate) f.generateCertificate(is);
+ } finally {
+ try {
+ is.close();
+ } catch (IOException ignored) {
+ }
+ }
+ }
+
+ private PublicKey getRsaCertificatePublicKey() throws Exception {
+ final InputStream ris = Support_Resources.getStream("x509/cert-rsa-pubkey.der");
+ try {
+ final int size = ris.available();
+ final DataInputStream is = new DataInputStream(ris);
+ final byte[] keyBytes = new byte[size];
+ is.readFully(keyBytes);
+
+ final KeyFactory kf = KeyFactory.getInstance("RSA");
+ return kf.generatePublic(new X509EncodedKeySpec(keyBytes));
+ } finally {
+ try {
+ ris.close();
+ } catch (IOException ignored) {
+ }
+ }
+ }
+
+ private Date[] getRsaCertificateDates() throws Exception {
+ final InputStream ris = Support_Resources.getStream("x509/cert-rsa-dates.txt");
+ try {
+ // notBefore=Dec 26 00:19:14 2012 GMT
+ final SimpleDateFormat sdf = new SimpleDateFormat("MMM dd HH:mm:ss yyyy zzz");
+
+ final BufferedReader buf = new BufferedReader(new InputStreamReader(ris));
+ String line = buf.readLine();
+ int index = line.indexOf('=');
+ assertEquals("notBefore", line.substring(0, index));
+ final Date startDate = sdf.parse(line.substring(index + 1));
+
+ line = buf.readLine();
+ index = line.indexOf('=');
+ assertEquals("notAfter", line.substring(0, index));
+ final Date endDate = sdf.parse(line.substring(index + 1));
+
+ assertTrue(startDate.before(endDate));
+ assertTrue(endDate.after(startDate));
+
+ return new Date[] { startDate, endDate };
+ } finally {
+ try {
+ ris.close();
+ } catch (IOException ignored) {
+ }
+ }
+ }
+
+ private BigInteger getRsaCertificateSerial() throws Exception {
+ final InputStream ris = Support_Resources.getStream("x509/cert-rsa-serial.txt");
+ try {
+ final BufferedReader buf = new BufferedReader(new InputStreamReader(ris));
+
+ String line = buf.readLine();
+ int index = line.indexOf('=');
+ assertEquals("serial", line.substring(0, index));
+
+ return new BigInteger(line.substring(index + 1), 16);
+ } finally {
+ try {
+ ris.close();
+ } catch (IOException ignored) {
+ }
+ }
+ }
+
+ private byte[] getResourceAsBytes(String name) throws Exception {
+ final InputStream ris = Support_Resources.getStream(name);
+ try {
+ DataInputStream dis = new DataInputStream(ris);
+ byte[] buf = new byte[ris.available()];
+ dis.readFully(buf);
+ return buf;
+ } finally {
+ try {
+ ris.close();
+ } catch (IOException ignored) {
+ }
+ }
+ }
+
+ private byte[] getRsaCertificateSignature() throws Exception {
+ return getResourceAsBytes(CERT_RSA_SIGNATURE);
+ }
+
+ private byte[] getRsaCertificateTbs() throws Exception {
+ return getResourceAsBytes(CERT_RSA_TBS);
+ }
+
+ public void test_Provider() throws Exception {
+ final ByteArrayOutputStream errBuffer = new ByteArrayOutputStream();
+ PrintStream out = new PrintStream(errBuffer);
+
+ for (Provider p : mX509Providers) {
+ try {
+ CertificateFactory f = CertificateFactory.getInstance("X.509", p);
+ getPublicKey(f);
+ getType(f);
+ check_equals(f);
+ check_toString(f);
+ check_hashCode(f);
+ checkValidity(f);
+ getVersion(f);
+ getSerialNumber(f);
+ getIssuerDN(f);
+ getIssuerX500Principal(f);
+ getSubjectDN(f);
+ getSubjectUniqueID(f);
+ getSubjectX500Principal(f);
+ getNotBeforeAndNotAfterDates(f);
+ getSigAlgName(f);
+ getSigAlgOID(f);
+ getSigAlgParams(f);
+ getIssuerUniqueID(f);
+ getSubjectUniqueID(f);
+ getKeyUsage(f);
+ getExtendedKeyUsage(f);
+ getBasicConstraints(f);
+ getSubjectAlternativeNames(f);
+ getSubjectAlternativeNames_IPV6(f);
+ getSubjectAlternativeNames_InvalidIP(f);
+ getSubjectAlternativeNames_Other(f);
+ getSubjectAlternativeNames_Email(f);
+ getSubjectAlternativeNames_DNS(f);
+ getSubjectAlternativeNames_DirName(f);
+ getSubjectAlternativeNames_URI(f);
+ getSubjectAlternativeNames_RID(f);
+ getIssuerAlternativeNames(f);
+ getTBSCertificate(f);
+ getSignature(f);
+ hasUnsupportedCriticalExtension(f);
+ getEncoded(f);
+ verify(f);
+ } catch (Throwable e) {
+ out.append("Error encountered checking " + p.getName() + "\n");
+ e.printStackTrace(out);
+ }
+ }
+
+ out.flush();
+ if (errBuffer.size() > 0) {
+ throw new Exception("Errors encountered:\n\n" + errBuffer.toString() + "\n\n");
+ }
+ }
+
+ private void getPublicKey(CertificateFactory f) throws Exception {
+ X509Certificate c = getCertificate(f, CERT_RSA);
+ PublicKey expected = getRsaCertificatePublicKey();
+
+ PublicKey actual = c.getPublicKey();
+ assertEquals(expected, actual);
+ assertEquals(Arrays.toString(expected.getEncoded()),
+ Arrays.toString(actual.getEncoded()));
+ }
+
+ private void getType(CertificateFactory f) throws Exception {
+ X509Certificate c = getCertificate(f, CERT_RSA);
+ assertEquals("X.509", c.getType());
+ }
+
+ private void verify(CertificateFactory f) throws Exception {
+ X509Certificate c = getCertificate(f, CERT_RSA);
+ PublicKey signer = getRsaCertificatePublicKey();
+
+ c.verify(signer);
+
+ KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
+ KeyPair pair = kpg.generateKeyPair();
+ PublicKey invalidKey = pair.getPublic();
+
+ try {
+ c.verify(invalidKey);
+ fail("RSA signature should not verify");
+ } catch (SignatureException expected) {
+ }
+
+ Provider[] providers = Security.getProviders("Signature." + c.getSigAlgName());
+ for (Provider p : providers) {
+ c.verify(signer, p.getName());
+
+ try {
+ c.verify(invalidKey, p.getName());
+ fail("RSA signature should not verify");
+ } catch (SignatureException expected) {
+ }
+ }
+ }
+
+ private void check_equals(CertificateFactory f) throws Exception {
+ X509Certificate c1 = getCertificate(f, CERT_RSA);
+ X509Certificate c2 = getCertificate(f, CERT_RSA);
+
+ assertEquals(c1, c2);
+
+ X509Certificate c3 = getCertificate(f, CERT_DSA);
+ assertFalse(c1.equals(c3));
+ assertFalse(c3.equals(c1));
+ }
+
+ private void check_toString(CertificateFactory f) throws Exception {
+ X509Certificate c1 = getCertificate(f, CERT_RSA);
+
+ String output1 = c1.toString();
+ assertNotNull(output1);
+ assertTrue(output1.length() > 0);
+
+ X509Certificate c2 = getCertificate(f, CERT_RSA);
+ assertEquals(c1.toString(), c2.toString());
+
+ X509Certificate c3 = getCertificate(f, CERT_DSA);
+ assertFalse(c3.toString().equals(c1.toString()));
+ }
+
+ private void check_hashCode(CertificateFactory f) throws Exception {
+ X509Certificate c1 = getCertificate(f, CERT_RSA);
+ X509Certificate c2 = getCertificate(f, CERT_RSA);
+
+ assertEquals(c1.hashCode(), c2.hashCode());
+
+ X509Certificate c3 = getCertificate(f, CERT_DSA);
+ assertFalse(c3.hashCode() == c1.hashCode());
+ }
+
+ private void checkValidity(CertificateFactory f) throws Exception {
+ X509Certificate c = getCertificate(f, CERT_RSA);
+ Calendar cal = Calendar.getInstance();
+ Date[] dates = getRsaCertificateDates();
+
+ /*
+ * The certificate validity periods in the test certificate MUST lie
+ * within the tested period. The API doesn't appear to allow any other
+ * way to test this code path as an unprivileged user.
+ */
+ Date now = new Date();
+ assertTrue(now.after(dates[0]));
+ assertTrue(now.before(dates[1]));
+
+ /* This assumes the script makes a long-lived cert. */
+ c.checkValidity();
+
+ /* A day after the start date. */
+ cal.setTime(dates[0]);
+ cal.add(Calendar.DAY_OF_MONTH, 1);
+ c.checkValidity(cal.getTime());
+
+ /* A second before the start date. */
+ cal.setTime(dates[1]);
+ cal.add(Calendar.SECOND, -1);
+ c.checkValidity(cal.getTime());
+
+ try {
+ cal.setTime(dates[0]);
+ cal.add(Calendar.SECOND, -1);
+ c.checkValidity(cal.getTime());
+ fail();
+ } catch (CertificateNotYetValidException expected) {
+ }
+
+ try {
+ cal.setTime(dates[0]);
+ cal.add(Calendar.MONTH, -6);
+ c.checkValidity(cal.getTime());
+ fail();
+ } catch (CertificateNotYetValidException expected) {
+ }
+
+ try {
+ cal.setTime(dates[1]);
+ cal.add(Calendar.SECOND, 1);
+ c.checkValidity(cal.getTime());
+ fail();
+ } catch (CertificateExpiredException expected) {
+ }
+
+ try {
+ cal.setTime(dates[1]);
+ cal.add(Calendar.YEAR, 1);
+ c.checkValidity(cal.getTime());
+ fail();
+ } catch (CertificateExpiredException expected) {
+ }
+ }
+
+ private void getVersion(CertificateFactory f) throws Exception {
+ X509Certificate c = getCertificate(f, CERT_RSA);
+ assertEquals(3, c.getVersion());
+ }
+
+ private void getSerialNumber(CertificateFactory f) throws Exception {
+ X509Certificate c = getCertificate(f, CERT_RSA);
+ BigInteger actual = getRsaCertificateSerial();
+
+ assertEquals(actual, c.getSerialNumber());
+ }
+
+ private void getIssuerDN(CertificateFactory f) throws Exception {
+ X509Certificate c = getCertificate(f, CERT_RSA);
+
+ Principal princ = c.getIssuerDN();
+ if (StandardNames.IS_RI) {
+ assertEquals("OU=NetOps, O=Genius.com Inc, L=San Mateo, ST=California, C=US",
+ princ.getName());
+ } else {
+ if ("BC".equals(f.getProvider().getName())) {
+ // TODO: is it acceptable to have this in reverse order?
+ assertEquals(f.getProvider().getName(),
+ "C=US,ST=California,L=San Mateo,O=Genius.com Inc,OU=NetOps",
+ princ.getName());
+ } else {
+ assertEquals("OU=NetOps,O=Genius.com Inc,L=San Mateo,ST=California,C=US",
+ princ.getName());
+ }
+ }
+
+ X509Certificate c2 = getCertificate(f, CERT_RSA);
+ assertEquals(princ, c2.getIssuerDN());
+ }
+
+ private void getIssuerX500Principal(CertificateFactory f) throws Exception {
+ X509Certificate c = getCertificate(f, CERT_RSA);
+
+ final byte[] expected = new byte[] {
+ 0x30, 0x60, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08,
+ 0x13, 0x0a, 0x43, 0x61, 0x6c, 0x69, 0x66, 0x6f, 0x72, 0x6e, 0x69, 0x61,
+ 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x09, 0x53,
+ 0x61, 0x6e, 0x20, 0x4d, 0x61, 0x74, 0x65, 0x6f, 0x31, 0x17, 0x30, 0x15,
+ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, 0x47, 0x65, 0x6e, 0x69, 0x75,
+ 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x20, 0x49, 0x6e, 0x63, 0x31, 0x0f, 0x30,
+ 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x06, 0x4e, 0x65, 0x74, 0x4f,
+ 0x70, 0x73
+ };
+ X500Principal princ = c.getIssuerX500Principal();
+ assertEquals(Arrays.toString(expected),
+ Arrays.toString(princ.getEncoded()));
+ assertEquals("OU=NetOps,O=Genius.com Inc,L=San Mateo,ST=California,C=US",
+ princ.getName());
+ assertEquals("ou=netops,o=genius.com inc,l=san mateo,st=california,c=us",
+ princ.getName(X500Principal.CANONICAL));
+ assertEquals("OU=NetOps, O=Genius.com Inc, L=San Mateo, ST=California, C=US",
+ princ.getName(X500Principal.RFC1779));
+ assertEquals("OU=NetOps,O=Genius.com Inc,L=San Mateo,ST=California,C=US",
+ princ.getName(X500Principal.RFC2253));
+
+ X509Certificate c2 = getCertificate(f, CERT_RSA);
+ assertEquals(princ, c2.getIssuerX500Principal());
+ }
+
+ private void getSubjectDN(CertificateFactory f) throws Exception {
+ X509Certificate c = getCertificate(f, CERT_RSA);
+
+ Principal princ = c.getSubjectDN();
+ if (StandardNames.IS_RI) {
+ assertEquals("OU=NetOps, O=Genius.com Inc, L=San Mateo, ST=California, C=US",
+ princ.getName());
+ } else {
+ if ("BC".equals(f.getProvider().getName())) {
+ // TODO: is it acceptable to have this in reverse order?
+ assertEquals(f.getProvider().getName(),
+ "C=US,ST=California,L=San Mateo,O=Genius.com Inc,OU=NetOps",
+ princ.getName());
+ } else {
+ assertEquals("OU=NetOps,O=Genius.com Inc,L=San Mateo,ST=California,C=US",
+ princ.getName());
+ }
+ }
+
+ X509Certificate c2 = getCertificate(f, CERT_RSA);
+ assertEquals(princ, c2.getSubjectDN());
+ }
+
+ private void getSubjectUniqueID(CertificateFactory f) throws Exception {
+ /* This certificate has no unique ID. */
+ X509Certificate c = getCertificate(f, CERT_RSA);
+ assertNull(c.getSubjectUniqueID());
+
+ // TODO: generate certificate that has a SubjectUniqueID field.
+ }
+
+ private void getIssuerUniqueID(CertificateFactory f) throws Exception {
+ /* This certificate has no unique ID. */
+ X509Certificate c = getCertificate(f, CERT_RSA);
+ assertNull(c.getIssuerUniqueID());
+
+ // TODO: generate certificate that has a IssuerUniqueID field.
+ }
+
+ private void getSubjectX500Principal(CertificateFactory f) throws Exception {
+ X509Certificate c = getCertificate(f, CERT_RSA);
+
+ final byte[] expected = new byte[] {
+ 0x30, 0x60, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08,
+ 0x13, 0x0a, 0x43, 0x61, 0x6c, 0x69, 0x66, 0x6f, 0x72, 0x6e, 0x69, 0x61,
+ 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x09, 0x53,
+ 0x61, 0x6e, 0x20, 0x4d, 0x61, 0x74, 0x65, 0x6f, 0x31, 0x17, 0x30, 0x15,
+ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, 0x47, 0x65, 0x6e, 0x69, 0x75,
+ 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x20, 0x49, 0x6e, 0x63, 0x31, 0x0f, 0x30,
+ 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x06, 0x4e, 0x65, 0x74, 0x4f,
+ 0x70, 0x73
+ };
+ X500Principal princ = c.getSubjectX500Principal();
+ assertEquals(Arrays.toString(expected),
+ Arrays.toString(princ.getEncoded()));
+ assertEquals("OU=NetOps,O=Genius.com Inc,L=San Mateo,ST=California,C=US",
+ princ.getName());
+ assertEquals("ou=netops,o=genius.com inc,l=san mateo,st=california,c=us",
+ princ.getName(X500Principal.CANONICAL));
+ assertEquals("OU=NetOps, O=Genius.com Inc, L=San Mateo, ST=California, C=US",
+ princ.getName(X500Principal.RFC1779));
+ assertEquals("OU=NetOps,O=Genius.com Inc,L=San Mateo,ST=California,C=US",
+ princ.getName(X500Principal.RFC2253));
+
+ X509Certificate c2 = getCertificate(f, CERT_RSA);
+ assertEquals(princ, c2.getSubjectX500Principal());
+ }
+
+ private static void assertDateEquals(Date date1, Date date2) throws Exception {
+ SimpleDateFormat formatter = new SimpleDateFormat("dd MMM yyyy HH:mm:ss");
+
+ String result1 = formatter.format(date1);
+ String result2 = formatter.format(date2);
+
+ assertEquals(result1, result2);
+ }
+
+ private void getNotBeforeAndNotAfterDates(CertificateFactory f) throws Exception {
+ X509Certificate c = getCertificate(f, CERT_RSA);
+ Date[] dates = getRsaCertificateDates();
+
+ assertDateEquals(dates[0], c.getNotBefore());
+ assertDateEquals(dates[1], c.getNotAfter());
+ }
+
+ private void getSigAlgName(CertificateFactory f) throws Exception {
+ {
+ /* The test certificate is sha1WithRSAEncryption */
+ X509Certificate c = getCertificate(f, CERT_RSA);
+ assertEquals("SHA1WITHRSA", c.getSigAlgName().toUpperCase(Locale.US));
+ }
+
+ {
+ /* The test certificate is sha1WithRSAEncryption */
+ X509Certificate c = getCertificate(f, CERT_DSA);
+ assertEquals("SHA1WITHDSA", c.getSigAlgName().toUpperCase(Locale.US));
+ }
+
+ {
+ /* The test certificate is sha1WithRSAEncryption */
+ X509Certificate c = getCertificate(f, CERT_EC);
+ if (StandardNames.IS_RI) {
+ assertEquals("SHA1WITHECDSA", c.getSigAlgName().toUpperCase(Locale.US));
+ } else {
+ assertEquals("ECDSA", c.getSigAlgName().toUpperCase(Locale.US));
+ }
+ }
+ }
+
+ private void getSigAlgOID(CertificateFactory f) throws Exception {
+ {
+ /* The test certificate is sha1WithRSAEncryption */
+ X509Certificate c = getCertificate(f, CERT_RSA);
+ assertEquals("1.2.840.113549.1.1.5", c.getSigAlgOID());
+ }
+
+ {
+ /* The test certificate is sha1WithRSAEncryption */
+ X509Certificate c = getCertificate(f, CERT_DSA);
+ assertEquals("1.2.840.10040.4.3", c.getSigAlgOID());
+ }
+
+ {
+ /* The test certificate is sha1WithRSAEncryption */
+ X509Certificate c = getCertificate(f, CERT_EC);
+ assertEquals("1.2.840.10045.4.1", c.getSigAlgOID());
+ }
+ }
+
+ private void getSigAlgParams(CertificateFactory f) throws Exception {
+ {
+ X509Certificate c = getCertificate(f, CERT_RSA);
+ // Harmony and BC are broken?
+ String provider = f.getProvider().getName();
+ if ("DRLCertFactory".equals(provider) || "BC".equals(provider)) {
+ assertNotNull(c.getSigAlgParams());
+ } else {
+ assertNull(f.getProvider().getName(), c.getSigAlgParams());
+ }
+ }
+
+ {
+ X509Certificate c = getCertificate(f, CERT_DSA);
+ assertNull(f.getProvider().getName(), c.getSigAlgParams());
+ }
+
+ {
+ X509Certificate c = getCertificate(f, CERT_EC);
+ assertNull(f.getProvider().getName(), c.getSigAlgParams());
+ }
+ }
+
+ private void getKeyUsage(CertificateFactory f) throws Exception {
+ {
+ /* The test certificate is sha1WithRSAEncryption */
+ X509Certificate c = getCertificate(f, CERT_RSA);
+ boolean[] expected = new boolean[] {
+ true, /* digitalSignature (0) */
+ true, /* nonRepudiation (1) */
+ true, /* keyEncipherment (2) */
+ false, /* dataEncipherment (3) */
+ false, /* keyAgreement (4) */
+ false, /* keyCertSign (5) */
+ false, /* cRLSign (6) */
+ false, /* encipherOnly (7) */
+ false, /* decipherOnly (8) */
+ };
+ assertEquals(Arrays.toString(expected), Arrays.toString(c.getKeyUsage()));
+ }
+
+ {
+ /* The test certificate is sha1WithRSAEncryption */
+ X509Certificate c = getCertificate(f, CERT_DSA);
+ boolean[] expected = new boolean[] {
+ false, /* digitalSignature (0) */
+ false, /* nonRepudiation (1) */
+ true, /* keyEncipherment (2) */
+ true, /* dataEncipherment (3) */
+ false, /* keyAgreement (4) */
+ true, /* keyCertSign (5) */
+ true, /* cRLSign (6) */
+ true, /* encipherOnly (7) */
+ false, /* decipherOnly (8) */
+ };
+ boolean[] actual = c.getKeyUsage();
+ assertEquals(9, actual.length);
+ assertEquals(Arrays.toString(expected), Arrays.toString(actual));
+ }
+
+ {
+ /* The test certificate is sha1WithRSAEncryption */
+ X509Certificate c = getCertificate(f, CERT_EC);
+ boolean[] expected = new boolean[] {
+ false, /* digitalSignature (0) */
+ false, /* nonRepudiation (1) */
+ false, /* keyEncipherment (2) */
+ false, /* dataEncipherment (3) */
+ true, /* keyAgreement (4) */
+ false, /* keyCertSign (5) */
+ false, /* cRLSign (6) */
+ false, /* encipherOnly (7) */
+ true, /* decipherOnly (8) */
+ };
+ boolean[] actual = c.getKeyUsage();
+ assertEquals(9, actual.length);
+ assertEquals(Arrays.toString(expected), Arrays.toString(actual));
+ }
+
+ {
+ /* All the bits are set in addition to some extra ones. */
+ X509Certificate c = getCertificate(f, CERT_KEYUSAGE_EXTRALONG);
+ boolean[] expected = new boolean[] {
+ true, /* digitalSignature (0) */
+ true, /* nonRepudiation (1) */
+ true, /* keyEncipherment (2) */
+ true, /* dataEncipherment (3) */
+ true, /* keyAgreement (4) */
+ true, /* keyCertSign (5) */
+ true, /* cRLSign (6) */
+ true, /* encipherOnly (7) */
+ true, /* decipherOnly (8) */
+ true, /* ????? (9) */
+ true, /* ????? (10) */
+ };
+ boolean[] actual = c.getKeyUsage();
+ assertEquals(11, actual.length);
+ assertEquals(Arrays.toString(expected), Arrays.toString(actual));
+ }
+ }
+
+ private void getExtendedKeyUsage(CertificateFactory f) throws Exception {
+ {
+ /* No ExtendedKeyUsage section */
+ final X509Certificate c = getCertificate(f, CERT_RSA);
+ List<String> actual = c.getExtendedKeyUsage();
+ assertNull(actual);
+ }
+
+ {
+ /* ExtendedKeyUsage section with one entry of OID 1.2.3.4 */
+ final X509Certificate c = getCertificate(f, CERT_EXTENDEDKEYUSAGE);
+ List<String> actual = c.getExtendedKeyUsage();
+ assertNotNull(actual);
+ assertEquals(1, actual.size());
+ assertEquals("1.2.3.4", actual.get(0));
+ }
+ }
+
+ private void getBasicConstraints(CertificateFactory f) throws Exception {
+ /* Non-CA cert with no pathLenConstraint */
+ {
+ final X509Certificate c = getCertificate(f, CERT_RSA);
+ assertEquals(f.getProvider().getName(), -1, c.getBasicConstraints());
+ }
+
+ /* Non-CA cert with pathLenConstraint */
+ {
+ final X509Certificate c = getCertificate(f, CERT_USERWITHPATHLEN);
+ assertEquals(f.getProvider().getName(), -1, c.getBasicConstraints());
+ }
+
+ /* CA cert with no pathLenConstraint */
+ {
+ final X509Certificate c = getCertificate(f, CERT_CA);
+ assertEquals(f.getProvider().getName(), Integer.MAX_VALUE, c.getBasicConstraints());
+ }
+
+ /* CA cert with pathLenConstraint=10 */
+ {
+ final X509Certificate c = getCertificate(f, CERT_CAWITHPATHLEN);
+ assertEquals(f.getProvider().getName(), 10, c.getBasicConstraints());
+ }
+ }
+
+ /** Encoding of: OID:1.2.3.4, UTF8:test1 */
+ private static byte[] getOIDTestBytes() {
+ if (StandardNames.IS_RI) {
+ return new byte[] { 0x30, 0x10, 0x06, 0x03, 0x2a, 0x03, 0x04, (byte) 0xa0,
+ 0x09, (byte) 0xa0, 0x07, 0x0c, 0x05, 0x74, 0x65, 0x73, 0x74, 0x31 };
+ } else {
+ return new byte[] { (byte) 0xa0, 0x0e, 0x06, 0x03, 0x2a, 0x03, 0x04,
+ (byte) 0xa0, 0x07, 0x0c, 0x05, 0x74, 0x65, 0x73, 0x74, 0x31 };
+ }
+ }
+
+ private void getSubjectAlternativeNames(CertificateFactory f) throws Exception {
+ X509Certificate c = getCertificate(f, CERT_RSA);
+ Collection<List<?>> col = null;
+ try {
+ col = c.getSubjectAlternativeNames();
+ if ("DRLCertFactory".equals(f.getProvider().getName())) {
+ fail("Harmony should throw (Provider=" + f.getProvider().getName() + ")");
+ }
+ } catch (CertificateParsingException e) {
+ if (!"DRLCertFactory".equals(f.getProvider().getName())) {
+ throw new Exception("Non-Harmony shouldn't throw", e);
+ }
+ return;
+ }
+
+ // BouncyCastle is broken
+ if ("BC".equals(f.getProvider().getName())) {
+ assertNull(col);
+ return;
+ }
+
+ checkAlternativeNames(col);
+ }
+
+ private void checkAlternativeNames(Collection<List<?>> col) {
+ assertNotNull(col);
+
+ /*
+ * There should be 9 types of alternative names in this test
+ * certificate.
+ */
+ boolean[] typesFound = new boolean[9];
+
+ for (List<?> item : col) {
+ assertTrue(item.get(0) instanceof Integer);
+ int type = (Integer) item.get(0);
+ typesFound[type] = true;
+
+ switch (type) {
+ case 0: /* OtherName */
+ final byte[] der = getOIDTestBytes();
+ assertEquals(Arrays.toString(der), Arrays.toString((byte[]) item.get(1)));
+ break;
+ case 1: /* rfc822Name: IA5String */
+ assertEquals("x509@example.com", (String) item.get(1));
+ break;
+ case 2: /* dNSName: IA5String */
+ assertEquals("x509.example.com", (String) item.get(1));
+ break;
+ case 3: /* x400Address: ORAddress */
+ assertEquals("UNSUPPORTED", (String) item.get(1));
+ break;
+ case 4: /* directoryName: Name */
+ assertEquals("CN=∆ƒ,OU=Über Frîends,O=Awesome Dudes,C=US", (String) item.get(1));
+ break;
+ case 5: /* ediPartyName */
+ assertEquals("UNSUPPORTED", Arrays.toString((byte[]) item.get(1)));
+ break;
+ case 6: /* uniformResourceIdentifier: IA5String */
+ assertEquals("http://www.example.com/?q=awesomeness", (String) item.get(1));
+ break;
+ case 7: /* iPAddress */
+ assertEquals("192.168.0.1", (String) item.get(1));
+ break;
+ case 8:
+ assertEquals("1.2.3.4", (String) item.get(1));
+ break;
+ }
+ }
+
+ Set<Integer> missing = new HashSet<Integer>();
+ for (int i = 0; i < typesFound.length; i++) {
+ if (!typesFound[i]) {
+ missing.add(i);
+ }
+ }
+
+ // TODO: fix X.400 names and ediPartyName
+ missing.remove(3);
+ missing.remove(5);
+
+ if (!missing.isEmpty()) {
+ fail("Missing types: " + Arrays.toString(missing.toArray(new Integer[missing.size()])));
+ }
+ }
+
+ private void getSubjectAlternativeNames_IPV6(CertificateFactory f) throws Exception {
+ X509Certificate c = getCertificate(f, CERT_IPV6);
+ Collection<List<?>> col = c.getSubjectAlternativeNames();
+
+ // BouncyCastle is broken
+ if ("BC".equals(f.getProvider().getName())) {
+ assertNull(col);
+ return;
+ }
+
+ assertNotNull(f.getProvider().getName(), col);
+
+ assertEquals(1, col.size());
+ List<?> item = col.iterator().next();
+
+ assertTrue(item.get(0) instanceof Integer);
+ assertTrue(7 == (Integer) item.get(0));
+
+ assertTrue(item.get(1) instanceof String);
+ // RI doesn't apply all the IPv6 shortening rules
+ if (StandardNames.IS_RI) {
+ assertEquals("2001:db8:0:0:0:ff00:42:8329", (String) item.get(1));
+ } else {
+ assertEquals("2001:db8::ff00:42:8329", (String) item.get(1));
+ }
+ }
+
+ private void getSubjectAlternativeNames_InvalidIP(CertificateFactory f) throws Exception {
+ X509Certificate c = getCertificate(f, CERT_INVALIDIP);
+ Collection<List<?>> col = null;
+ try {
+ col = c.getSubjectAlternativeNames();
+ if ("DRLCertFactory".equals(f.getProvider().getName())) {
+ fail("Harmony throws (Provider=" + f.getProvider().getName() + ")");
+ }
+ } catch (IllegalArgumentException expected) {
+ if (!"DRLCertFactory".equals(f.getProvider().getName())) {
+ fail("Non-Harmony shouldn't throw");
+ }
+ }
+ assertNull(col);
+ }
+
+ private void getSubjectAlternativeNames_Other(CertificateFactory f) throws Exception {
+ X509Certificate c = getCertificate(f, CERT_ALT_OTHER);
+ Collection<List<?>> col = c.getSubjectAlternativeNames();
+
+ // BouncyCastle is broken
+ if ("BC".equals(f.getProvider().getName())) {
+ assertNull(col);
+ return;
+ }
+
+ assertNotNull(f.getProvider().getName(), col);
+
+ assertEquals(1, col.size());
+ List<?> item = col.iterator().next();
+
+ assertTrue(item.get(0) instanceof Integer);
+ assertTrue(0 == (Integer) item.get(0));
+
+ /* OID:1.2.3.4, UTF8:test1 */
+ final byte[] der = getOIDTestBytes();
+ assertEquals(Arrays.toString(der), Arrays.toString((byte[]) item.get(1)));
+ }
+
+ private void getSubjectAlternativeNames_Email(CertificateFactory f) throws Exception {
+ X509Certificate c = getCertificate(f, CERT_ALT_EMAIL);
+ Collection<List<?>> col = c.getSubjectAlternativeNames();
+
+ // BouncyCastle is broken
+ if ("BC".equals(f.getProvider().getName())) {
+ assertNull(col);
+ return;
+ }
+
+ assertNotNull(f.getProvider().getName(), col);
+
+ assertEquals(1, col.size());
+ List<?> item = col.iterator().next();
+
+ assertTrue(item.get(0) instanceof Integer);
+ assertTrue(1 == (Integer) item.get(0));
+
+ assertTrue(item.get(1) instanceof String);
+ assertEquals("x509@example.com", (String) item.get(1));
+ }
+
+ private void getSubjectAlternativeNames_DNS(CertificateFactory f) throws Exception {
+ X509Certificate c = getCertificate(f, CERT_ALT_DNS);
+ Collection<List<?>> col = c.getSubjectAlternativeNames();
+
+ // BouncyCastle is broken
+ if ("BC".equals(f.getProvider().getName())) {
+ assertNull(col);
+ return;
+ }
+
+ assertNotNull(f.getProvider().getName(), col);
+
+ assertEquals(1, col.size());
+ List<?> item = col.iterator().next();
+
+ assertTrue(item.get(0) instanceof Integer);
+ assertTrue(2 == (Integer) item.get(0));
+
+ assertTrue(item.get(1) instanceof String);
+ assertEquals("x509.example.com", (String) item.get(1));
+ }
+
+ private void getSubjectAlternativeNames_DirName(CertificateFactory f) throws Exception {
+ X509Certificate c = getCertificate(f, CERT_ALT_DIRNAME);
+ Collection<List<?>> col = null;
+ try {
+ col = c.getSubjectAlternativeNames();
+ if ("DRLCertFactory".equals(f.getProvider().getName())) {
+ fail("Harmony throws (Provider=" + f.getProvider().getName() + ")");
+ }
+ } catch (CertificateParsingException e) {
+ // Harmony is broken
+ if (!"DRLCertFactory".equals(f.getProvider().getName())) {
+ fail("Non-Harmony shouldn't throw");
+ }
+ return;
+ }
+
+ // BouncyCastle is broken
+ if ("BC".equals(f.getProvider().getName())) {
+ assertNull(col);
+ return;
+ }
+
+ assertNotNull(f.getProvider().getName(), col);
+
+ assertEquals(1, col.size());
+ List<?> item = col.iterator().next();
+
+ assertTrue(item.get(0) instanceof Integer);
+ assertTrue(String.valueOf((Integer) item.get(0)), 4 == (Integer) item.get(0));
+
+ assertTrue(item.get(1) instanceof String);
+ assertEquals("CN=∆ƒ,OU=Über Frîends,O=Awesome Dudes,C=US", (String) item.get(1));
+ }
+
+ private void getSubjectAlternativeNames_URI(CertificateFactory f) throws Exception {
+ X509Certificate c = getCertificate(f, CERT_ALT_URI);
+ Collection<List<?>> col = c.getSubjectAlternativeNames();
+
+ // BouncyCastle is broken
+ if ("BC".equals(f.getProvider().getName())) {
+ assertNull(col);
+ return;
+ }
+
+ assertNotNull(f.getProvider().getName(), col);
+
+ assertEquals(1, col.size());
+ List<?> item = col.iterator().next();
+
+ assertTrue(item.get(0) instanceof Integer);
+ assertTrue(6 == (Integer) item.get(0));
+
+ assertTrue(item.get(1) instanceof String);
+ assertEquals("http://www.example.com/?q=awesomeness", (String) item.get(1));
+ }
+
+ private void getSubjectAlternativeNames_RID(CertificateFactory f) throws Exception {
+ X509Certificate c = getCertificate(f, CERT_ALT_RID);
+ Collection<List<?>> col = c.getSubjectAlternativeNames();
+
+ // BouncyCastle is broken
+ if ("BC".equals(f.getProvider().getName())) {
+ assertNull(col);
+ return;
+ }
+
+ assertNotNull(f.getProvider().getName(), col);
+
+ assertEquals(1, col.size());
+ List<?> item = col.iterator().next();
+
+ assertTrue(item.get(0) instanceof Integer);
+ assertTrue(8 == (Integer) item.get(0));
+
+ assertTrue(item.get(1) instanceof String);
+ assertEquals("1.2.3.4", (String) item.get(1));
+ }
+
+ private void getIssuerAlternativeNames(CertificateFactory f) throws Exception {
+ X509Certificate c = getCertificate(f, CERT_RSA);
+ Collection<List<?>> col = null;
+ try {
+ col = c.getSubjectAlternativeNames();
+ if ("DRLCertFactory".equals(f.getProvider().getName())) {
+ fail("Harmony should throw (Provider=" + f.getProvider().getName() + ")");
+ }
+ } catch (CertificateParsingException e) {
+ if (!"DRLCertFactory".equals(f.getProvider().getName())) {
+ throw new Exception("Non-Harmony shouldn't throw", e);
+ }
+ return;
+ }
+
+ // BouncyCastle is broken
+ if ("BC".equals(f.getProvider().getName())) {
+ assertNull(col);
+ return;
+ }
+
+ checkAlternativeNames(col);
+ }
+
+ private void getSignature(CertificateFactory f) throws Exception {
+ X509Certificate c = getCertificate(f, CERT_RSA);
+
+ assertEquals(Arrays.toString(getRsaCertificateSignature()),
+ Arrays.toString(c.getSignature()));
+ }
+
+ private void getTBSCertificate(CertificateFactory f) throws Exception {
+ X509Certificate c = getCertificate(f, CERT_RSA);
+
+ assertEquals(Arrays.toString(getRsaCertificateTbs()),
+ Arrays.toString(c.getTBSCertificate()));
+ }
+
+ private void hasUnsupportedCriticalExtension(CertificateFactory f) throws Exception {
+ X509Certificate c = getCertificate(f, CERT_RSA);
+ assertFalse(c.hasUnsupportedCriticalExtension());
+
+ X509Certificate unsupported = getCertificate(f, CERT_UNSUPPORTED);
+ assertTrue(unsupported.hasUnsupportedCriticalExtension());
+ }
+
+ private void getEncoded(CertificateFactory f) throws Exception {
+ X509Certificate c = getCertificate(f, CERT_RSA);
+
+ byte[] cBytes = getResourceAsBytes(CERT_RSA);
+
+ assertEquals(Arrays.toString(cBytes), Arrays.toString(c.getEncoded()));
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mX509Providers = Security.getProviders("CertificateFactory.X509");
+ }
+}
diff --git a/support/src/test/java/tests/resources/x509/cert-alt-dirname.der b/support/src/test/java/tests/resources/x509/cert-alt-dirname.der
new file mode 100644
index 0000000..ae2d2a8
--- /dev/null
+++ b/support/src/test/java/tests/resources/x509/cert-alt-dirname.der
Binary files differ
diff --git a/support/src/test/java/tests/resources/x509/cert-alt-dns.der b/support/src/test/java/tests/resources/x509/cert-alt-dns.der
new file mode 100644
index 0000000..008ef31
--- /dev/null
+++ b/support/src/test/java/tests/resources/x509/cert-alt-dns.der
Binary files differ
diff --git a/support/src/test/java/tests/resources/x509/cert-alt-email.der b/support/src/test/java/tests/resources/x509/cert-alt-email.der
new file mode 100644
index 0000000..4b0189b
--- /dev/null
+++ b/support/src/test/java/tests/resources/x509/cert-alt-email.der
Binary files differ
diff --git a/support/src/test/java/tests/resources/x509/cert-alt-other.der b/support/src/test/java/tests/resources/x509/cert-alt-other.der
new file mode 100644
index 0000000..772d9e9
--- /dev/null
+++ b/support/src/test/java/tests/resources/x509/cert-alt-other.der
Binary files differ
diff --git a/support/src/test/java/tests/resources/x509/cert-alt-rid.der b/support/src/test/java/tests/resources/x509/cert-alt-rid.der
new file mode 100644
index 0000000..8a1bf42
--- /dev/null
+++ b/support/src/test/java/tests/resources/x509/cert-alt-rid.der
Binary files differ
diff --git a/support/src/test/java/tests/resources/x509/cert-alt-uri.der b/support/src/test/java/tests/resources/x509/cert-alt-uri.der
new file mode 100644
index 0000000..262ffa8
--- /dev/null
+++ b/support/src/test/java/tests/resources/x509/cert-alt-uri.der
Binary files differ
diff --git a/support/src/test/java/tests/resources/x509/cert-ca.der b/support/src/test/java/tests/resources/x509/cert-ca.der
new file mode 100644
index 0000000..8699071
--- /dev/null
+++ b/support/src/test/java/tests/resources/x509/cert-ca.der
Binary files differ
diff --git a/support/src/test/java/tests/resources/x509/cert-caWithPathLen.der b/support/src/test/java/tests/resources/x509/cert-caWithPathLen.der
new file mode 100644
index 0000000..ac56314
--- /dev/null
+++ b/support/src/test/java/tests/resources/x509/cert-caWithPathLen.der
Binary files differ
diff --git a/support/src/test/java/tests/resources/x509/cert-dsa.der b/support/src/test/java/tests/resources/x509/cert-dsa.der
new file mode 100644
index 0000000..9b5e62a
--- /dev/null
+++ b/support/src/test/java/tests/resources/x509/cert-dsa.der
Binary files differ
diff --git a/support/src/test/java/tests/resources/x509/cert-ec.der b/support/src/test/java/tests/resources/x509/cert-ec.der
new file mode 100644
index 0000000..a683a5e
--- /dev/null
+++ b/support/src/test/java/tests/resources/x509/cert-ec.der
Binary files differ
diff --git a/support/src/test/java/tests/resources/x509/cert-extendedKeyUsage.der b/support/src/test/java/tests/resources/x509/cert-extendedKeyUsage.der
new file mode 100644
index 0000000..be2d20c
--- /dev/null
+++ b/support/src/test/java/tests/resources/x509/cert-extendedKeyUsage.der
Binary files differ
diff --git a/support/src/test/java/tests/resources/x509/cert-invalidip.der b/support/src/test/java/tests/resources/x509/cert-invalidip.der
new file mode 100644
index 0000000..63ed7e3
--- /dev/null
+++ b/support/src/test/java/tests/resources/x509/cert-invalidip.der
Binary files differ
diff --git a/support/src/test/java/tests/resources/x509/cert-ipv6.der b/support/src/test/java/tests/resources/x509/cert-ipv6.der
new file mode 100644
index 0000000..4ee144a
--- /dev/null
+++ b/support/src/test/java/tests/resources/x509/cert-ipv6.der
Binary files differ
diff --git a/support/src/test/java/tests/resources/x509/cert-keyUsage-extraLong.der b/support/src/test/java/tests/resources/x509/cert-keyUsage-extraLong.der
new file mode 100644
index 0000000..464799d
--- /dev/null
+++ b/support/src/test/java/tests/resources/x509/cert-keyUsage-extraLong.der
Binary files differ
diff --git a/support/src/test/java/tests/resources/x509/cert-rsa-dates.txt b/support/src/test/java/tests/resources/x509/cert-rsa-dates.txt
new file mode 100644
index 0000000..2bb7733
--- /dev/null
+++ b/support/src/test/java/tests/resources/x509/cert-rsa-dates.txt
@@ -0,0 +1,2 @@
+notBefore=Dec 31 01:16:36 2012 GMT
+notAfter=Dec 29 01:16:36 2022 GMT
diff --git a/support/src/test/java/tests/resources/x509/cert-rsa-pubkey.der b/support/src/test/java/tests/resources/x509/cert-rsa-pubkey.der
new file mode 100644
index 0000000..dec442f
--- /dev/null
+++ b/support/src/test/java/tests/resources/x509/cert-rsa-pubkey.der
Binary files differ
diff --git a/support/src/test/java/tests/resources/x509/cert-rsa-serial.txt b/support/src/test/java/tests/resources/x509/cert-rsa-serial.txt
new file mode 100644
index 0000000..c6f9585
--- /dev/null
+++ b/support/src/test/java/tests/resources/x509/cert-rsa-serial.txt
@@ -0,0 +1 @@
+serial=925D02E030B81D33
diff --git a/support/src/test/java/tests/resources/x509/cert-rsa-sig.der b/support/src/test/java/tests/resources/x509/cert-rsa-sig.der
new file mode 100644
index 0000000..2b80ab0
--- /dev/null
+++ b/support/src/test/java/tests/resources/x509/cert-rsa-sig.der
@@ -0,0 +1,2 @@
+fD\/o'r9#m [0n2'9 =^kS5K VILe6$:kOHyd:S=tMTlZK̉F(Q<`
+f) \ No newline at end of file
diff --git a/support/src/test/java/tests/resources/x509/cert-rsa-tbs.der b/support/src/test/java/tests/resources/x509/cert-rsa-tbs.der
new file mode 100644
index 0000000..6982699
--- /dev/null
+++ b/support/src/test/java/tests/resources/x509/cert-rsa-tbs.der
Binary files differ
diff --git a/support/src/test/java/tests/resources/x509/cert-rsa.der b/support/src/test/java/tests/resources/x509/cert-rsa.der
new file mode 100644
index 0000000..5816ec3
--- /dev/null
+++ b/support/src/test/java/tests/resources/x509/cert-rsa.der
Binary files differ
diff --git a/support/src/test/java/tests/resources/x509/cert-unsupported.der b/support/src/test/java/tests/resources/x509/cert-unsupported.der
new file mode 100644
index 0000000..525d782
--- /dev/null
+++ b/support/src/test/java/tests/resources/x509/cert-unsupported.der
Binary files differ
diff --git a/support/src/test/java/tests/resources/x509/cert-userWithPathLen.der b/support/src/test/java/tests/resources/x509/cert-userWithPathLen.der
new file mode 100644
index 0000000..da64c51
--- /dev/null
+++ b/support/src/test/java/tests/resources/x509/cert-userWithPathLen.der
Binary files differ
diff --git a/support/src/test/java/tests/resources/x509/create.sh b/support/src/test/java/tests/resources/x509/create.sh
new file mode 100755
index 0000000..5e78620
--- /dev/null
+++ b/support/src/test/java/tests/resources/x509/create.sh
@@ -0,0 +1,72 @@
+#!/bin/bash -
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -o nounset # Treat unset variables as an error
+set -e
+
+DIR=$(dirname $0)
+
+openssl req -config ${DIR}/default.cnf -new -nodes -batch > cert-rsa-req.pem
+openssl req -in cert-rsa-req.pem -pubkey -noout | openssl rsa -pubin -pubout -outform der > cert-rsa-pubkey.der
+openssl x509 -extfile ${DIR}/default.cnf -days 3650 -extensions usr_cert -req -signkey privkey.pem -outform d < cert-rsa-req.pem > cert-rsa.der
+rm -f cert-rsa-req.pem
+
+openssl asn1parse -in cert-rsa.der -inform d -out cert-rsa-tbs.der -noout -strparse 4
+SIG_OFFSET=$(openssl asn1parse -in cert-rsa.der -inform d | tail -1 | cut -f1 -d:)
+openssl asn1parse -in cert-rsa.der -inform d -strparse ${SIG_OFFSET} -noout -out cert-rsa-sig.der
+
+# extract startdate and enddate
+openssl x509 -in cert-rsa.der -inform d -noout -startdate -enddate > cert-rsa-dates.txt
+
+# extract serial
+openssl x509 -in cert-rsa.der -inform d -noout -serial > cert-rsa-serial.txt
+
+openssl req -config ${DIR}/default.cnf -new -nodes -batch | openssl x509 -extfile ${DIR}/default.cnf -extensions keyUsage_extraLong_cert -req -signkey privkey.pem -outform d > cert-keyUsage-extraLong.der
+
+openssl req -config ${DIR}/default.cnf -new -nodes -batch | openssl x509 -extfile ${DIR}/default.cnf -extensions extendedKeyUsage_cert -req -signkey privkey.pem -outform d > cert-extendedKeyUsage.der
+
+openssl req -config ${DIR}/default.cnf -new -nodes -batch | openssl x509 -extfile ${DIR}/default.cnf -extensions ca_cert -req -signkey privkey.pem -outform d > cert-ca.der
+
+openssl req -config ${DIR}/default.cnf -new -nodes -batch | openssl x509 -extfile ${DIR}/default.cnf -extensions userWithPathLen_cert -req -signkey privkey.pem -outform d > cert-userWithPathLen.der
+
+openssl req -config ${DIR}/default.cnf -new -nodes -batch | openssl x509 -extfile ${DIR}/default.cnf -extensions caWithPathLen_cert -req -signkey privkey.pem -outform d > cert-caWithPathLen.der
+
+openssl req -config ${DIR}/default.cnf -new -nodes -batch | openssl x509 -extfile ${DIR}/default.cnf -extensions alt_other_cert -req -signkey privkey.pem -outform d > cert-alt-other.der
+
+openssl req -config ${DIR}/default.cnf -new -nodes -batch | openssl x509 -extfile ${DIR}/default.cnf -extensions alt_email_cert -req -signkey privkey.pem -outform d > cert-alt-email.der
+
+openssl req -config ${DIR}/default.cnf -new -nodes -batch | openssl x509 -extfile ${DIR}/default.cnf -extensions alt_dns_cert -req -signkey privkey.pem -outform d > cert-alt-dns.der
+
+openssl req -config ${DIR}/default.cnf -new -nodes -batch | openssl x509 -extfile ${DIR}/default.cnf -extensions alt_dirname_cert -req -signkey privkey.pem -outform d > cert-alt-dirname.der
+
+openssl req -config ${DIR}/default.cnf -new -nodes -batch | openssl x509 -extfile ${DIR}/default.cnf -extensions alt_uri_cert -req -signkey privkey.pem -outform d > cert-alt-uri.der
+
+openssl req -config ${DIR}/default.cnf -new -nodes -batch | openssl x509 -extfile ${DIR}/default.cnf -extensions alt_rid_cert -req -signkey privkey.pem -outform d > cert-alt-rid.der
+
+openssl req -config ${DIR}/default.cnf -new -nodes -batch | openssl x509 -extfile ${DIR}/default.cnf -extensions ipv6_cert -req -signkey privkey.pem -outform d > cert-ipv6.der
+
+openssl req -config ${DIR}/default.cnf -new -nodes -batch | openssl x509 -extfile ${DIR}/default.cnf -extensions unsupported_cert -req -signkey privkey.pem -outform d > cert-unsupported.der
+
+openssl dsaparam -out dsaparam.pem 1024
+openssl req -config ${DIR}/default.cnf -newkey dsa:dsaparam.pem -keyout dsapriv.pem -nodes -batch | openssl x509 -extfile ${DIR}/default.cnf -extensions keyUsage_cert -req -signkey dsapriv.pem -outform d > cert-dsa.der
+rm -f dsaparam.pem
+
+openssl ecparam -name sect283k1 -out ecparam.pem
+openssl req -config ${DIR}/default.cnf -newkey ec:ecparam.pem -keyout ecpriv.pem -nodes -batch | openssl x509 -extfile ${DIR}/default.cnf -extensions keyUsage_critical_cert -req -signkey ecpriv.pem -outform d > cert-ec.der
+rm -f ecparam.pem
+
+rm -f privkey.pem
+rm -f dsapriv.pem
+rm -f ecpriv.pem
diff --git a/support/src/test/java/tests/resources/x509/default.cnf b/support/src/test/java/tests/resources/x509/default.cnf
new file mode 100644
index 0000000..d4b4c80
--- /dev/null
+++ b/support/src/test/java/tests/resources/x509/default.cnf
@@ -0,0 +1,292 @@
+# This is based on the default OpenSSL configuration file which is
+# licensed with the following license:
+
+# Copyright (c) 1998-2011 The OpenSSL Project. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# 3. All advertising materials mentioning features or use of this
+# software must display the following acknowledgment:
+# "This product includes software developed by the OpenSSL Project
+# for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+#
+# 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+# endorse or promote products derived from this software without
+# prior written permission. For written permission, please contact
+# openssl-core@openssl.org.
+#
+# 5. Products derived from this software may not be called "OpenSSL"
+# nor may "OpenSSL" appear in their names without prior written
+# permission of the OpenSSL Project.
+#
+# 6. Redistributions of any form whatsoever must retain the following
+# acknowledgment:
+# "This product includes software developed by the OpenSSL Project
+# for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+#
+# THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+# EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
+# ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+# ====================================================================
+#
+# This product includes cryptographic software written by Eric Young
+# (eay@cryptsoft.com). This product includes software written by Tim
+# Hudson (tjh@cryptsoft.com).
+#
+
+HOME = .
+RANDFILE = $ENV::HOME/.rnd
+
+# Extra OBJECT IDENTIFIER info:
+#oid_file = $ENV::HOME/.oid
+oid_section = new_oids
+
+# To use this configuration file with the "-extfile" option of the
+# "openssl x509" utility, name here the section containing the
+# X.509v3 extensions to use:
+# extensions =
+# (Alternatively, use a configuration file that has only
+# X.509v3 extensions in its main [= default] section.)
+
+[ new_oids ]
+
+# We can add new OIDs in here for use by 'ca' and 'req'.
+# Add a simple OID like this:
+# testoid1=1.2.3.4
+# Or use config file substitution like this:
+# testoid2=${testoid1}.5.6
+
+####################################################################
+[ ca ]
+default_ca = CA_default # The default ca section
+
+####################################################################
+[ CA_default ]
+
+dir = /root/certificates # Where everything is kept
+certs = $dir/certs # Where the issued certs are kept
+crl_dir = $dir/crl # Where the issued crl are kept
+database = $dir/index.txt # database index file.
+new_certs_dir = $dir/newcerts # default place for new certs.
+
+certificate = $dir/cacert.pem # The CA certificate
+serial = $dir/serial # The current serial number
+crl = $dir/crl.pem # The current CRL
+private_key = $dir/private/cakey.pem# The private key
+RANDFILE = $dir/private/.rand # private random number file
+
+x509_extensions = usr_cert # The extentions to add to the cert
+
+# Comment out the following two lines for the "traditional"
+# (and highly broken) format.
+name_opt = ca_default # Subject Name options
+cert_opt = ca_default # Certificate field options
+
+# Extension copying option: use with caution.
+# copy_extensions = copy
+
+# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs
+# so this is commented out by default to leave a V1 CRL.
+# crl_extensions = crl_ext
+
+default_days = 365 # how long to certify for
+default_crl_days= 30 # how long before next CRL
+default_md = md5 # which md to use.
+preserve = no # keep passed DN ordering
+
+policy = policy_anything
+
+[ policy_match ]
+countryName = match
+stateOrProvinceName = match
+organizationName = match
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+[ policy_anything ]
+countryName = optional
+stateOrProvinceName = optional
+localityName = optional
+organizationName = optional
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+####################################################################
+[ req ]
+default_bits = 1024
+default_keyfile = privkey.pem
+distinguished_name = req_distinguished_name
+attributes = req_attributes
+x509_extensions = v3_ca # The extentions to add to the self signed cert
+string_mask = nombstr
+req_extensions = v3_req # The extensions to add to a certificate request
+
+[ req_distinguished_name ]
+countryName = Country Name (2 letter code)
+countryName_default = US
+countryName_min = 2
+countryName_max = 2
+
+stateOrProvinceName = State or Province Name (full name)
+stateOrProvinceName_default = California
+
+localityName = Locality Name (eg, city)
+localityName_default = San Mateo
+
+0.organizationName = Organization Name (eg, company)
+0.organizationName_default = Genius.com Inc
+
+organizationalUnitName = Organizational Unit Name (eg, section)
+organizationalUnitName_default = NetOps
+
+commonName = Common Name (eg, your name or your server\'s hostname)
+commonName_max = 64
+
+emailAddress = Email Address
+emailAddress_max = 64
+
+[ req_attributes ]
+challengePassword = A challenge password
+challengePassword_min = 4
+challengePassword_max = 20
+unstructuredName = An optional company name
+
+[ unsupported_cert ]
+# Just a made-up OID
+1.2.3.4.99999.1.2.3.4 = critical,ASN1:FORMAT:BITLIST,BITSTRING:0,1,2
+
+[ keyUsage_critical_cert ]
+basicConstraints=CA:FALSE
+keyUsage = critical, decipherOnly, keyAgreement
+
+[ keyUsage_extraLong_cert ]
+keyUsage=ASN1:FORMAT:BITLIST,BITSTRING:0,1,2,3,4,5,6,7,8,9,10
+
+[ keyUsage_cert ]
+basicConstraints=CA:FALSE
+keyUsage = encipherOnly, keyEncipherment, dataEncipherment, keyCertSign, cRLSign, cRLSign, keyEncipherment, dataEncipherment, keyCertSign, cRLSign
+
+[ extendedKeyUsage_cert ]
+extendedKeyUsage=1.2.3.4
+
+[ userWithPathLen_cert ]
+basicConstraints=CA:false,pathlen:10
+
+[ ca_cert ]
+basicConstraints=CA:true
+
+[ caWithPathLen_cert ]
+basicConstraints=CA:true,pathlen:10
+
+[ invalid_ip_cert ]
+subjectAltName = ASN1:SEQUENCE:invalid_ip_SEQ
+issuerAltName = ASN1:SEQUENCE:invalid_ip_SEQ
+
+[ invalid_ip_SEQ ]
+IP.1 = IMPLICIT:7,FORMAT:HEX,OCTETSTRING:0A
+
+[ ipv6_cert ]
+subjectAltName = ASN1:SEQUENCE:ipv6_SEQ
+issuerAltName = ASN1:SEQUENCE:ipv6_SEQ
+
+[ ipv6_SEQ ]
+IP.1 = IMPLICIT:7,FORMAT:HEX,OCTETSTRING:20010DB8000000000000FF0000428329
+
+[ usr_cert ]
+basicConstraints=CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer:always
+nsComment = "X.509 Unit Test"
+
+subjectAltName = @alt_names
+issuerAltName = @alt_names
+#subjectAltName = ASN1:SEQUENCE:raw_alt_names
+
+[ alt_names ]
+otherName.0 = 1.2.3.4;UTF8:test1
+email.0 = x509@example.com
+DNS.0 = x509.example.com
+dirName.0 = dir_example
+URI.0 = http://www.example.com/?q=awesomeness
+IP.0 = 192.168.0.1
+RID.0 = 1.2.3.4
+
+[ alt_other_cert ]
+subjectAltName = otherName:1.2.3.4;UTF8:test1
+
+[ alt_email_cert ]
+subjectAltName = email:x509@example.com
+
+[ alt_dns_cert ]
+subjectAltName = DNS:x509.example.com
+
+[ alt_dirname_cert ]
+subjectAltName = dirName:dir_example
+
+[ alt_uri_cert ]
+subjectAltName = URI:http://www.example.com/?q=awesomeness
+
+[ alt_rid_cert ]
+subjectAltName = RID:1.2.3.4
+
+[ raw_alt_names ]
+ediPartyName = IMPLICIT:5,SEQUENCE:ediPartyName_SEQ
+x400 = IMPLICIT:3,SEQUENCE:x400_SEQ
+
+[ x400_SEQ ]
+BuiltInStandardAttributes = SEQUENCE:x400_BuiltInStandardAddtributes_SEQ
+
+[ x400_BuiltInStandardAddtributes_SEQ ]
+PersonalName=IMPLICIT:5,SET:x400_PersonalName_SET
+
+[ x400_PersonalName_SET ]
+Surname=IMPLICIT:0,PRINTABLESTRING:Root
+GivenName=IMPLICIT:1,PRINTABLESTRING:Kenny
+
+[ ediPartyName_SEQ ]
+partyName = IMPLICIT:1,PRINTABLESTRING:Joe
+
+[ dir_example ]
+C=US
+O=Awesome Dudes
+OU=Über Frîends
+CN=example X.509
+CN=∆ƒ
+
+[ v3_req ]
+basicConstraints = CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+subjectAltName = @alt_names
+issuerAltName = @alt_names
+basicConstraints=CA:FALSE
+nsComment = "X.509 Unit Test"
+
+[ v3_ca ]
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid:always,issuer:always
+basicConstraints = CA:true
+
+[ crl_ext ]
+authorityKeyIdentifier=keyid:always,issuer:always