blob: 5ddbed373ecca689e9fa9ea843d926d1008a48ed [file] [log] [blame]
/*
* 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 org.apache.harmony.security.utils.AlgNameMapper;
import tests.support.resource.Support_Resources;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
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.Certificate;
import java.security.cert.CertificateException;
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.ArrayList;
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_ALT_NONE = "x509/cert-alt-none.der";
private static final String CERT_UNSUPPORTED = "x509/cert-unsupported.der";
private static final String CERT_SIGOPT = "x509/cert-sigopt.der";
private static final String CERTS_X509_PEM = "x509/certs.pem";
private static final String CERTS_X509_DER = "x509/certs.der";
private static final String CERTS_PKCS7_PEM = "x509/certs-pk7.pem";
private static final String CERTS_PKCS7_DER = "x509/certs-pk7.der";
/** A list of certs that are all slightly different. */
private static final String[] VARIOUS_CERTS = new String[] {
CERT_RSA, CERT_DSA, CERT_EC,
};
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 final Collection<? extends X509Certificate> getCertificates(CertificateFactory f, String name)
throws Exception {
final InputStream is = Support_Resources.getStream(name);
assertNotNull("File does not exist: " + name, is);
try {
return (Collection<? extends X509Certificate>) f.generateCertificates(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", Locale.US);
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);
getSubjectAlternativeNames_None(f);
getIssuerAlternativeNames(f);
getTBSCertificate(f);
getSignature(f);
hasUnsupportedCriticalExtension(f);
getEncoded(f);
verify(f);
generateCertificate_PEM_TrailingData(f);
generateCertificate_DER_TrailingData(f);
generateCertificates_X509_PEM(f);
generateCertificates_X509_DER(f);
generateCertificates_PKCS7_PEM(f);
generateCertificates_PKCS7_DER(f);
generateCertificates_Empty(f);
generateCertificates_X509_PEM_TrailingData(f);
generateCertificates_X509_DER_TrailingData(f);
generateCertificates_PKCS7_PEM_TrailingData(f);
generateCertificates_PKCS7_DER_TrailingData(f);
test_Serialization(f);
test_UnknownUnmappedKeyOID(f);
test_UnknownMappedKeyOID(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);
// RI appears to disagree
if (StandardNames.IS_RI) {
assertNull(f.getProvider().getName(), c.getSigAlgParams());
} else {
assertNotNull(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());
}
{
X509Certificate c = getCertificate(f, CERT_SIGOPT);
/* SEQUENCE, INTEGER 1 */
final byte[] expected = new byte[] {
/* SEQUENCE, constructed, len=5 */
(byte) 0x30, (byte) 0x05,
/* Type=2, constructed, context-specific, len=3 */
(byte) 0xA2, (byte) 0x03,
/* INTEGER, len=1, value=1 */
(byte) 0x02, (byte) 0x01, (byte) 0x01,
};
final byte[] params = c.getSigAlgParams();
assertNotNull(f.getProvider().getName(), params);
assertEquals(Arrays.toString(expected), Arrays.toString(params));
}
}
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 = c.getSubjectAlternativeNames();
checkAlternativeNames(f, col);
}
private void checkAlternativeNames(CertificateFactory f, Collection<List<?>> col) throws Exception {
assertNotNull(col);
/* Check to see that the Collection is unmodifiable. */
{
try {
col.add(new ArrayList<Object>());
fail("should be an unmodifiable list");
} catch (UnsupportedOperationException expected) {
}
}
/*
* There should be 9 types of alternative names in this test
* certificate.
*/
boolean[] typesFound = new boolean[9];
for (List<?> item : col) {
/* Check to see that the List is unmodifiable. */
{
try {
item.remove(0);
fail("should be an unmodifiable list");
} catch (UnsupportedOperationException expected) {
}
}
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 */
if ("BC".equals(f.getProvider().getName())) {
// Bouncycastle doesn't parse T61String as UTF-8 like the RI, libcore, or OpenSSL.
byte[] bytes = "CN=∆ƒ,OU=Über Frîends,O=Awesome Dudes,C=US".getBytes("UTF-8");
String string = new String(bytes, 0);
assertEquals(string, (String) item.get(1));
} else {
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();
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 = c.getSubjectAlternativeNames();
assertNull(col);
}
private void getSubjectAlternativeNames_Other(CertificateFactory f) throws Exception {
X509Certificate c = getCertificate(f, CERT_ALT_OTHER);
Collection<List<?>> col = c.getSubjectAlternativeNames();
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();
final byte[] actual = (byte[]) item.get(1);
assertEquals(Arrays.toString(der), Arrays.toString(actual));
/* Make sure the byte[] array isn't modified by our test. */
{
actual[0] ^= (byte) 0xFF;
byte[] actual2 = (byte[]) c.getSubjectAlternativeNames().iterator().next().get(1);
if (!StandardNames.IS_RI) {
assertEquals(Arrays.toString(der), Arrays.toString(actual2));
} else {
/* RI is broken here. */
assertEquals(Arrays.toString(actual), Arrays.toString(actual2));
}
}
}
private void getSubjectAlternativeNames_Email(CertificateFactory f) throws Exception {
X509Certificate c = getCertificate(f, CERT_ALT_EMAIL);
Collection<List<?>> col = c.getSubjectAlternativeNames();
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();
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 = c.getSubjectAlternativeNames();
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);
if ("BC".equals(f.getProvider().getName())) {
// Bouncycastle doesn't parse T61String as UTF-8 like the RI, libcore, or OpenSSL.
byte[] bytes = "CN=∆ƒ,OU=Über Frîends,O=Awesome Dudes,C=US".getBytes("UTF-8");
String string = new String(bytes, 0);
assertEquals(string, (String) item.get(1));
} else {
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();
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();
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 getSubjectAlternativeNames_None(CertificateFactory f) throws Exception {
X509Certificate c = getCertificate(f, CERT_ALT_NONE);
Collection<List<?>> col = c.getSubjectAlternativeNames();
assertNull(col);
}
private void getIssuerAlternativeNames(CertificateFactory f) throws Exception {
X509Certificate c = getCertificate(f, CERT_RSA);
Collection<List<?>> col = c.getIssuerAlternativeNames();
checkAlternativeNames(f, 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()));
}
private void generateCertificate_PEM_TrailingData(CertificateFactory f) throws Exception {
byte[] certsBytes = getResourceAsBytes(CERTS_X509_PEM);
byte[] certsTwice = new byte[certsBytes.length * 2];
System.arraycopy(certsBytes, 0, certsTwice, 0, certsBytes.length);
System.arraycopy(certsBytes, 0, certsTwice, certsBytes.length, certsBytes.length);
ByteArrayInputStream bais = new ByteArrayInputStream(certsTwice);
assertEquals(certsBytes.length * 2, bais.available());
X509Certificate cert1 = (X509Certificate) f.generateCertificate(bais);
// TODO: If we had a single PEM certificate, we could know exact bytes.
assertTrue(certsBytes.length < bais.available());
}
private void generateCertificate_DER_TrailingData(CertificateFactory f) throws Exception {
byte[] cert1Bytes = getResourceAsBytes(CERT_RSA);
byte[] cert1WithTrailing = new byte[cert1Bytes.length * 2];
System.arraycopy(cert1Bytes, 0, cert1WithTrailing, 0, cert1Bytes.length);
System.arraycopy(cert1Bytes, 0, cert1WithTrailing, cert1Bytes.length, cert1Bytes.length);
ByteArrayInputStream bais = new ByteArrayInputStream(cert1WithTrailing);
assertEquals(cert1Bytes.length * 2, bais.available());
X509Certificate cert1 = (X509Certificate) f.generateCertificate(bais);
assertEquals(cert1Bytes.length, bais.available());
}
private void generateCertificates_X509_DER(CertificateFactory f) throws Exception {
/* DER-encoded list of certificates */
Collection<? extends X509Certificate> certs = getCertificates(f, CERTS_X509_DER);
assertNotNull(certs);
assertEquals(2, certs.size());
}
private void generateCertificates_X509_PEM(CertificateFactory f) throws Exception {
/* PEM-encoded list of certificates */
Collection<? extends X509Certificate> certs = getCertificates(f, CERTS_X509_PEM);
assertNotNull(certs);
assertEquals(2, certs.size());
}
private void generateCertificates_PKCS7_PEM(CertificateFactory f) throws Exception {
/* PEM-encoded PKCS7 bag of certificates */
Collection<? extends X509Certificate> certs = getCertificates(f, CERTS_PKCS7_PEM);
assertNotNull(certs);
if ("BC".equals(f.getProvider().getName())) {
// Bouncycastle is broken
assertEquals(0, certs.size());
} else {
assertEquals(2, certs.size());
}
}
private void generateCertificates_PKCS7_DER(CertificateFactory f) throws Exception {
/* DER-encoded PKCS7 bag of certificates */
Collection<? extends X509Certificate> certs = getCertificates(f, CERTS_PKCS7_DER);
assertNotNull(certs);
assertEquals(2, certs.size());
}
private void generateCertificates_Empty(CertificateFactory f) throws Exception {
final InputStream is = new ByteArrayInputStream(new byte[0]);
final Collection<? extends Certificate> certs = f.generateCertificates(is);
assertNotNull(certs);
assertEquals(0, certs.size());
}
private void generateCertificates_X509_PEM_TrailingData(CertificateFactory f) throws Exception {
byte[] certBytes = getResourceAsBytes(CERTS_X509_PEM);
byte[] certsPlusExtra = new byte[certBytes.length + 4096];
System.arraycopy(certBytes, 0, certsPlusExtra, 0, certBytes.length);
ByteArrayInputStream bais = new ByteArrayInputStream(certsPlusExtra);
assertEquals(certsPlusExtra.length, bais.available());
// RI is broken
try {
Collection<? extends X509Certificate> certs = (Collection<? extends X509Certificate>)
f.generateCertificates(bais);
if (StandardNames.IS_RI) {
return;
}
} catch (CertificateParsingException e) {
if (StandardNames.IS_RI) {
return;
}
throw e;
}
// Bouncycastle is broken
if ("BC".equals(f.getProvider().getName())) {
assertEquals(0, bais.available());
} else {
assertEquals(4096, bais.available());
}
}
private void generateCertificates_X509_DER_TrailingData(CertificateFactory f) throws Exception {
byte[] certBytes = getResourceAsBytes(CERTS_X509_DER);
byte[] certsPlusExtra = new byte[certBytes.length + 4096];
System.arraycopy(certBytes, 0, certsPlusExtra, 0, certBytes.length);
ByteArrayInputStream bais = new ByteArrayInputStream(certsPlusExtra);
assertEquals(certsPlusExtra.length, bais.available());
// RI is broken
try {
Collection<? extends X509Certificate> certs = (Collection<? extends X509Certificate>)
f.generateCertificates(bais);
if (StandardNames.IS_RI) {
return;
}
} catch (CertificateParsingException e) {
if (StandardNames.IS_RI) {
return;
}
throw e;
}
// Bouncycastle is broken
if ("BC".equals(f.getProvider().getName())) {
assertEquals(0, bais.available());
} else {
assertEquals(4096, bais.available());
}
}
private void generateCertificates_PKCS7_PEM_TrailingData(CertificateFactory f) throws Exception {
byte[] certBytes = getResourceAsBytes(CERTS_PKCS7_PEM);
byte[] certsPlusExtra = new byte[certBytes.length + 4096];
System.arraycopy(certBytes, 0, certsPlusExtra, 0, certBytes.length);
ByteArrayInputStream bais = new ByteArrayInputStream(certsPlusExtra);
assertEquals(certsPlusExtra.length, bais.available());
Collection<? extends X509Certificate> certs = (Collection<? extends X509Certificate>)
f.generateCertificates(bais);
// Bouncycastle is broken
if ("BC".equals(f.getProvider().getName())) {
assertEquals(0, bais.available());
} else {
assertEquals(4096, bais.available());
}
}
private void generateCertificates_PKCS7_DER_TrailingData(CertificateFactory f) throws Exception {
byte[] certBytes = getResourceAsBytes(CERTS_PKCS7_DER);
byte[] certsPlusExtra = new byte[certBytes.length + 4096];
System.arraycopy(certBytes, 0, certsPlusExtra, 0, certBytes.length);
ByteArrayInputStream bais = new ByteArrayInputStream(certsPlusExtra);
assertEquals(certsPlusExtra.length, bais.available());
Collection<? extends X509Certificate> certs = (Collection<? extends X509Certificate>)
f.generateCertificates(bais);
assertEquals(4096, bais.available());
}
private void test_Serialization(CertificateFactory f) throws Exception {
for (String certName : VARIOUS_CERTS) {
X509Certificate expected = getCertificate(f, certName);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
try {
oos.writeObject(expected);
} finally {
oos.close();
}
byte[] certBytes = baos.toByteArray();
ByteArrayInputStream bais = new ByteArrayInputStream(certBytes);
try {
ObjectInputStream ois = new ObjectInputStream(bais);
X509Certificate actual = (X509Certificate) ois.readObject();
assertEquals(certName, expected, actual);
} finally {
bais.close();
}
}
}
private void test_UnknownUnmappedKeyOID(CertificateFactory f) throws Exception {
byte[] certBytes = generateFakeOidCertificate();
{
X509Certificate cert = (X509Certificate) f
.generateCertificate(new ByteArrayInputStream(certBytes));
assertEquals(FakeOidProvider.SIGALG_OID, cert.getSigAlgOID());
assertEquals(FakeOidProvider.SIGALG_OID, cert.getSigAlgName());
}
}
private void test_UnknownMappedKeyOID(CertificateFactory f) throws Exception {
AlgNameMapper.addMapping(FakeOidProvider.SIGALG_OID, FakeOidProvider.SIGALG_OID_NAME);
Security.addProvider(new FakeOidProvider());
try {
byte[] certBytes = generateFakeOidCertificate();
// Make sure the certificate is outputting something.
X509Certificate cert = (X509Certificate) f
.generateCertificate(new ByteArrayInputStream(certBytes));
assertEquals(FakeOidProvider.SIGALG_OID, cert.getSigAlgOID());
if ("AndroidOpenSSL".equals(f.getProvider().getName())) {
// AndroidOpenSSL provider has a connection to AlgNameMapper, so
// we expect it to get our special name.
assertEquals(FakeOidProvider.SIGALG_OID_NAME, cert.getSigAlgName());
} else {
assertNotNull(cert.getSigAlgName());
}
cert.verify(cert.getPublicKey());
} finally {
AlgNameMapper
.removeMapping(FakeOidProvider.SIGALG_OID, FakeOidProvider.SIGALG_OID_NAME);
Security.removeProvider(FakeOidProvider.PROVIDER_NAME);
}
}
private byte[] generateFakeOidCertificate() throws IOException {
byte[] certBytes;
// Read in the original cert.
{
InputStream is = null;
try {
is = Support_Resources.getStream(CERT_RSA);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[2048];
int numRead;
while ((numRead = is.read(buffer, 0, buffer.length)) != -1) {
baos.write(buffer, 0, numRead);
}
certBytes = baos.toByteArray();
} finally {
if (is != null) {
try {
is.close();
} catch (IOException ignored) {
}
}
}
}
// Fix the OID for the certificate.
{
int numFixed = 0;
for (int i = 0; i < certBytes.length - 5; i++) {
if (certBytes[i] == (byte) 0x2A && certBytes[i + 1] == (byte) 0x86
&& certBytes[i + 2] == (byte) 0x48 && certBytes[i + 3] == (byte) 0x86
&& certBytes[i + 4] == (byte) 0xF7) {
certBytes[i + 1] = (byte) 0xFF;
certBytes[i + 2] = (byte) 0xFF;
certBytes[i + 3] = (byte) 0xFF;
i += 4;
numFixed++;
}
}
assertEquals(3, numFixed);
}
return certBytes;
}
@Override
protected void setUp() throws Exception {
super.setUp();
mX509Providers = Security.getProviders("CertificateFactory.X509");
}
}