WolfSSLSession.getPeerCertificates(): provide full cert chain

This is needed for Certificate Pinning which is a common practice in
Android applications.
pull/256/head
Julian Winkler 2025-03-25 22:39:31 +01:00
parent 95bedabeb2
commit aba417593f
4 changed files with 156 additions and 72 deletions

View File

@ -2858,9 +2858,29 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_sessionReused
return wolfSSL_session_reused(ssl); return wolfSSL_session_reused(ssl);
} }
JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSLSession_getPeerCertificate JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_getPeerCertificateCount
(JNIEnv* jenv, jobject jcl, jlong sslPtr) (JNIEnv* jenv, jobject jcl, jlong sslPtr)
{ {
#ifdef KEEP_PEER_CERT
WOLFSSL* ssl = (WOLFSSL*)(uintptr_t)sslPtr;
(void)jenv;
(void)jcl;
if (ssl == NULL) {
return (jlong)0;
}
WOLFSSL_X509_CHAIN* chain = wolfSSL_get_peer_chain(ssl);
return wolfSSL_get_chain_count(chain);
#else
(void)jenv;
(void)jcl;
(void)sslPtr;
return 0;
#endif
}
JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSLSession_getPeerCertificate
(JNIEnv* jenv, jobject jcl, jlong sslPtr, jint index)
{
#ifdef KEEP_PEER_CERT #ifdef KEEP_PEER_CERT
WOLFSSL_X509* x509 = NULL; WOLFSSL_X509* x509 = NULL;
WOLFSSL* ssl = (WOLFSSL*)(uintptr_t)sslPtr; WOLFSSL* ssl = (WOLFSSL*)(uintptr_t)sslPtr;
@ -2870,8 +2890,8 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSLSession_getPeerCertificate
if (ssl == NULL) { if (ssl == NULL) {
return (jlong)0; return (jlong)0;
} }
WOLFSSL_X509_CHAIN* chain = wolfSSL_get_peer_chain(ssl);
x509 = wolfSSL_get_peer_certificate(ssl); x509 = wolfSSL_get_chain_X509(chain, index);
return (jlong)(uintptr_t)x509; return (jlong)(uintptr_t)x509;
#else #else

View File

@ -351,13 +351,21 @@ JNIEXPORT jobject JNICALL Java_com_wolfssl_WolfSSLSession_dtlsGetPeer
JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_sessionReused JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_sessionReused
(JNIEnv *, jobject, jlong); (JNIEnv *, jobject, jlong);
/*
* Class: com_wolfssl_WolfSSLSession
* Method: getPeerCertificateCount
* Signature: (J)I
*/
JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_getPeerCertificateCount
(JNIEnv *, jobject, jlong);
/* /*
* Class: com_wolfssl_WolfSSLSession * Class: com_wolfssl_WolfSSLSession
* Method: getPeerCertificate * Method: getPeerCertificate
* Signature: (J)J * Signature: (JI)J
*/ */
JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSLSession_getPeerCertificate JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSLSession_getPeerCertificate
(JNIEnv *, jobject, jlong); (JNIEnv *, jobject, jlong, jint);
/* /*
* Class: com_wolfssl_WolfSSLSession * Class: com_wolfssl_WolfSSLSession

View File

@ -358,7 +358,8 @@ public class WolfSSLSession {
private native long getDtlsReplayDropCount(long ssl); private native long getDtlsReplayDropCount(long ssl);
private native InetSocketAddress dtlsGetPeer(long ssl); private native InetSocketAddress dtlsGetPeer(long ssl);
private native int sessionReused(long ssl); private native int sessionReused(long ssl);
private native long getPeerCertificate(long ssl); private native int getPeerCertificateCount(long ssl);
private native long getPeerCertificate(long ssl, int index);
private native String getPeerX509Issuer(long ssl, long x509); private native String getPeerX509Issuer(long ssl, long x509);
private native String getPeerX509Subject(long ssl, long x509); private native String getPeerX509Subject(long ssl, long x509);
private native String getPeerX509AltName(long ssl, long x509); private native String getPeerX509AltName(long ssl, long x509);
@ -2590,6 +2591,27 @@ public class WolfSSLSession {
return ret; return ret;
} }
/**
* Returns the number of certificates in the peer's certificate chain.
*
* @return number of certificates in the peer's certificate chain.
* @throws IllegalStateException WolfSSLContext has been freed
* @throws WolfSSLJNIException Internal JNI error
* @see WolfSSLSession#getPeerCertificate(int)
*/
public int getPeerCertificateCount()
throws IllegalStateException, WolfSSLJNIException {
confirmObjectIsActive();
synchronized (sslLock) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.Component.JNI,
WolfSSLDebug.INFO, this.sslPtr, "entered getPeerCertificateCount()");
return getPeerCertificateCount(this.sslPtr);
}
}
/** /**
* Gets the native (long) WOLFSSL_X509 pointer to the peer's certificate. * Gets the native (long) WOLFSSL_X509 pointer to the peer's certificate.
* This can be used to retrieve further information about the peer's * This can be used to retrieve further information about the peer's
@ -2606,6 +2628,7 @@ public class WolfSSLSession {
* Pointer should be freed by calling: * Pointer should be freed by calling:
* WolfSSLCertificate.freeX509(long x509); * WolfSSLCertificate.freeX509(long x509);
* *
* @param index index of the certificate in the peer's certificate chain
* @return (long) WOLFSSL_X509 pointer to the peer's certificate. * @return (long) WOLFSSL_X509 pointer to the peer's certificate.
* @throws IllegalStateException WolfSSLContext has been freed * @throws IllegalStateException WolfSSLContext has been freed
* @throws WolfSSLJNIException Internal JNI error * @throws WolfSSLJNIException Internal JNI error
@ -2614,7 +2637,7 @@ public class WolfSSLSession {
* @see WolfSSLSession#getVersion() * @see WolfSSLSession#getVersion()
* @see WolfSSLSession#getCurrentCipher() * @see WolfSSLSession#getCurrentCipher()
*/ */
public long getPeerCertificate() public long getPeerCertificate(int index)
throws IllegalStateException, WolfSSLJNIException { throws IllegalStateException, WolfSSLJNIException {
confirmObjectIsActive(); confirmObjectIsActive();
@ -2623,7 +2646,7 @@ public class WolfSSLSession {
WolfSSLDebug.log(getClass(), WolfSSLDebug.Component.JNI, WolfSSLDebug.log(getClass(), WolfSSLDebug.Component.JNI,
WolfSSLDebug.INFO, this.sslPtr, "entered getPeerCertificate()"); WolfSSLDebug.INFO, this.sslPtr, "entered getPeerCertificate()");
return getPeerCertificate(this.sslPtr); return getPeerCertificate(this.sslPtr, index);
} }
} }

