am d910cf33: Merge "Fix DefaultHostnameVerifierTest failure."
* commit 'd910cf339d61153d66f9a3d835eff027791ad78c':
Fix DefaultHostnameVerifierTest failure.
diff --git a/expectations/knownfailures.txt b/expectations/knownfailures.txt
index e1d54bc..622d401 100644
--- a/expectations/knownfailures.txt
+++ b/expectations/knownfailures.txt
@@ -1456,6 +1456,7 @@
"com.squareup.okhttp.internal.spdy.SpdyConnectionTest",
"com.squareup.okhttp.internal.http.HttpOverHttp20Draft09Test",
"com.squareup.okhttp.internal.http.HttpOverSpdy3Test",
+ "com.squareup.okhttp.internal.http.ResponseCacheAdapterTest",
"com.squareup.okhttp.internal.http.URLConnectionTest#npnSetsProtocolHeader_SPDY_3",
"com.squareup.okhttp.internal.http.URLConnectionTest#npnSetsProtocolHeader_HTTP_2",
"com.squareup.okhttp.internal.http.URLConnectionTest#zeroLengthPost_SPDY_3",
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/jar/JarFileTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/jar/JarFileTest.java
index f55829d..0bc8920 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/jar/JarFileTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/jar/JarFileTest.java
@@ -37,7 +37,14 @@
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Enumeration;
+import java.util.List;
import java.util.Vector;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
@@ -97,6 +104,27 @@
private final String emptyEntryJar = "EmptyEntries_signed.jar";
+ /*
+ * /usr/bin/openssl genrsa 2048 > root1.pem
+ * /usr/bin/openssl req -new -key root1.pem -out root1.csr -subj '/CN=root1'
+ * /usr/bin/openssl x509 -req -days 3650 -in root1.csr -signkey root1.pem -out root1.crt
+ * /usr/bin/openssl genrsa 2048 > root2.pem
+ * /usr/bin/openssl req -new -key root2.pem -out root2.csr -subj '/CN=root2'
+ * echo 4000 > root1.srl
+ * echo 8000 > root2.srl
+ * /usr/bin/openssl x509 -req -days 3650 -in root2.csr -CA root1.crt -CAkey root1.pem -out root2.crt
+ * /usr/bin/openssl x509 -req -days 3650 -in root1.csr -CA root2.crt -CAkey root2.pem -out root1.crt
+ * /usr/bin/openssl genrsa 2048 > signer.pem
+ * /usr/bin/openssl req -new -key signer.pem -out signer.csr -subj '/CN=signer'
+ * /usr/bin/openssl x509 -req -days 3650 -in signer.csr -CA root1.crt -CAkey root1.pem -out signer.crt
+ * /usr/bin/openssl pkcs12 -inkey signer.pem -in signer.crt -export -out signer.p12 -name signer -passout pass:certloop
+ * keytool -importkeystore -srckeystore signer.p12 -srcstoretype PKCS12 -destkeystore signer.jks -srcstorepass certloop -deststorepass certloop
+ * cat signer.crt root1.crt root2.crt > chain.crt
+ * zip -d hyts_certLoop.jar 'META-INF/*'
+ * jarsigner -keystore signer.jks -certchain chain.crt -storepass certloop hyts_certLoop.jar signer
+ */
+ private final String certLoopJar = "hyts_certLoop.jar";
+
private final String emptyEntry1 = "subfolder/internalSubset01.js";
private final String emptyEntry2 = "svgtest.js";
@@ -616,6 +644,9 @@
// JAR with a signature that has PKCS#7 Authenticated Attributes
checkSignedJar(authAttrsJar);
+
+ // JAR with certificates that loop
+ checkSignedJar(certLoopJar, 3);
}
/**
@@ -628,29 +659,52 @@
checkSignedJar(jarName9);
}
+ /**
+ * Checks that a JAR is signed correctly with a signature length of 1.
+ */
private void checkSignedJar(String jarName) throws Exception {
+ checkSignedJar(jarName, 1);
+ }
+
+ /**
+ * Checks that a JAR is signed correctly with a signature length of sigLength.
+ */
+ private void checkSignedJar(String jarName, final int sigLength) throws Exception {
Support_Resources.copyFile(resources, null, jarName);
- File file = new File(resources, jarName);
- boolean foundCerts = false;
+ final File file = new File(resources, jarName);
- JarFile jarFile = new JarFile(file, true);
- try {
-
- Enumeration<JarEntry> e = jarFile.entries();
- while (e.hasMoreElements()) {
- JarEntry entry = e.nextElement();
- InputStream is = jarFile.getInputStream(entry);
- is.skip(100000);
- is.close();
- Certificate[] certs = entry.getCertificates();
- if (certs != null && certs.length > 0) {
- foundCerts = true;
- break;
+ ExecutorService executor = Executors.newSingleThreadExecutor();
+ Future<Boolean> future = executor.submit(new Callable<Boolean>() {
+ @Override
+ public Boolean call() throws Exception {
+ JarFile jarFile = new JarFile(file, true);
+ try {
+ Enumeration<JarEntry> e = jarFile.entries();
+ while (e.hasMoreElements()) {
+ JarEntry entry = e.nextElement();
+ InputStream is = jarFile.getInputStream(entry);
+ is.skip(100000);
+ is.close();
+ Certificate[] certs = entry.getCertificates();
+ if (certs != null && certs.length > 0) {
+ assertEquals(sigLength, certs.length);
+ return true;
+ }
+ }
+ return false;
+ } finally {
+ jarFile.close();
}
}
- } finally {
- jarFile.close();
+ });
+ executor.shutdown();
+ final boolean foundCerts;
+ try {
+ foundCerts = future.get(10, TimeUnit.SECONDS);
+ } catch (TimeoutException e) {
+ fail("Could not finish building chain; possibly confused by loops");
+ return; // Not actually reached.
}
assertTrue(
diff --git a/luni/src/main/java/java/util/Locale.java b/luni/src/main/java/java/util/Locale.java
index e509a6e..a6368e8 100644
--- a/luni/src/main/java/java/util/Locale.java
+++ b/luni/src/main/java/java/util/Locale.java
@@ -95,7 +95,7 @@
* <td><a href="http://site.icu-project.org/download/51">ICU 51</a></td>
* <td><a href="http://cldr.unicode.org/index/downloads/cldr-23">CLDR 23</a></td>
* <td><a href="http://www.unicode.org/versions/Unicode6.2.0/">Unicode 6.2</a></td></tr>
- * <tr><td>Android 4.? (STOPSHIP)</td>
+ * <tr><td>Android 5.0 (Lollipop)</td>
* <td><a href="http://site.icu-project.org/download/53">ICU 53</a></td>
* <td><a href="http://cldr.unicode.org/index/downloads/cldr-25">CLDR 25</a></td>
* <td><a href="http://www.unicode.org/versions/Unicode6.3.0/">Unicode 6.3</a></td></tr>
diff --git a/luni/src/main/java/org/apache/harmony/security/utils/JarUtils.java b/luni/src/main/java/org/apache/harmony/security/utils/JarUtils.java
index 33584d8..020663e 100644
--- a/luni/src/main/java/org/apache/harmony/security/utils/JarUtils.java
+++ b/luni/src/main/java/org/apache/harmony/security/utils/JarUtils.java
@@ -31,6 +31,7 @@
import java.security.Principal;
import java.security.Signature;
import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
@@ -82,8 +83,10 @@
CertificateFactory cf = CertificateFactory.getInstance("X.509");
int i = 0;
for (org.apache.harmony.security.x509.Certificate encCert : encCerts) {
- final InputStream is = new ByteArrayInputStream(encCert.getEncoded());
- certs[i++] = (X509Certificate) cf.generateCertificate(is);
+ final byte[] encoded = encCert.getEncoded();
+ final InputStream is = new ByteArrayInputStream(encoded);
+ certs[i++] = new VerbatimX509Certificate((X509Certificate) cf.generateCertificate(is),
+ encoded);
}
List<SignerInfo> sigInfos = signedData.getSignerInfos();
@@ -246,6 +249,10 @@
}
chain.add(issuerCert);
count++;
+ /* Prevent growing infinitely if there is a loop */
+ if (count > candidates.length) {
+ break;
+ }
issuer = issuerCert.getIssuerDN();
if (issuerCert.getSubjectDN().equals(issuer)) {
break;
@@ -263,4 +270,22 @@
return null;
}
+ /**
+ * For legacy reasons we need to return exactly the original encoded
+ * certificate bytes, instead of letting the underlying implementation have
+ * a shot at re-encoding the data.
+ */
+ private static class VerbatimX509Certificate extends WrappedX509Certificate {
+ private byte[] encodedVerbatim;
+
+ public VerbatimX509Certificate(X509Certificate wrapped, byte[] encodedVerbatim) {
+ super(wrapped);
+ this.encodedVerbatim = encodedVerbatim;
+ }
+
+ @Override
+ public byte[] getEncoded() throws CertificateEncodingException {
+ return encodedVerbatim;
+ }
+ }
}
diff --git a/luni/src/main/java/org/apache/harmony/security/utils/WrappedX509Certificate.java b/luni/src/main/java/org/apache/harmony/security/utils/WrappedX509Certificate.java
new file mode 100644
index 0000000..2b09309
--- /dev/null
+++ b/luni/src/main/java/org/apache/harmony/security/utils/WrappedX509Certificate.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2014 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 org.apache.harmony.security.utils;
+
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Principal;
+import java.security.PublicKey;
+import java.security.SignatureException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+import java.util.Set;
+
+public class WrappedX509Certificate extends X509Certificate {
+ private final X509Certificate wrapped;
+
+ public WrappedX509Certificate(X509Certificate wrapped) {
+ this.wrapped = wrapped;
+ }
+
+ @Override
+ public Set<String> getCriticalExtensionOIDs() {
+ return wrapped.getCriticalExtensionOIDs();
+ }
+
+ @Override
+ public byte[] getExtensionValue(String oid) {
+ return wrapped.getExtensionValue(oid);
+ }
+
+ @Override
+ public Set<String> getNonCriticalExtensionOIDs() {
+ return wrapped.getNonCriticalExtensionOIDs();
+ }
+
+ @Override
+ public boolean hasUnsupportedCriticalExtension() {
+ return wrapped.hasUnsupportedCriticalExtension();
+ }
+
+ @Override
+ public void checkValidity() throws CertificateExpiredException,
+ CertificateNotYetValidException {
+ wrapped.checkValidity();
+ }
+
+ @Override
+ public void checkValidity(Date date) throws CertificateExpiredException,
+ CertificateNotYetValidException {
+ wrapped.checkValidity(date);
+ }
+
+ @Override
+ public int getVersion() {
+ return wrapped.getVersion();
+ }
+
+ @Override
+ public BigInteger getSerialNumber() {
+ return wrapped.getSerialNumber();
+ }
+
+ @Override
+ public Principal getIssuerDN() {
+ return wrapped.getIssuerDN();
+ }
+
+ @Override
+ public Principal getSubjectDN() {
+ return wrapped.getSubjectDN();
+ }
+
+ @Override
+ public Date getNotBefore() {
+ return wrapped.getNotBefore();
+ }
+
+ @Override
+ public Date getNotAfter() {
+ return wrapped.getNotAfter();
+ }
+
+ @Override
+ public byte[] getTBSCertificate() throws CertificateEncodingException {
+ return wrapped.getTBSCertificate();
+ }
+
+ @Override
+ public byte[] getSignature() {
+ return wrapped.getSignature();
+ }
+
+ @Override
+ public String getSigAlgName() {
+ return wrapped.getSigAlgName();
+ }
+
+ @Override
+ public String getSigAlgOID() {
+ return wrapped.getSigAlgOID();
+ }
+
+ @Override
+ public byte[] getSigAlgParams() {
+ return wrapped.getSigAlgParams();
+ }
+
+ @Override
+ public boolean[] getIssuerUniqueID() {
+ return wrapped.getIssuerUniqueID();
+ }
+
+ @Override
+ public boolean[] getSubjectUniqueID() {
+ return wrapped.getSubjectUniqueID();
+ }
+
+ @Override
+ public boolean[] getKeyUsage() {
+ return wrapped.getKeyUsage();
+ }
+
+ @Override
+ public int getBasicConstraints() {
+ return wrapped.getBasicConstraints();
+ }
+
+ @Override
+ public byte[] getEncoded() throws CertificateEncodingException {
+ return wrapped.getEncoded();
+ }
+
+ @Override
+ public void verify(PublicKey key) throws CertificateException, NoSuchAlgorithmException,
+ InvalidKeyException, NoSuchProviderException, SignatureException {
+ wrapped.verify(key);
+ }
+
+ @Override
+ public void verify(PublicKey key, String sigProvider) throws CertificateException,
+ NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException,
+ SignatureException {
+ verify(key, sigProvider);
+ }
+
+ @Override
+ public String toString() {
+ return wrapped.toString();
+ }
+
+ @Override
+ public PublicKey getPublicKey() {
+ return wrapped.getPublicKey();
+ }
+}
diff --git a/luni/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderImpl.java b/luni/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderImpl.java
index d1079c8..040a012 100644
--- a/luni/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderImpl.java
+++ b/luni/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderImpl.java
@@ -416,11 +416,13 @@
private String resolveCharacterReference(String value, int base) {
try {
- int ch = Integer.parseInt(value, base);
- if (ch < 0 || ch > Character.MAX_VALUE) {
- return null;
+ int codePoint = Integer.parseInt(value, base);
+ if (Character.isBmpCodePoint(codePoint)) {
+ return String.valueOf((char) codePoint);
+ } else {
+ char[] surrogatePair = Character.toChars(codePoint);
+ return new String(surrogatePair);
}
- return String.valueOf((char) ch);
} catch (NumberFormatException ex) {
return null;
}
diff --git a/luni/src/test/java/libcore/icu/DateIntervalFormatTest.java b/luni/src/test/java/libcore/icu/DateIntervalFormatTest.java
index bac8138..1c1296b 100644
--- a/luni/src/test/java/libcore/icu/DateIntervalFormatTest.java
+++ b/luni/src/test/java/libcore/icu/DateIntervalFormatTest.java
@@ -408,4 +408,16 @@
assertEquals("یکشنبه د ۱۹۸۰ د فبروري ۱۰", formatDateRange(new Locale("ps"), utc, thisYear, thisYear, flags));
assertEquals("วันอาทิตย์ 10 กุมภาพันธ์ 1980", formatDateRange(new Locale("th"), utc, thisYear, thisYear, flags));
}
+
+ // http://b/13234532
+ public void test13234532() throws Exception {
+ Locale l = Locale.US;
+ TimeZone utc = TimeZone.getTimeZone("UTC");
+
+ int flags = FORMAT_SHOW_TIME | FORMAT_ABBREV_ALL | FORMAT_12HOUR;
+
+ assertEquals("10 – 11 AM", formatDateRange(l, utc, 10*HOUR, 11*HOUR, flags));
+ assertEquals("11 AM – 1 PM", formatDateRange(l, utc, 11*HOUR, 13*HOUR, flags));
+ assertEquals("2 – 3 PM", formatDateRange(l, utc, 14*HOUR, 15*HOUR, flags));
+ }
}
diff --git a/luni/src/test/java/libcore/xml/KxmlSerializerTest.java b/luni/src/test/java/libcore/xml/KxmlSerializerTest.java
index 6a75a9b..5f68a99 100644
--- a/luni/src/test/java/libcore/xml/KxmlSerializerTest.java
+++ b/luni/src/test/java/libcore/xml/KxmlSerializerTest.java
@@ -22,7 +22,9 @@
import junit.framework.TestCase;
import org.kxml2.io.KXmlSerializer;
import org.w3c.dom.Document;
+import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
+import org.w3c.dom.Text;
import org.xmlpull.v1.XmlSerializer;
import static tests.support.Support_Xml.domOf;
@@ -87,12 +89,67 @@
return serializer;
}
+ public String fromCodePoint(int codePoint) {
+ if (codePoint > Character.MAX_VALUE) {
+ return new String(Character.toChars(codePoint));
+ }
+ return Character.toString((char) codePoint);
+ }
+
+ // http://b/17960630
+ public void testSpeakNoEvilMonkeys() throws Exception {
+ StringWriter stringWriter = new StringWriter();
+ XmlSerializer serializer = new KXmlSerializer();
+ serializer.setOutput(stringWriter);
+ serializer.startDocument("UTF-8", null);
+ serializer.startTag(NAMESPACE, "tag");
+ serializer.attribute(NAMESPACE, "attr", "a\ud83d\ude4ab");
+ serializer.text("c\ud83d\ude4ad");
+ serializer.cdsect("e\ud83d\ude4af");
+ serializer.endTag(NAMESPACE, "tag");
+ serializer.endDocument();
+ assertXmlEquals("<tag attr=\"a🙊b\">" +
+ "c🙊d" +
+ "<![CDATA[e]]>🙊<![CDATA[f]]>" +
+ "</tag>", stringWriter.toString());
+
+ // Check we can parse what we just output.
+ Document doc = domOf(stringWriter.toString());
+ Node root = doc.getDocumentElement();
+ assertEquals("a\ud83d\ude4ab", root.getAttributes().getNamedItem("attr").getNodeValue());
+ Text text = (Text) root.getFirstChild();
+ assertEquals("c\ud83d\ude4ade\ud83d\ude4af", text.getNodeValue());
+ }
+
+ public void testBadSurrogates() throws Exception {
+ StringWriter stringWriter = new StringWriter();
+ XmlSerializer serializer = new KXmlSerializer();
+ serializer.setOutput(stringWriter);
+ serializer.startDocument("UTF-8", null);
+ serializer.startTag(NAMESPACE, "tag");
+ try {
+ serializer.attribute(NAMESPACE, "attr", "a\ud83d\u0040b");
+ } catch (IllegalArgumentException expected) {
+ }
+ try {
+ serializer.text("c\ud83d\u0040d");
+ } catch (IllegalArgumentException expected) {
+ }
+ try {
+ serializer.cdsect("e\ud83d\u0040f");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ // Cover all the BMP code points plus a few that require us to use surrogates.
+ private static int MAX_TEST_CODE_POINT = 0x10008;
+
public void testInvalidCharactersInText() throws IOException {
XmlSerializer serializer = newSerializer();
serializer.startTag(NAMESPACE, "root");
- for (int ch = 0; ch <= 0xffff; ++ch) {
- final String s = Character.toString((char) ch);
- if (isValidXmlCodePoint(ch)) {
+ for (int c = 0; c <= MAX_TEST_CODE_POINT; ++c) {
+ final String s = fromCodePoint(c);
+ if (isValidXmlCodePoint(c)) {
serializer.text("a" + s + "b");
} else {
try {
@@ -108,9 +165,9 @@
public void testInvalidCharactersInAttributeValues() throws IOException {
XmlSerializer serializer = newSerializer();
serializer.startTag(NAMESPACE, "root");
- for (int ch = 0; ch <= 0xffff; ++ch) {
- final String s = Character.toString((char) ch);
- if (isValidXmlCodePoint(ch)) {
+ for (int c = 0; c <= MAX_TEST_CODE_POINT; ++c) {
+ final String s = fromCodePoint(c);
+ if (isValidXmlCodePoint(c)) {
serializer.attribute(NAMESPACE, "a", "a" + s + "b");
} else {
try {
@@ -126,9 +183,9 @@
public void testInvalidCharactersInCdataSections() throws IOException {
XmlSerializer serializer = newSerializer();
serializer.startTag(NAMESPACE, "root");
- for (int ch = 0; ch <= 0xffff; ++ch) {
- final String s = Character.toString((char) ch);
- if (isValidXmlCodePoint(ch)) {
+ for (int c = 0; c <= MAX_TEST_CODE_POINT; ++c) {
+ final String s = fromCodePoint(c);
+ if (isValidXmlCodePoint(c)) {
serializer.cdsect("a" + s + "b");
} else {
try {
diff --git a/support/src/test/java/libcore/java/security/TestKeyStore.java b/support/src/test/java/libcore/java/security/TestKeyStore.java
index 203c028..bd64360 100644
--- a/support/src/test/java/libcore/java/security/TestKeyStore.java
+++ b/support/src/test/java/libcore/java/security/TestKeyStore.java
@@ -47,6 +47,7 @@
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
+import java.security.UnrecoverableEntryException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
@@ -260,6 +261,7 @@
private X500Principal subject;
private int keyUsage;
private boolean ca;
+ private PrivateKeyEntry privateEntry;
private PrivateKeyEntry signer;
private Certificate rootCa;
private final List<KeyPurposeId> extendedKeyUsages = new ArrayList<KeyPurposeId>();
@@ -314,6 +316,12 @@
return this;
}
+ /** a private key entry to use for the generation of the certificate */
+ public Builder privateEntry(PrivateKeyEntry privateEntry) {
+ this.privateEntry = privateEntry;
+ return this;
+ }
+
/** a private key entry to be used for signing, otherwise self-sign */
public Builder signer(PrivateKeyEntry signer) {
this.signer = signer;
@@ -368,21 +376,32 @@
}
}
+ /*
+ * This is not implemented for other key types because the logic
+ * would be long to write and it's not needed currently.
+ */
+ if (privateEntry != null
+ && (keyAlgorithms.length != 1 || !"RSA".equals(keyAlgorithms[0]))) {
+ throw new IllegalStateException(
+ "Only reusing an existing key is implemented for RSA");
+ }
+
KeyStore keyStore = createKeyStore();
for (String keyAlgorithm : keyAlgorithms) {
String publicAlias = aliasPrefix + "-public-" + keyAlgorithm;
String privateAlias = aliasPrefix + "-private-" + keyAlgorithm;
if ((keyAlgorithm.equals("EC_RSA") || keyAlgorithm.equals("DH_RSA"))
&& signer == null && rootCa == null) {
- createKeys(keyStore, keyAlgorithm, publicAlias, privateAlias,
- privateKey(keyStore, keyPassword, "RSA", "RSA"));
+ createKeys(keyStore, keyAlgorithm, publicAlias, privateAlias, null,
+ privateKey(keyStore, keyPassword, "RSA", "RSA"));
continue;
} else if (keyAlgorithm.equals("DH_DSA") && signer == null && rootCa == null) {
- createKeys(keyStore, keyAlgorithm, publicAlias, privateAlias,
+ createKeys(keyStore, keyAlgorithm, publicAlias, privateAlias, null,
privateKey(keyStore, keyPassword, "DSA", "DSA"));
continue;
}
- createKeys(keyStore, keyAlgorithm, publicAlias, privateAlias, signer);
+ createKeys(keyStore, keyAlgorithm, publicAlias, privateAlias, privateEntry,
+ signer);
}
if (rootCa != null) {
keyStore.setCertificateEntry(aliasPrefix
@@ -416,6 +435,7 @@
String keyAlgorithm,
String publicAlias,
String privateAlias,
+ PrivateKeyEntry privateEntry,
PrivateKeyEntry signer) throws Exception {
PrivateKey caKey;
X509Certificate caCert;
@@ -430,42 +450,51 @@
caCertChain = (X509Certificate[])signer.getCertificateChain();
}
- PrivateKey privateKey;
+ final PrivateKey privateKey;
+ final PublicKey publicKey;
X509Certificate x509c;
if (publicAlias == null && privateAlias == null) {
// don't want anything apparently
privateKey = null;
+ publicKey = null;
x509c = null;
} else {
- // 1.) we make the keys
- int keySize;
- if (keyAlgorithm.equals("RSA")) {
- // 512 breaks SSL_RSA_EXPORT_* on RI and TLS_ECDHE_RSA_WITH_RC4_128_SHA for us
- keySize = 1024;
- } else if (keyAlgorithm.equals("DH_RSA")) {
- keySize = 512;
- keyAlgorithm = "DH";
- } else if (keyAlgorithm.equals("DSA")) {
- keySize = 512;
- } else if (keyAlgorithm.equals("DH_DSA")) {
- keySize = 512;
- keyAlgorithm = "DH";
- } else if (keyAlgorithm.equals("EC")) {
- keySize = 256;
- } else if (keyAlgorithm.equals("EC_RSA")) {
- keySize = 256;
- keyAlgorithm = "EC";
+ if (privateEntry == null) {
+ // 1a.) we make the keys
+ int keySize;
+ if (keyAlgorithm.equals("RSA")) {
+ // 512 breaks SSL_RSA_EXPORT_* on RI and
+ // TLS_ECDHE_RSA_WITH_RC4_128_SHA for us
+ keySize = 1024;
+ } else if (keyAlgorithm.equals("DH_RSA")) {
+ keySize = 512;
+ keyAlgorithm = "DH";
+ } else if (keyAlgorithm.equals("DSA")) {
+ keySize = 512;
+ } else if (keyAlgorithm.equals("DH_DSA")) {
+ keySize = 512;
+ keyAlgorithm = "DH";
+ } else if (keyAlgorithm.equals("EC")) {
+ keySize = 256;
+ } else if (keyAlgorithm.equals("EC_RSA")) {
+ keySize = 256;
+ keyAlgorithm = "EC";
+ } else {
+ throw new IllegalArgumentException("Unknown key algorithm " + keyAlgorithm);
+ }
+
+ KeyPairGenerator kpg = KeyPairGenerator.getInstance(keyAlgorithm);
+ kpg.initialize(keySize, new SecureRandom());
+
+ KeyPair kp = kpg.generateKeyPair();
+ privateKey = kp.getPrivate();
+ publicKey = kp.getPublic();
} else {
- throw new IllegalArgumentException("Unknown key algorithm " + keyAlgorithm);
+ // 1b.) we use the previous keys
+ privateKey = privateEntry.getPrivateKey();
+ publicKey = privateEntry.getCertificate().getPublicKey();
}
- KeyPairGenerator kpg = KeyPairGenerator.getInstance(keyAlgorithm);
- kpg.initialize(keySize, new SecureRandom());
-
- KeyPair kp = kpg.generateKeyPair();
- privateKey = kp.getPrivate();
- PublicKey publicKey = kp.getPublic();
-
// 2.) use keys to make certificate
X500Principal issuer = ((caCert != null)
? caCert.getSubjectX500Principal()
@@ -820,6 +849,24 @@
}
/**
+ * Return an {@code X509Certificate that matches the given {@code alias}.
+ */
+ public KeyStore.Entry getEntryByAlias(String alias) {
+ return entryByAlias(keyStore, alias);
+ }
+
+ /**
+ * Finds an entry in the keystore by the given alias.
+ */
+ public static KeyStore.Entry entryByAlias(KeyStore keyStore, String alias) {
+ try {
+ return keyStore.getEntry(alias, null);
+ } catch (NoSuchAlgorithmException | UnrecoverableEntryException | KeyStoreException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
* Create a client key store that only contains self-signed certificates but no private keys
*/
public static KeyStore createClient(KeyStore caKeyStore) {
diff --git a/support/src/test/java/tests/resources/hyts_certLoop.jar b/support/src/test/java/tests/resources/hyts_certLoop.jar
new file mode 100644
index 0000000..cb4ebe1
--- /dev/null
+++ b/support/src/test/java/tests/resources/hyts_certLoop.jar
Binary files differ
diff --git a/xml/src/main/java/org/kxml2/io/KXmlSerializer.java b/xml/src/main/java/org/kxml2/io/KXmlSerializer.java
index 8fa2756..bfdeece 100644
--- a/xml/src/main/java/org/kxml2/io/KXmlSerializer.java
+++ b/xml/src/main/java/org/kxml2/io/KXmlSerializer.java
@@ -125,14 +125,18 @@
// otherwise generate.
// Note: tab, newline, and carriage return have already been
// handled above.
- boolean valid = (c >= 0x20 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xfffd);
- if (!valid) {
- reportInvalidCharacter(c);
- }
- if (unicode || c < 127) {
- writer.write(c);
+ boolean allowedInXml = (c >= 0x20 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xfffd);
+ if (allowedInXml) {
+ if (unicode || c < 127) {
+ writer.write(c);
+ } else {
+ writer.write("&#" + ((int) c) + ";");
+ }
+ } else if (Character.isHighSurrogate(c) && i < s.length() - 1) {
+ writeSurrogate(c, s.charAt(i + 1));
+ ++i;
} else {
- writer.write("&#" + ((int) c) + ";");
+ reportInvalidCharacter(c);
}
// END android-changed
}
@@ -141,7 +145,7 @@
// BEGIN android-added
private static void reportInvalidCharacter(char ch) {
- throw new IllegalArgumentException("Illegal character (" + Integer.toHexString((int) ch) + ")");
+ throw new IllegalArgumentException("Illegal character (U+" + Integer.toHexString((int) ch) + ")");
}
// END android-added
@@ -548,22 +552,41 @@
// BEGIN android-changed: ]]> is not allowed within a CDATA,
// so break and start a new one when necessary.
data = data.replace("]]>", "]]]]><![CDATA[>");
- char[] chars = data.toCharArray();
- // We also aren't allowed any invalid characters.
- for (char ch : chars) {
- boolean valid = (ch >= 0x20 && ch <= 0xd7ff) ||
+ writer.write("<![CDATA[");
+ for (int i = 0; i < data.length(); ++i) {
+ char ch = data.charAt(i);
+ boolean allowedInCdata = (ch >= 0x20 && ch <= 0xd7ff) ||
(ch == '\t' || ch == '\n' || ch == '\r') ||
(ch >= 0xe000 && ch <= 0xfffd);
- if (!valid) {
+ if (allowedInCdata) {
+ writer.write(ch);
+ } else if (Character.isHighSurrogate(ch) && i < data.length() - 1) {
+ // Character entities aren't valid in CDATA, so break out for this.
+ writer.write("]]>");
+ writeSurrogate(ch, data.charAt(++i));
+ writer.write("<![CDATA[");
+ } else {
reportInvalidCharacter(ch);
}
}
- writer.write("<![CDATA[");
- writer.write(chars, 0, chars.length);
writer.write("]]>");
// END android-changed
}
+ // BEGIN android-added
+ private void writeSurrogate(char high, char low) throws IOException {
+ if (!Character.isLowSurrogate(low)) {
+ throw new IllegalArgumentException("Bad surrogate pair (U+" + Integer.toHexString((int) high) +
+ " U+" + Integer.toHexString((int) low) + ")");
+ }
+ // Java-style surrogate pairs aren't allowed in XML. We could use the > 3-byte encodings, but that
+ // seems likely to upset anything expecting modified UTF-8 rather than "real" UTF-8. It seems more
+ // conservative in a Java environment to use an entity reference instead.
+ int codePoint = Character.toCodePoint(high, low);
+ writer.write("&#" + codePoint + ";");
+ }
+ // END android-added
+
public void comment(String comment) throws IOException {
check(false);
writer.write("<!--");