Skip to content

Commit ffa0578

Browse files
committed
Decode PEM format root cert in gradle to make program boot faster
1 parent 79bed67 commit ffa0578

File tree

2 files changed

+35
-79
lines changed

2 files changed

+35
-79
lines changed

build.gradle

+17
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ buildscript {
6262

6363
dependencies {
6464
classpath 'com.github.ben-manes:gradle-versions-plugin:0.12.0'
65+
classpath 'com.dropbox.maven:pem-converter-maven-plugin:1.0'
6566
}
6667
}
6768

@@ -101,6 +102,22 @@ processResources {
101102
filesMatching('**/sdk-version.txt') {
102103
expand project.properties
103104
}
105+
106+
filesMatching('**/*.crt') { fcd ->
107+
def inputstream = fcd.open()
108+
def certDatas = com.dropbox.maven.pem_converter.PemLoader.load(
109+
new InputStreamReader(inputstream, "UTF-8")
110+
)
111+
inputstream.close()
112+
113+
def out = new DataOutputStream(new FileOutputStream(new File(
114+
getDestinationDir(), fcd.name.substring(0, fcd.name.length()-4) + ".raw"
115+
)))
116+
com.dropbox.maven.pem_converter.RawLoader.store(certDatas, out)
117+
out.close()
118+
119+
fcd.exclude()
120+
}
104121
}
105122

106123
compileJava {

src/main/java/com/dropbox/core/http/SSLConfig.java

+18-79
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.Arrays;
2222
import java.util.Collection;
2323
import java.util.HashSet;
24+
import java.util.List;
2425

2526
import javax.net.ssl.HttpsURLConnection;
2627
import javax.net.ssl.SSLContext;
@@ -65,7 +66,8 @@ public class SSLConfig {
6566

6667
private static /*@MonotonicNonNull*/CipherSuiteFilterationResults CACHED_CIPHER_SUITE_FILTERATION_RESULTS;
6768

68-
private static final String ROOT_CERTS_RESOURCE = "/trusted-certs.crt";
69+
private static final String ROOT_CERTS_RESOURCE = "/trusted-certs.raw";
70+
private static final int MAX_CERT_LENGTH = 10 * 1024;
6971

7072
// All client ciphersuites allowed by Dropbox.
7173
//
@@ -359,8 +361,7 @@ private static void loadKeyStore(KeyStore keyStore, InputStream in)
359361

360362
Collection<X509Certificate> certs;
361363
try {
362-
certs = (Collection<X509Certificate>) x509CertFactory
363-
.generateCertificates(new CommentFilterInputStream(in));
364+
certs = deserializeCertificates(x509CertFactory, in);
364365
} catch (CertificateException ex) {
365366
throw new LoadException("Error loading certificate: " + ex.getMessage(), ex);
366367
}
@@ -375,87 +376,25 @@ private static void loadKeyStore(KeyStore keyStore, InputStream in)
375376
}
376377
}
377378

379+
private static List<X509Certificate> deserializeCertificates(CertificateFactory x509CertFactory, InputStream in) throws IOException, LoadException, CertificateException {
380+
List<X509Certificate> certs = new ArrayList<X509Certificate>();
378381

379-
/**
380-
* Strips '#' comments from PEM encoded cert file. Java 7+ handles skipping comments that aren't
381-
* within certificate blocks. Java 6, however, will fail to parse the cert file if it contains
382-
* anything other than certificate blocks.
383-
*
384-
* <p><b> NOTE: Android will incorrectly parse PEM encoded files containing comments.</b> When
385-
* comments are left in the file, some of the certificates may not be loaded properly. This
386-
* results in exceptions like the one below:
387-
*
388-
* <pre>
389-
* Caused by: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
390-
* at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:328)
391-
* at com.android.okhttp.internal.http.SocketConnector.connectTls(SocketConnector.java:103)
392-
* at com.android.okhttp.Connection.connect(Connection.java:143)
393-
* ...
394-
* </pre>
395-
*/
396-
private static final class CommentFilterInputStream extends FilterInputStream {
397-
private boolean isLineStart;
398-
399-
public CommentFilterInputStream(InputStream in) {
400-
super(in);
401-
this.isLineStart = true;
402-
}
403-
404-
@Override
405-
public int read() throws IOException {
406-
int ord = super.read();
407-
408-
// only filter at start of line
409-
if (!isLineStart) {
410-
return ord;
411-
}
412-
413-
while (ord == '#') {
414-
// chomp the comment
415-
do {
416-
ord = super.read();
417-
} while (!isLineFeed(ord) && ord != -1);
418-
419-
// now chomp the line feeds
420-
while (isLineFeed(ord) && ord != -1) {
421-
ord = super.read();
422-
}
423-
isLineStart = true;
382+
DataInputStream din = new DataInputStream(in);
383+
byte[] data = new byte[MAX_CERT_LENGTH];
384+
while (true) {
385+
int length = din.readUnsignedShort();
386+
if (length == 0) break;
387+
if (length > MAX_CERT_LENGTH) {
388+
throw new LoadException("Invalid length for certificate entry: " + length, null);
424389
}
425-
426-
return ord;
390+
din.readFully(data, 0, length);
391+
certs.add((X509Certificate) x509CertFactory.generateCertificate(new ByteArrayInputStream(data, 0, length)));
427392
}
428393

429-
@Override
430-
public int read(byte [] b) throws IOException {
431-
return read(b, 0, b.length);
432-
}
433-
434-
@Override
435-
public int read(byte [] b, int off, int len) throws IOException {
436-
if (b == null) {
437-
throw new NullPointerException("b");
438-
}
439-
if (off < 0 || len < 0 || len > (b.length - off)) {
440-
throw new IndexOutOfBoundsException();
441-
}
442-
443-
int count = 0;
444-
for (int i = 0; i < len; ++i) {
445-
int ord = read();
446-
if (ord == -1) {
447-
break;
448-
}
449-
450-
b[off + i] = (byte) ord;
451-
++count;
452-
}
453-
454-
return count == 0 ? -1 : count;
394+
if (din.read() >= 0) {
395+
throw new LoadException("Found data after after zero-length header.", null);
455396
}
456397

457-
private static boolean isLineFeed(int ord) {
458-
return ord == '\n' || ord == '\r';
459-
}
398+
return certs;
460399
}
461400
}

0 commit comments

Comments
 (0)