View File

@ -516,17 +516,13 @@ public class WolfSSLImplementSSLSession extends ExtendedSSLSession
"SSLSocket/Engine closed"); "SSLSocket/Engine closed");
} }
int numCerts;
try { try {
x509 = this.ssl.getPeerCertificate(); numCerts = this.ssl.getPeerCertificateCount();
} catch (IllegalStateException | WolfSSLJNIException ex) { } catch (IllegalStateException | WolfSSLJNIException ex) {
Logger.getLogger( Logger.getLogger(
WolfSSLImplementSSLSession.class.getName()).log( WolfSSLImplementSSLSession.class.getName()).log(
Level.SEVERE, null, ex); Level.SEVERE, null, ex);
x509 = 0;
}
/* if no peer cert, throw SSLPeerUnverifiedException */
if (x509 == 0) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"ssl.getPeerCertificates() returned null, trying cached cert"); "ssl.getPeerCertificates() returned null, trying cached cert");
@ -542,56 +538,88 @@ public class WolfSSLImplementSSLSession extends ExtendedSSLSession
throw new SSLPeerUnverifiedException("No peer certificate"); throw new SSLPeerUnverifiedException("No peer certificate");
} }
} }
X509Certificate[] certs = new X509Certificate[numCerts];
try { for (int i = 0; i < numCerts; i++) {
/* wolfSSL starting with 5.3.0 returns a new WOLFSSL_X509 try {
* structure from wolfSSL_get_peer_certificate(). In that case, x509 = this.ssl.getPeerCertificate(i);
* we need to free the pointer when finished. Prior to 5.3.0, } catch (IllegalStateException | WolfSSLJNIException ex) {
* this memory was freed internally by wolfSSL since the API Logger.getLogger(
* only returned a pointer to internal memory */ WolfSSLImplementSSLSession.class.getName()).log(
if (WolfSSL.getLibVersionHex() >= 0x05003000) { Level.SEVERE, null, ex);
cert = new WolfSSLX509(x509, true); x509 = 0;
} }
else {
cert = new WolfSSLX509(x509, false); /* if no peer cert, throw SSLPeerUnverifiedException */
if (x509 == 0) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"ssl.getPeerCertificates() returned null, trying cached cert");
if (this.peerCerts != null) {
/* If peer cert is already cached, just return that */
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"peer cert already cached, returning it");
return this.peerCerts.clone();
}
else {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"No peer cert sent and none cached");
throw new SSLPeerUnverifiedException("No peer certificate");
}
} }
} catch (WolfSSLException ex) {
throw new SSLPeerUnverifiedException("Error creating certificate");
}
/* convert WolfSSLX509 into X509Certificate so we can release try {
* our native memory */ /* wolfSSL starting with 5.3.0 returns a new WOLFSSL_X509
try { * structure from wolfSSL_get_peer_certificate(). In that case,
cf = CertificateFactory.getInstance("X.509"); * we need to free the pointer when finished. Prior to 5.3.0,
} catch (CertificateException ex) { * this memory was freed internally by wolfSSL since the API
* only returned a pointer to internal memory */
if (WolfSSL.getLibVersionHex() >= 0x05003000) {
cert = new WolfSSLX509(x509, true);
}
else {
cert = new WolfSSLX509(x509, false);
}
} catch (WolfSSLException ex) {
throw new SSLPeerUnverifiedException("Error creating certificate");
}
/* convert WolfSSLX509 into X509Certificate so we can release
* our native memory */
try {
cf = CertificateFactory.getInstance("X.509");
} catch (CertificateException ex) {
cert.free();
throw new SSLPeerUnverifiedException(
"Error getting CertificateFactory instance");
}
try {
der = new ByteArrayInputStream(cert.getEncoded());
} catch (CertificateEncodingException ex) {
cert.free();
throw new SSLPeerUnverifiedException(
"Error getting encoded DER from WolfSSLX509 object");
}
try {
exportCert = (X509Certificate)cf.generateCertificate(der);
} catch (CertificateException ex) {
cert.free();
throw new SSLPeerUnverifiedException(
"Error generating X509Certificdate from DER encoding");
}
/* release native memory */
cert.free(); cert.free();
throw new SSLPeerUnverifiedException(
"Error getting CertificateFactory instance");
}
try { certs[i] = exportCert;
der = new ByteArrayInputStream(cert.getEncoded());
} catch (CertificateEncodingException ex) {
cert.free();
throw new SSLPeerUnverifiedException(
"Error getting encoded DER from WolfSSLX509 object");
} }
try {
exportCert = (X509Certificate)cf.generateCertificate(der);
} catch (CertificateException ex) {
cert.free();
throw new SSLPeerUnverifiedException(
"Error generating X509Certificdate from DER encoding");
}
/* release native memory */
cert.free();
/* cache peer cert for use by app in resumed session */ /* cache peer cert for use by app in resumed session */
this.peerCerts = new X509Certificate[] { exportCert }; this.peerCerts = certs;
return this.peerCerts.clone(); return certs.clone();
} }
@Override @Override
@ -612,25 +640,30 @@ public class WolfSSLImplementSSLSession extends ExtendedSSLSession
} }
try { try {
peerX509 = this.ssl.getPeerCertificate(); int numCerts = this.ssl.getPeerCertificateCount();
if (peerX509 == 0) { javax.security.cert.X509Certificate[] certs = new javax.security.cert.X509Certificate[numCerts];
return null;
for (int i = 0; i < numCerts; i++) {
peerX509 = this.ssl.getPeerCertificate(i);
if (peerX509 == 0) {
return null;
}
/* wolfSSL starting with 5.3.0 returns a new WOLFSSL_X509
* structure from wolfSSL_get_peer_certificate(). In that case,
* we need to free the pointer when finished. Prior to 5.3.0,
* this memory was freed internally by wolfSSL since the API
* only returned a pointer to internal memory */
if (WolfSSL.getLibVersionHex() >= 0x05003000) {
x509 = new WolfSSLX509X(peerX509, true);
}
else {
x509 = new WolfSSLX509X(peerX509, false);
}
certs[i] = (javax.security.cert.X509Certificate)x509;
} }
/* wolfSSL starting with 5.3.0 returns a new WOLFSSL_X509 return certs;
* structure from wolfSSL_get_peer_certificate(). In that case,
* we need to free the pointer when finished. Prior to 5.3.0,
* this memory was freed internally by wolfSSL since the API
* only returned a pointer to internal memory */
if (WolfSSL.getLibVersionHex() >= 0x05003000) {
x509 = new WolfSSLX509X(peerX509, true);
}
else {
x509 = new WolfSSLX509X(peerX509, false);
}
return new javax.security.cert.X509Certificate[] {
(javax.security.cert.X509Certificate)x509 };
} catch (IllegalStateException | WolfSSLJNIException | } catch (IllegalStateException | WolfSSLJNIException |
WolfSSLException ex) { WolfSSLException ex) {
@ -654,7 +687,7 @@ public class WolfSSLImplementSSLSession extends ExtendedSSLSession
} }
try { try {
peerX509 = this.ssl.getPeerCertificate(); peerX509 = this.ssl.getPeerCertificate(0);
if (peerX509 == 0) { if (peerX509 == 0) {
return null; return null;
} }