NativeCrypto: add CertPath encoding PkiPath
Set the default encoding to be PkiPath to conform to other
implementations. This now passes all the tests.
Change-Id: I8475e328e8440aa3ecccd88c34e2aba6bc169be5
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java
index a357319..7e669f8 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java
@@ -374,6 +374,10 @@
/** Takes an X509 context not an X509_PUBKEY context. */
public static native byte[] i2d_X509_PUBKEY(long x509ctx);
+ public static native byte[] ASN1_seq_pack_X509(long[] x509CertRefs);
+
+ public static native long[] ASN1_seq_unpack_X509_bio(long bioRef);
+
public static native void X509_free(long x509ctx);
public static native int X509_cmp(long x509ctx1, long x509ctx2);
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLX509CertPath.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLX509CertPath.java
index 6cd31ee..4639dfd 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLX509CertPath.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLX509CertPath.java
@@ -18,8 +18,6 @@
import org.apache.harmony.xnet.provider.jsse.OpenSSLX509CertificateFactory.ParsingException;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;
@@ -28,6 +26,7 @@
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
@@ -43,6 +42,7 @@
* encode this into bytes such as {@link #getEncoded()}.
*/
private enum Encoding {
+ PKI_PATH("PkiPath"),
PKCS7("PKCS7");
private final String apiName;
@@ -65,10 +65,11 @@
/** Unmodifiable list of encodings for the API. */
private static final List<String> ALL_ENCODINGS = Collections.unmodifiableList(Arrays
.asList(new String[] {
- Encoding.PKCS7.apiName,
+ Encoding.PKI_PATH.apiName,
+ Encoding.PKCS7.apiName,
}));
- private static final Encoding DEFAULT_ENCODING = Encoding.PKCS7;
+ private static final Encoding DEFAULT_ENCODING = Encoding.PKI_PATH;
private final List<? extends X509Certificate> mCertificates;
@@ -88,28 +89,29 @@
}
private byte[] getEncoded(Encoding encoding) throws CertificateEncodingException {
- switch (encoding) {
- case PKCS7:
- return toPkcs7Format();
- default:
- throw new CertificateEncodingException("Unknown encoding");
- }
- }
-
- private byte[] toPkcs7Format() throws CertificateEncodingException {
final OpenSSLX509Certificate[] certs = new OpenSSLX509Certificate[mCertificates.size()];
final long[] certRefs = new long[certs.length];
- for (int i = 0; i < certs.length; i++) {
- if (certs[i] instanceof OpenSSLX509Certificate) {
- certs[i] = (OpenSSLX509Certificate) mCertificates.get(i);
+ for (int i = 0, j = certs.length - 1; j >= 0; i++, j--) {
+ final X509Certificate cert = mCertificates.get(i);
+
+ if (cert instanceof OpenSSLX509Certificate) {
+ certs[j] = (OpenSSLX509Certificate) cert;
} else {
- certs[i] = OpenSSLX509Certificate.fromX509Der(mCertificates.get(i).getEncoded());
+ certs[j] = OpenSSLX509Certificate.fromX509Der(cert.getEncoded());
}
- certRefs[i] = certs[i].getContext();
+
+ certRefs[j] = certs[j].getContext();
}
- return NativeCrypto.i2d_PKCS7(certRefs);
+ switch (encoding) {
+ case PKI_PATH:
+ return NativeCrypto.ASN1_seq_pack_X509(certRefs);
+ case PKCS7:
+ return NativeCrypto.i2d_PKCS7(certRefs);
+ default:
+ throw new CertificateEncodingException("Unknown encoding");
+ }
}
@Override
@@ -124,7 +126,7 @@
throw new CertificateEncodingException("Invalid encoding: " + encoding);
}
- return getEncoded();
+ return getEncoded(enc);
}
@Override
@@ -132,6 +134,34 @@
return getEncodingsIterator();
}
+ private static CertPath fromPkiPathEncoding(InputStream inStream) throws CertificateException {
+ OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(inStream);
+
+ final long[] certRefs;
+ try {
+ certRefs = NativeCrypto.ASN1_seq_unpack_X509_bio(bis.getBioContext());
+ } catch (Exception e) {
+ throw new CertificateException(e);
+ } finally {
+ NativeCrypto.BIO_free(bis.getBioContext());
+ }
+
+ if (certRefs == null) {
+ return new OpenSSLX509CertPath(Collections.<X509Certificate> emptyList());
+ }
+
+ final List<OpenSSLX509Certificate> certs =
+ new ArrayList<OpenSSLX509Certificate>(certRefs.length);
+ for (int i = certRefs.length - 1; i >= 0; i--) {
+ if (certRefs[i] == 0) {
+ continue;
+ }
+ certs.add(new OpenSSLX509Certificate(certRefs[i]));
+ }
+
+ return new OpenSSLX509CertPath(certs);
+ }
+
private static CertPath fromPkcs7Encoding(InputStream inStream) throws CertificateException {
try {
if (inStream == null || inStream.available() == 0) {
@@ -177,6 +207,8 @@
private static CertPath fromEncoding(InputStream inStream, Encoding encoding)
throws CertificateException {
switch (encoding) {
+ case PKI_PATH:
+ return fromPkiPathEncoding(inStream);
case PKCS7:
return fromPkcs7Encoding(inStream);
default:
diff --git a/luni/src/main/native/org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp b/luni/src/main/native/org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp
index 853cf4a..8795428 100644
--- a/luni/src/main/native/org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp
+++ b/luni/src/main/native/org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp
@@ -29,6 +29,7 @@
#include <jni.h>
+#include <openssl/asn1t.h>
#include <openssl/dsa.h>
#include <openssl/engine.h>
#include <openssl/err.h>
@@ -76,6 +77,13 @@
static jmethodID outputStream_writeMethod;
static jmethodID outputStream_flushMethod;
+struct OPENSSL_Delete {
+ void operator()(void* p) const {
+ OPENSSL_free(p);
+ }
+};
+typedef UniquePtr<unsigned char, OPENSSL_Delete> Unique_OPENSSL_str;
+
struct BIO_Delete {
void operator()(BIO* p) const {
BIO_free(p);
@@ -697,7 +705,7 @@
T* ByteArrayToASN1(JNIEnv* env, jbyteArray byteArray) {
ScopedByteArrayRO bytes(env, byteArray);
if (bytes.get() == NULL) {
- JNI_TRACE("ByteArrayToASN1(%p) => using byte array failed", obj);
+ JNI_TRACE("ByteArrayToASN1(%p) => using byte array failed", byteArray);
return 0;
}
@@ -4619,18 +4627,18 @@
static jlongArray NativeCrypto_d2i_PKCS7_bio(JNIEnv* env, jclass, jlong bioRef, jint which) {
BIO* bio = reinterpret_cast<BIO*>(static_cast<uintptr_t>(bioRef));
- JNI_TRACE("d2i_PKCS7_bio_certs(%p)", bio);
+ JNI_TRACE("d2i_PKCS7_bio(%p, %d)", bio, which);
if (bio == NULL) {
jniThrowNullPointerException(env, "bio == null");
- JNI_TRACE("d2i_PKCS7_bio_certs(%p) => bio == null", bio);
+ JNI_TRACE("d2i_PKCS7_bio(%p, %d) => bio == null", bio, which);
return 0;
}
Unique_PKCS7 pkcs7(d2i_PKCS7_bio(bio, NULL));
if (pkcs7.get() == NULL) {
- throwExceptionIfNecessary(env, "d2i_PKCS7_bio_certs");
- JNI_TRACE("d2i_PKCS7_bio_certs(%p) => threw exception", bio);
+ throwExceptionIfNecessary(env, "d2i_PKCS7_bio");
+ JNI_TRACE("d2i_PKCS7_bio(%p, %d) => threw exception", bio, which);
return 0;
}
@@ -4674,6 +4682,82 @@
return ASN1ToByteArray<PKCS7, i2d_PKCS7>(env, pkcs7.get());
}
+typedef STACK_OF(X509) PKIPATH;
+
+ASN1_ITEM_TEMPLATE(PKIPATH) =
+ ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, PkiPath, X509)
+ASN1_ITEM_TEMPLATE_END(PKIPATH)
+
+static jlongArray NativeCrypto_ASN1_seq_unpack_X509_bio(JNIEnv* env, jclass, jlong bioRef) {
+ BIO* bio = reinterpret_cast<BIO*>(static_cast<uintptr_t>(bioRef));
+ JNI_TRACE("ASN1_seq_unpack_X509_bio(%p)", bio);
+
+ Unique_sk_X509 path((PKIPATH*) ASN1_item_d2i_bio(ASN1_ITEM_rptr(PKIPATH), bio, NULL));
+ if (path.get() == NULL) {
+ throwExceptionIfNecessary(env, "ASN1_seq_unpack_X509_bio");
+ return NULL;
+ }
+
+ size_t size = sk_X509_num(path.get());
+
+ ScopedLocalRef<jlongArray> certArray(env, env->NewLongArray(size));
+ ScopedLongArrayRW certs(env, certArray.get());
+ for (size_t i = 0; i < size; i++) {
+ X509* item = reinterpret_cast<X509*>(sk_X509_value(path.get(), i));
+ certs[i] = reinterpret_cast<uintptr_t>(item);
+ }
+
+ JNI_TRACE("ASN1_seq_unpack_X509_bio(%p) => returns %d items", bio, size);
+ return certArray.release();
+}
+
+static jbyteArray NativeCrypto_ASN1_seq_pack_X509(JNIEnv* env, jclass, jlongArray certs) {
+ JNI_TRACE("ASN1_seq_pack_X509(%p)", certs);
+ ScopedLongArrayRO certsArray(env, certs);
+ if (certsArray.get() == NULL) {
+ JNI_TRACE("ASN1_seq_pack_X509(%p) => failed to get certs array", certs);
+ return NULL;
+ }
+
+ Unique_sk_X509 certStack(sk_X509_new_null());
+ if (certStack.get() == NULL) {
+ JNI_TRACE("ASN1_seq_pack_X509(%p) => failed to make cert stack", certs);
+ return NULL;
+ }
+
+ for (size_t i = 0; i < certsArray.size(); i++) {
+ X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(certsArray[i]));
+ sk_X509_push(certStack.get(), X509_dup(x509));
+ }
+
+ int len;
+ Unique_OPENSSL_str encoded(ASN1_seq_pack(
+ reinterpret_cast<STACK_OF(OPENSSL_BLOCK)*>(
+ reinterpret_cast<uintptr_t>(certStack.get())),
+ reinterpret_cast<int (*)(void*, unsigned char**)>(i2d_X509), NULL, &len));
+ if (encoded.get() == NULL) {
+ JNI_TRACE("ASN1_seq_pack_X509(%p) => trouble encoding", certs);
+ return NULL;
+ }
+
+ ScopedLocalRef<jbyteArray> byteArray(env, env->NewByteArray(len));
+ if (byteArray.get() == NULL) {
+ JNI_TRACE("ASN1_seq_pack_X509(%p) => creating byte array failed", certs);
+ return NULL;
+ }
+
+ ScopedByteArrayRW bytes(env, byteArray.get());
+ if (bytes.get() == NULL) {
+ JNI_TRACE("ASN1_seq_pack_X509(%p) => using byte array failed", certs);
+ return NULL;
+ }
+
+ unsigned char* p = reinterpret_cast<unsigned char*>(bytes.get());
+ memcpy(p, encoded.get(), len);
+
+ return byteArray.release();
+}
+
static void NativeCrypto_X509_free(JNIEnv* env, jclass, jlong x509Ref) {
X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref));
JNI_TRACE("X509_free(%p)", x509);
@@ -7647,6 +7731,8 @@
NATIVE_METHOD(NativeCrypto, PEM_read_bio_PKCS7, "(JI)[J"),
NATIVE_METHOD(NativeCrypto, d2i_PKCS7_bio, "(JI)[J"),
NATIVE_METHOD(NativeCrypto, i2d_PKCS7, "([J)[B"),
+ NATIVE_METHOD(NativeCrypto, ASN1_seq_unpack_X509_bio, "(J)[J"),
+ NATIVE_METHOD(NativeCrypto, ASN1_seq_pack_X509, "([J)[B"),
NATIVE_METHOD(NativeCrypto, X509_free, "(J)V"),
NATIVE_METHOD(NativeCrypto, X509_cmp, "(JJ)I"),
NATIVE_METHOD(NativeCrypto, get_X509_hashCode, "(J)I"),