JSSE: add support for SSLSocket/SSLEngine get/setHandshakeApplicationProtocolSelector() for ALPN select support
parent
4cc6a45842
commit
682f1ea5fc
|
@ -161,8 +161,8 @@
|
|||
<property name="java.debuglevel" value="source,lines,vars"/>
|
||||
<property name="java.deprecation" value="true"/>
|
||||
<property name="java.optimize" value="false"/>
|
||||
<property name="java.source" value="1.7"/>
|
||||
<property name="java.target" value="1.7"/>
|
||||
<property name="java.source" value="1.8"/>
|
||||
<property name="java.target" value="1.8"/>
|
||||
</target>
|
||||
|
||||
<target name="jar">
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
/* MyALPNSelectCallback.java
|
||||
*
|
||||
* Copyright (C) 2006-2023 wolfSSL Inc.
|
||||
*
|
||||
* This file is part of wolfSSL.
|
||||
*
|
||||
* wolfSSL is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* wolfSSL is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
||||
*/
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.nio.*;
|
||||
import com.wolfssl.*;
|
||||
|
||||
class MyALPNSelectCallback implements WolfSSLALPNSelectCallback
|
||||
{
|
||||
public int alpnSelectCallback(WolfSSLSession ssl, String[] out,
|
||||
String[] in, Object arg) {
|
||||
|
||||
System.out.println("Entered MyALPNSelectCallback");
|
||||
System.out.println("... out.length = " + out.length);
|
||||
if (out.length > 0) {
|
||||
System.out.println("out[0] = " + out[0]);
|
||||
}
|
||||
System.out.println("... in.length = " + in.length);
|
||||
if (in.length > 0) {
|
||||
System.out.println("in[0] = " + in[0]);
|
||||
}
|
||||
System.out.println("... arg = " + arg);
|
||||
|
||||
if (in.length > 0 && in[0].equals("h2")) {
|
||||
out[0] = "h22";
|
||||
return WolfSSL.SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
|
||||
return WolfSSL.SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
}
|
||||
}
|
||||
|
|
@ -469,6 +469,15 @@ public class Server {
|
|||
}
|
||||
}
|
||||
|
||||
/* ALPN select callback */
|
||||
MyALPNSelectCallback alpnSelectCb = new MyALPNSelectCallback();
|
||||
ret = ssl.setAlpnSelectCb(alpnSelectCb, null);
|
||||
if (ret != WolfSSL.SSL_SUCCESS) {
|
||||
System.out.println("failed to set ALPN select callback, " +
|
||||
"ret = " + ret);
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
if (useIOCallbacks || (doDTLS == 1)) {
|
||||
/* register I/O callback user context */
|
||||
MyIOCtx ioctx = new MyIOCtx(outstream, instream,
|
||||
|
|
|
@ -81,6 +81,8 @@ extern "C" {
|
|||
#define com_wolfssl_WolfSSL_SSL_ERROR_SSL 85L
|
||||
#undef com_wolfssl_WolfSSL_SSL_ERROR_SOCKET_PEER_CLOSED
|
||||
#define com_wolfssl_WolfSSL_SSL_ERROR_SOCKET_PEER_CLOSED -397L
|
||||
#undef com_wolfssl_WolfSSL_UNKNOWN_ALPN_PROTOCOL_NAME_E
|
||||
#define com_wolfssl_WolfSSL_UNKNOWN_ALPN_PROTOCOL_NAME_E -405L
|
||||
#undef com_wolfssl_WolfSSL_WOLFSSL_CRL_CHECKALL
|
||||
#define com_wolfssl_WolfSSL_WOLFSSL_CRL_CHECKALL 1L
|
||||
#undef com_wolfssl_WolfSSL_WOLFSSL_OCSP_URL_OVERRIDE
|
||||
|
@ -167,6 +169,12 @@ extern "C" {
|
|||
#define com_wolfssl_WolfSSL_CACHE_MATCH_ERROR -280L
|
||||
#undef com_wolfssl_WolfSSL_WOLFSSL_SNI_HOST_NAME
|
||||
#define com_wolfssl_WolfSSL_WOLFSSL_SNI_HOST_NAME 0L
|
||||
#undef com_wolfssl_WolfSSL_SSL_TLSEXT_ERR_OK
|
||||
#define com_wolfssl_WolfSSL_SSL_TLSEXT_ERR_OK 0L
|
||||
#undef com_wolfssl_WolfSSL_SSL_TLSEXT_ERR_NOACK
|
||||
#define com_wolfssl_WolfSSL_SSL_TLSEXT_ERR_NOACK 3L
|
||||
#undef com_wolfssl_WolfSSL_SSL_TLSEXT_ERR_ALERT_FATAL
|
||||
#define com_wolfssl_WolfSSL_SSL_TLSEXT_ERR_ALERT_FATAL 2L
|
||||
#undef com_wolfssl_WolfSSL_MEMORY_E
|
||||
#define com_wolfssl_WolfSSL_MEMORY_E -125L
|
||||
#undef com_wolfssl_WolfSSL_BUFFER_E
|
||||
|
|
|
@ -45,8 +45,14 @@
|
|||
#include "com_wolfssl_WolfSSL.h"
|
||||
|
||||
/* custom I/O native fn prototypes */
|
||||
int NativeSSLIORecvCb(WOLFSSL *ssl, char *buf, int sz, void *ctx);
|
||||
int NativeSSLIOSendCb(WOLFSSL *ssl, char *buf, int sz, void *ctx);
|
||||
int NativeSSLIORecvCb(WOLFSSL *ssl, char *buf, int sz, void *ctx);
|
||||
int NativeSSLIOSendCb(WOLFSSL *ssl, char *buf, int sz, void *ctx);
|
||||
|
||||
/* ALPN select native callback prototype */
|
||||
int NativeALPNSelectCb(WOLFSSL *ssl, const unsigned char **out,
|
||||
unsigned char *outlen, const unsigned char *in,
|
||||
unsigned int inlen, void *arg);
|
||||
|
||||
#ifdef HAVE_CRL
|
||||
/* global object refs for CRL callback */
|
||||
static jobject g_crlCbIfaceObj;
|
||||
|
@ -4168,6 +4174,336 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_useALPN
|
|||
return ret;
|
||||
}
|
||||
|
||||
JNIEXPORT int JNICALL Java_com_wolfssl_WolfSSLSession_setALPNSelectCb
|
||||
(JNIEnv* jenv, jobject jcl, jlong sslPtr)
|
||||
{
|
||||
#if defined(HAVE_ALPN) && (LIBWOLFSSL_VERSION_HEX >= 0x05006006)
|
||||
/* wolfSSL_set_alpn_select_cb() added as of wolfSSL 5.6.6 */
|
||||
WOLFSSL* ssl = (WOLFSSL*)(uintptr_t)sslPtr;
|
||||
int ret = SSL_SUCCESS;
|
||||
(void)jcl;
|
||||
|
||||
if (jenv == NULL || ssl == NULL) {
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
/* set ALPN select callback */
|
||||
wolfSSL_set_alpn_select_cb(ssl, NativeALPNSelectCb, NULL);
|
||||
|
||||
return ret;
|
||||
#else
|
||||
(void)jenv;
|
||||
(void)jcl;
|
||||
(void)sslPtr;
|
||||
return NOT_COMPILED_IN;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef HAVE_ALPN
|
||||
|
||||
int NativeALPNSelectCb(WOLFSSL *ssl, const unsigned char **out,
|
||||
unsigned char *outlen, const unsigned char *in,
|
||||
unsigned int inlen, void *arg)
|
||||
{
|
||||
JNIEnv* jenv; /* JNI environment */
|
||||
jclass excClass; /* WolfSSLJNIException class */
|
||||
int needsDetach = 0; /* Should we explicitly detach? */
|
||||
jint vmret = 0;
|
||||
|
||||
jobject* g_cachedSSLObj; /* WolfSSLSession cached object */
|
||||
jclass sslClass; /* WolfSSLSession class */
|
||||
jmethodID alpnSelectMethodId; /* internalAlpnSelectCallback ID */
|
||||
|
||||
int ret = 0;
|
||||
int idx = 0;
|
||||
int peerProtoCount = 0;
|
||||
char* peerProtos = NULL;
|
||||
char* peerProtosCopy = NULL;
|
||||
word16 peerProtosSz = 0;
|
||||
char* curr = NULL;
|
||||
char* ptr = NULL;
|
||||
jobjectArray peerProtosArr = NULL;
|
||||
jobjectArray outProtoArr = NULL;
|
||||
int outProtoArrSz = 0;
|
||||
jstring selectedProto = NULL;
|
||||
const char* selectedProtoCharArr = NULL;
|
||||
int selectedProtoCharArrSz = 0;
|
||||
|
||||
if (g_vm == NULL || ssl == NULL || out == NULL || outlen == NULL ||
|
||||
in == NULL) {
|
||||
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
}
|
||||
|
||||
/* get JavaEnv from JavaVM */
|
||||
vmret = (int)((*g_vm)->GetEnv(g_vm, (void**) &jenv, JNI_VERSION_1_6));
|
||||
if (vmret == JNI_EDETACHED) {
|
||||
#ifdef __ANDROID__
|
||||
vmret = (*g_vm)->AttachCurrentThread(g_vm, &jenv, NULL);
|
||||
#else
|
||||
vmret = (*g_vm)->AttachCurrentThread(g_vm, (void**) &jenv, NULL);
|
||||
#endif
|
||||
if (vmret) {
|
||||
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
}
|
||||
needsDetach = 1;
|
||||
}
|
||||
else if (vmret != JNI_OK) {
|
||||
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
}
|
||||
|
||||
/* find exception class in case we need it */
|
||||
excClass = (*jenv)->FindClass(jenv, "com/wolfssl/WolfSSLJNIException");
|
||||
if ((*jenv)->ExceptionOccurred(jenv)) {
|
||||
(*jenv)->ExceptionDescribe(jenv);
|
||||
(*jenv)->ExceptionClear(jenv);
|
||||
if (needsDetach) {
|
||||
(*g_vm)->DetachCurrentThread(g_vm);
|
||||
}
|
||||
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
}
|
||||
|
||||
/* get stored WolfSSLSession object */
|
||||
g_cachedSSLObj = (jobject*) wolfSSL_get_jobject(ssl);
|
||||
if (!g_cachedSSLObj) {
|
||||
(*jenv)->ThrowNew(jenv, excClass,
|
||||
"Can't get native WolfSSLSession object reference in "
|
||||
"NativeALPNSelectCb");
|
||||
if (needsDetach) {
|
||||
(*g_vm)->DetachCurrentThread(g_vm);
|
||||
}
|
||||
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
}
|
||||
|
||||
/* lookup WolfSSLSession class from object */
|
||||
sslClass = (*jenv)->GetObjectClass(jenv, (jobject)(*g_cachedSSLObj));
|
||||
if (sslClass == NULL) {
|
||||
(*jenv)->ThrowNew(jenv, excClass,
|
||||
"Can't get native WolfSSLSession class reference in "
|
||||
"NativeALPNSelectCb");
|
||||
if (needsDetach) {
|
||||
(*g_vm)->DetachCurrentThread(g_vm);
|
||||
}
|
||||
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
}
|
||||
|
||||
/* call internal ALPN select callback */
|
||||
alpnSelectMethodId = (*jenv)->GetMethodID(jenv, sslClass,
|
||||
"internalAlpnSelectCallback",
|
||||
"(Lcom/wolfssl/WolfSSLSession;[Ljava/lang/String;[Ljava/lang/String;)I");
|
||||
if (alpnSelectMethodId == NULL) {
|
||||
if ((*jenv)->ExceptionOccurred(jenv)) {
|
||||
(*jenv)->ExceptionDescribe(jenv);
|
||||
(*jenv)->ExceptionClear(jenv);
|
||||
}
|
||||
(*jenv)->ThrowNew(jenv, excClass,
|
||||
"Error getting internalAlpnSelectCallback method from JNI");
|
||||
if (needsDetach) {
|
||||
(*g_vm)->DetachCurrentThread(g_vm);
|
||||
}
|
||||
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
}
|
||||
|
||||
/* Use wolfSSL_ALPN_GetPeerProtocol() here to get ALPN protocols sent
|
||||
* by the peer instead of directly using in/inlen, since this API
|
||||
* splits/formats into a comma-separated, null-terminated list */
|
||||
ret = wolfSSL_ALPN_GetPeerProtocol(ssl, &peerProtos, &peerProtosSz);
|
||||
if (ret != WOLFSSL_SUCCESS) {
|
||||
if ((*jenv)->ExceptionOccurred(jenv)) {
|
||||
(*jenv)->ExceptionDescribe(jenv);
|
||||
(*jenv)->ExceptionClear(jenv);
|
||||
}
|
||||
(*jenv)->ThrowNew(jenv, excClass,
|
||||
"Error in wolfSSL_ALPN_GetPeerProtocol()");
|
||||
if (needsDetach) {
|
||||
(*g_vm)->DetachCurrentThread(g_vm);
|
||||
}
|
||||
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
}
|
||||
|
||||
/* Make a copy of peer protos since we have to scan through it first
|
||||
* to get total number of tokens */
|
||||
peerProtosCopy = (char*)XMALLOC(peerProtosSz, NULL,
|
||||
DYNAMIC_TYPE_TMP_BUFFER);
|
||||
if (peerProtosCopy == NULL) {
|
||||
if ((*jenv)->ExceptionOccurred(jenv)) {
|
||||
(*jenv)->ExceptionDescribe(jenv);
|
||||
(*jenv)->ExceptionClear(jenv);
|
||||
}
|
||||
(*jenv)->ThrowNew(jenv, excClass,
|
||||
"Error allocating memory for peer protocols array");
|
||||
if (needsDetach) {
|
||||
(*g_vm)->DetachCurrentThread(g_vm);
|
||||
}
|
||||
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
}
|
||||
XMEMCPY(peerProtosCopy, peerProtos, peerProtosSz);
|
||||
|
||||
/* get count of protocols, used to create Java array of proper size */
|
||||
curr = XSTRTOK(peerProtosCopy, ",", &ptr);
|
||||
while (curr != NULL) {
|
||||
peerProtoCount++;
|
||||
curr = XSTRTOK(NULL, ",", &ptr);
|
||||
}
|
||||
XFREE(peerProtosCopy, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
peerProtosCopy = NULL;
|
||||
|
||||
if (peerProtoCount == 0) {
|
||||
wolfSSL_ALPN_FreePeerProtocol(ssl, &peerProtos);
|
||||
if ((*jenv)->ExceptionOccurred(jenv)) {
|
||||
(*jenv)->ExceptionDescribe(jenv);
|
||||
(*jenv)->ExceptionClear(jenv);
|
||||
}
|
||||
(*jenv)->ThrowNew(jenv, excClass,
|
||||
"ALPN peer protocol list size is 0");
|
||||
if (needsDetach) {
|
||||
(*g_vm)->DetachCurrentThread(g_vm);
|
||||
}
|
||||
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
}
|
||||
|
||||
/* create Java String[] of size peerProtoCount */
|
||||
peerProtosArr = (*jenv)->NewObjectArray(jenv, peerProtoCount,
|
||||
(*jenv)->FindClass(jenv, "java/lang/String"), NULL);
|
||||
if (peerProtosArr == NULL) {
|
||||
wolfSSL_ALPN_FreePeerProtocol(ssl, &peerProtos);
|
||||
if ((*jenv)->ExceptionOccurred(jenv)) {
|
||||
(*jenv)->ExceptionDescribe(jenv);
|
||||
(*jenv)->ExceptionClear(jenv);
|
||||
}
|
||||
(*jenv)->ThrowNew(jenv, excClass,
|
||||
"Failed to create JNI String[] for ALPN protocols");
|
||||
if (needsDetach) {
|
||||
(*g_vm)->DetachCurrentThread(g_vm);
|
||||
}
|
||||
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
}
|
||||
|
||||
/* add each char* to String[] for call to Java callback method */
|
||||
curr = XSTRTOK(peerProtos, ",", &ptr);
|
||||
while (curr != NULL) {
|
||||
(*jenv)->SetObjectArrayElement(jenv, peerProtosArr, idx++,
|
||||
(*jenv)->NewStringUTF(jenv, curr));
|
||||
if ((*jenv)->ExceptionOccurred(jenv)) {
|
||||
(*jenv)->ExceptionDescribe(jenv);
|
||||
(*jenv)->ExceptionClear(jenv);
|
||||
|
||||
wolfSSL_ALPN_FreePeerProtocol(ssl, &peerProtos);
|
||||
(*jenv)->ThrowNew(jenv, excClass,
|
||||
"Failed to add String to JNI String[] for ALPN protocols");
|
||||
if (needsDetach) {
|
||||
(*g_vm)->DetachCurrentThread(g_vm);
|
||||
}
|
||||
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
}
|
||||
|
||||
curr = XSTRTOK(NULL, ",", &ptr);
|
||||
}
|
||||
|
||||
/* free native peer protocol list, no longer needed */
|
||||
wolfSSL_ALPN_FreePeerProtocol(ssl, &peerProtos);
|
||||
|
||||
/* create new String[1] to let Java callback put output selection into
|
||||
* first array offset, ie String[0] */
|
||||
outProtoArr = (*jenv)->NewObjectArray(jenv, 1,
|
||||
(*jenv)->FindClass(jenv, "java/lang/String"), NULL);
|
||||
if (outProtoArr == NULL) {
|
||||
if ((*jenv)->ExceptionOccurred(jenv)) {
|
||||
(*jenv)->ExceptionDescribe(jenv);
|
||||
(*jenv)->ExceptionClear(jenv);
|
||||
}
|
||||
(*jenv)->ThrowNew(jenv, excClass,
|
||||
"Failed to create JNI String[1] for output ALPN protocol");
|
||||
if (needsDetach) {
|
||||
(*g_vm)->DetachCurrentThread(g_vm);
|
||||
}
|
||||
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
}
|
||||
|
||||
/* call Java callback */
|
||||
ret = (*jenv)->CallIntMethod(jenv, (jobject)(*g_cachedSSLObj),
|
||||
alpnSelectMethodId, (jobject)(*g_cachedSSLObj), outProtoArr,
|
||||
peerProtosArr);
|
||||
if ((*jenv)->ExceptionOccurred(jenv)) {
|
||||
(*jenv)->ExceptionDescribe(jenv);
|
||||
(*jenv)->ExceptionClear(jenv);
|
||||
(*jenv)->ThrowNew(jenv, excClass,
|
||||
"Exception while calling internalAlpnSelectCallback()");
|
||||
if (needsDetach) {
|
||||
(*g_vm)->DetachCurrentThread(g_vm);
|
||||
}
|
||||
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
}
|
||||
|
||||
if (ret == SSL_TLSEXT_ERR_OK) {
|
||||
/* convert returned String[0] into char* */
|
||||
outProtoArrSz = (*jenv)->GetArrayLength(jenv, outProtoArr);
|
||||
if (outProtoArrSz != 1) {
|
||||
if ((*jenv)->ExceptionOccurred(jenv)) {
|
||||
(*jenv)->ExceptionDescribe(jenv);
|
||||
(*jenv)->ExceptionClear(jenv);
|
||||
}
|
||||
(*jenv)->ThrowNew(jenv, excClass,
|
||||
"Output String[] for ALPN result not size 1");
|
||||
if (needsDetach) {
|
||||
(*g_vm)->DetachCurrentThread(g_vm);
|
||||
}
|
||||
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
}
|
||||
|
||||
/* get jstring from String[0] */
|
||||
selectedProto = (jstring)(*jenv)->GetObjectArrayElement(
|
||||
jenv, outProtoArr, 0);
|
||||
if (selectedProto == NULL) {
|
||||
if ((*jenv)->ExceptionOccurred(jenv)) {
|
||||
(*jenv)->ExceptionDescribe(jenv);
|
||||
(*jenv)->ExceptionClear(jenv);
|
||||
}
|
||||
(*jenv)->ThrowNew(jenv, excClass,
|
||||
"Selected ALPN protocol in String[] is NULL");
|
||||
if (needsDetach) {
|
||||
(*g_vm)->DetachCurrentThread(g_vm);
|
||||
}
|
||||
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
}
|
||||
|
||||
/* get char* from jstring */
|
||||
selectedProtoCharArr = (*jenv)->GetStringUTFChars(jenv,
|
||||
selectedProto, 0);
|
||||
selectedProtoCharArrSz = XSTRLEN(selectedProtoCharArr);
|
||||
|
||||
/* see if selected ALPN protocol is in original sent list */
|
||||
if (selectedProtoCharArr != NULL) {
|
||||
for (idx = 0; idx < inlen; idx++) {
|
||||
if (idx + selectedProtoCharArrSz > inlen) {
|
||||
/* No match found, fatal error. in not long enough for
|
||||
* search. Reset ret to error condition, match not set
|
||||
* correctly */
|
||||
ret = SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
break;
|
||||
}
|
||||
if (XMEMCMP(in + idx, selectedProtoCharArr,
|
||||
selectedProtoCharArrSz) == 0) {
|
||||
/* Match found. Format of input array is length byte of
|
||||
* ALPN protocol, followed by ALPN protocol,
|
||||
* ie (LEN+ALPN|LEN+ALPN|...) We set *out to ALPN selected
|
||||
* protocol and *outlen to length of protocol (idx - 1) */
|
||||
*out = in + idx;
|
||||
*outlen = in[idx - 1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Not able to get selected ALPN protocol from Java, fatal error */
|
||||
ret = SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* HAVE_ALPN */
|
||||
|
||||
JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_useSecureRenegotiation
|
||||
(JNIEnv* jenv, jobject jcl, jlong ssl)
|
||||
{
|
||||
|
|
|
@ -759,6 +759,14 @@ JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_WolfSSLSession_sslGet0AlpnSelected
|
|||
JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_useALPN
|
||||
(JNIEnv *, jobject, jlong, jstring, jint);
|
||||
|
||||
/*
|
||||
* Class: com_wolfssl_WolfSSLSession
|
||||
* Method: setALPNSelectCb
|
||||
* Signature: (J)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_setALPNSelectCb
|
||||
(JNIEnv *, jobject, jlong);
|
||||
|
||||
/*
|
||||
* Class: com_wolfssl_WolfSSLSession
|
||||
* Method: useSecureRenegotiation
|
||||
|
|
|
@ -174,6 +174,8 @@ public class WolfSSL {
|
|||
public static final int SSL_ERROR_SSL = 85;
|
||||
/** Peer closed socket */
|
||||
public static final int SSL_ERROR_SOCKET_PEER_CLOSED = -397;
|
||||
/** Unrecognized ALPN protocol name */
|
||||
public static final int UNKNOWN_ALPN_PROTOCOL_NAME_E = -405;
|
||||
|
||||
/* extra definitions from ssl.h */
|
||||
/** CertManager: check all cert CRLs */
|
||||
|
@ -302,6 +304,15 @@ public class WolfSSL {
|
|||
/** SNI Host name type, for UseSNI() */
|
||||
public static final int WOLFSSL_SNI_HOST_NAME = 0;
|
||||
|
||||
/** ALPN ERR OK, ALPN protocol match */
|
||||
public static final int SSL_TLSEXT_ERR_OK = 0;
|
||||
|
||||
/** ALPN ERR NOACK, ALPN callback no match but not fatal */
|
||||
public static final int SSL_TLSEXT_ERR_NOACK = 3;
|
||||
|
||||
/** ALPN ERR FATAL, ALPN callback no match and fatal */
|
||||
public static final int SSL_TLSEXT_ERR_ALERT_FATAL = 2;
|
||||
|
||||
/* ---------------------- wolfCrypt codes ---------------------------- */
|
||||
|
||||
/** Out of memory error */
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
/* WolfSSLALPNSelectCallback.java
|
||||
*
|
||||
* Copyright (C) 2006-2023 wolfSSL Inc.
|
||||
*
|
||||
* This file is part of wolfSSL.
|
||||
*
|
||||
* wolfSSL is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* wolfSSL is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
||||
*/
|
||||
|
||||
package com.wolfssl;
|
||||
|
||||
/**
|
||||
* wolfSSL ALPN Select Callback Interface.
|
||||
* This interface specifies how applications should implement the ALPN
|
||||
* select callback class to be used by wolfSSL.
|
||||
* <p>
|
||||
* After implementing this interface, it should be passed as a parameter
|
||||
* to the {@link WolfSSLSession#setAlpnSelectCb(WolfSSLALPNSelectCallback,
|
||||
* Object) WolfSSLSession.setALPNSelectCb()} method to be registered with the
|
||||
* native wolfSSL session.
|
||||
*
|
||||
* @author wolfSSL
|
||||
*/
|
||||
public interface WolfSSLALPNSelectCallback {
|
||||
|
||||
/**
|
||||
* ALPN select callback method.
|
||||
* This method acts as the selection callback for Application Layer
|
||||
* Negotiation Protocol (ALPN). This will be called during the handshake
|
||||
* and gives the ALPN protocols proposed by the peer, allowing the server
|
||||
* to select the desired protocol.
|
||||
*
|
||||
* @param ssl the current SSL session object from which the callback was
|
||||
* initiated.
|
||||
* @param out output array; the selected ALPN protocol should be placed as
|
||||
* a String into the first array element, ie out[0].
|
||||
* @param in input array containing the ALPN values sent by the client
|
||||
* in the ClientHello message.
|
||||
* @param arg Object set by user when registering callback, passed back
|
||||
* to user inside callback in case needed to select ALPN.
|
||||
* @return WolfSSL.SSL_TLSEXT_ERR_OK if ALPN protocol has been selected,
|
||||
* WolfSSL.SSL_TLSEXT_ERR_NOACK if ALPN protocol was not selected
|
||||
* but handshake should proceed without ALPN,
|
||||
* WolfSSL.SSL_TLSEXT_ERR_ALERT_FATAL if no ALPN match can be found
|
||||
* and a fatal alert should be sent to peer to end the
|
||||
* handshake.
|
||||
*/
|
||||
public int alpnSelectCallback(WolfSSLSession ssl, String[] out,
|
||||
String[] in, Object arg);
|
||||
}
|
||||
|
|
@ -55,6 +55,7 @@ public class WolfSSLSession {
|
|||
private Object rsaVerifyCtx;
|
||||
private Object rsaEncCtx;
|
||||
private Object rsaDecCtx;
|
||||
private Object alpnSelectArg;
|
||||
|
||||
/* reference to the associated WolfSSLContext */
|
||||
private WolfSSLContext ctx = null;
|
||||
|
@ -69,6 +70,10 @@ public class WolfSSLSession {
|
|||
private WolfSSLIORecvCallback internRecvSSLCb;
|
||||
private WolfSSLIOSendCallback internSendSSLCb;
|
||||
|
||||
/* user-registered ALPN select callback, called by internal WolfSSLSession
|
||||
* ALPN select callback */
|
||||
private WolfSSLALPNSelectCallback internAlpnSelectCb;
|
||||
|
||||
/* have session tickets been enabled for this session? Default to false. */
|
||||
private boolean sessionTicketsEnabled = false;
|
||||
|
||||
|
@ -164,6 +169,31 @@ public class WolfSSLSession {
|
|||
return this.rsaDecCtx;
|
||||
}
|
||||
|
||||
/* these callbacks will be registered with native wolfSSL library */
|
||||
private int internalIOSSLRecvCallback(WolfSSLSession ssl, byte[] buf,
|
||||
int sz)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* call user-registered recv method */
|
||||
ret = internRecvSSLCb.receiveCallback(ssl, buf, sz,
|
||||
ssl.getIOReadCtx());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private int internalIOSSLSendCallback(WolfSSLSession ssl, byte[] buf,
|
||||
int sz)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* call user-registered recv method */
|
||||
ret = internSendSSLCb.sendCallback(ssl, buf, sz,
|
||||
ssl.getIOWriteCtx());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private long internalPskClientCallback(WolfSSLSession ssl, String hint,
|
||||
StringBuffer identity, long idMaxLen, byte[] key,
|
||||
long keyMaxLen)
|
||||
|
@ -189,6 +219,18 @@ public class WolfSSLSession {
|
|||
return ret;
|
||||
}
|
||||
|
||||
private int internalAlpnSelectCallback(WolfSSLSession ssl, String[] out,
|
||||
String[] in)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* call user-registered ALPN select callback */
|
||||
ret = internAlpnSelectCb.alpnSelectCallback(ssl, out, in,
|
||||
this.alpnSelectArg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the current WolfSSLSession object is active.
|
||||
*
|
||||
|
@ -305,6 +347,7 @@ public class WolfSSLSession {
|
|||
private native int sslSetAlpnProtos(long ssl, byte[] alpnProtos);
|
||||
private native byte[] sslGet0AlpnSelected(long ssl);
|
||||
private native int useALPN(long ssl, String protocols, int options);
|
||||
private native int setALPNSelectCb(long ssl);
|
||||
private native int useSecureRenegotiation(long ssl);
|
||||
private native int rehandshake(long ssl);
|
||||
private native int set1SigAlgsList(long ssl, String list);
|
||||
|
@ -3337,6 +3380,43 @@ public class WolfSSLSession {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers ALPN select callback.
|
||||
*
|
||||
* This callback is called by native wolfSSL during the handshake
|
||||
* on the server side after receiving the ALPN protocols by the client
|
||||
* in the ClientHello message.
|
||||
*
|
||||
* @param cb callback to be registered with SSL session
|
||||
* @param arg Object that will be passed back to user inside callback
|
||||
* @return <code>SSL_SUCCESS</code> upon success. <code>
|
||||
* NOT_COMPILED_IN</code> if wolfSSL was not compiled with
|
||||
* ALPN support, and other negative value representing other
|
||||
* error scenarios.
|
||||
* @throws IllegalStateException WolfSSLSession has been freed
|
||||
* @throws WolfSSLJNIException Internal JNI error
|
||||
*/
|
||||
public int setAlpnSelectCb(WolfSSLALPNSelectCallback cb, Object arg)
|
||||
throws IllegalStateException, WolfSSLJNIException {
|
||||
|
||||
int ret = 0;
|
||||
|
||||
confirmObjectIsActive();
|
||||
|
||||
synchronized (sslLock) {
|
||||
ret = setALPNSelectCb(getSessionPtr());
|
||||
if (ret == WolfSSL.SSL_SUCCESS) {
|
||||
/* set ALPN select callback */
|
||||
internAlpnSelectCb = cb;
|
||||
|
||||
/* set ALPN select arg Object, returned to user in callback */
|
||||
this.alpnSelectArg = arg;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable use of secure renegotiation on this session. Calling this
|
||||
* API does not initiate secure renegotiation, but enables it. If enabled,
|
||||
|
@ -3420,31 +3500,6 @@ public class WolfSSLSession {
|
|||
}
|
||||
}
|
||||
|
||||
/* this will be registered with native wolfSSL library */
|
||||
private int internalIOSSLRecvCallback(WolfSSLSession ssl, byte[] buf,
|
||||
int sz)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* call user-registered recv method */
|
||||
ret = internRecvSSLCb.receiveCallback(ssl, buf, sz,
|
||||
ssl.getIOReadCtx());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private int internalIOSSLSendCallback(WolfSSLSession ssl, byte[] buf,
|
||||
int sz)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* call user-registered recv method */
|
||||
ret = internSendSSLCb.sendCallback(ssl, buf, sz,
|
||||
ssl.getIOWriteCtx());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
protected void finalize() throws Throwable
|
||||
|
|
|
@ -27,7 +27,12 @@ import com.wolfssl.WolfSSLIORecvCallback;
|
|||
import com.wolfssl.WolfSSLIOSendCallback;
|
||||
import com.wolfssl.WolfSSLJNIException;
|
||||
import com.wolfssl.WolfSSLSession;
|
||||
import com.wolfssl.WolfSSLALPNSelectCallback;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.List;
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
|
@ -35,6 +40,7 @@ import javax.net.ssl.SSLEngineResult;
|
|||
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
|
||||
import javax.net.ssl.SSLEngineResult.Status;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import javax.net.ssl.SSLParameters;
|
||||
import java.net.SocketException;
|
||||
|
@ -97,6 +103,9 @@ public class WolfSSLEngine extends SSLEngine {
|
|||
private final Object ioLock = new Object();
|
||||
private final Object toSendLock = new Object();
|
||||
|
||||
/* ALPN selector callback, if set */
|
||||
protected BiFunction<SSLEngine, List<String>, String> alpnSelector = null;
|
||||
|
||||
/** Turn on extra/verbose SSLEngine debug logging */
|
||||
public boolean extraDebugEnabled = false;
|
||||
|
||||
|
@ -961,8 +970,15 @@ public class WolfSSLEngine extends SSLEngine {
|
|||
if (ret < 0 &&
|
||||
(err != WolfSSL.SSL_ERROR_WANT_READ) &&
|
||||
(err != WolfSSL.SSL_ERROR_WANT_WRITE)) {
|
||||
throw new SSLException(
|
||||
"wolfSSL error, ret:err = " + ret + " : " + err);
|
||||
if (err == WolfSSL.UNKNOWN_ALPN_PROTOCOL_NAME_E) {
|
||||
throw new SSLHandshakeException(
|
||||
"Unrecognized protocol name error, ret:err = " +
|
||||
ret + " : " + err);
|
||||
}
|
||||
else {
|
||||
throw new SSLException(
|
||||
"wolfSSL error, ret:err = " + ret + " : " + err);
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (toSendLock) {
|
||||
|
@ -1381,6 +1397,160 @@ public class WolfSSLEngine extends SSLEngine {
|
|||
return EngineHelper.getAlpnSelectedProtocolString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the application protocol value negotiated on a handshake
|
||||
* currently in progress.
|
||||
*
|
||||
* After the handshake has finished, this will return null. To get the
|
||||
* ALPN protocol negotiated during the handshake, after it has completed,
|
||||
* call getApplicationProtocol().
|
||||
*
|
||||
* Not marked at @Override since this API was added as of
|
||||
* Java SE 8 Maintenance Release 3, and Java 7 SSLSocket will not
|
||||
* have this.
|
||||
*
|
||||
* @return String representating the application protocol negotiated
|
||||
*/
|
||||
public synchronized String getHandshakeApplicationProtocol() {
|
||||
|
||||
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
||||
"entered getHandshakeApplicationProtocol()");
|
||||
|
||||
if (!this.needInit && !this.handshakeFinished) {
|
||||
return EngineHelper.getAlpnSelectedProtocolString();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the callback that selects an application protocol during the
|
||||
* SSL/TLS handshake.
|
||||
*
|
||||
* @return the callback function, or null if no callback has been set
|
||||
*/
|
||||
public synchronized BiFunction<SSLEngine,List<String>,String>
|
||||
getHandshakeApplicationProtocolSelector() {
|
||||
|
||||
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
||||
"entered getHandshakeApplicationProtocolSelector()");
|
||||
|
||||
return this.alpnSelector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a callback function that selects an application protocol
|
||||
* value for the SSL/TLS handshake.
|
||||
*
|
||||
* Usage of this callback will override any values set by
|
||||
* SSLParameters.setApplicationProtocols().
|
||||
*
|
||||
* Callback argument descriptions:
|
||||
*
|
||||
* SSLEngine - the current SSLEngine, allows for inspection by the
|
||||
* callback if needed
|
||||
* List<String> - List of Strings representing application protocol
|
||||
* names sent by the peer
|
||||
* String - Result of the callback is an application protocol
|
||||
* name String, or null if none of the peer's protocols
|
||||
* are acceptable. If return value is an empty String,
|
||||
* ALPN will not be used.
|
||||
*
|
||||
* @param selector callback used to select ALPN protocol for handshake
|
||||
*/
|
||||
public synchronized void setHandshakeApplicationProtocolSelector(
|
||||
BiFunction<SSLEngine,List<String>,String> selector) {
|
||||
|
||||
int ret = 0;
|
||||
|
||||
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
||||
"entered setHandshakeApplicationProtocolSelector()");
|
||||
|
||||
if (selector != null) {
|
||||
ALPNSelectCallback alpnCb = new ALPNSelectCallback();
|
||||
|
||||
try {
|
||||
/* Pass in SSLSocket Object for use inside callback */
|
||||
ret = this.ssl.setAlpnSelectCb(alpnCb, this);
|
||||
if (ret != WolfSSL.SSL_SUCCESS) {
|
||||
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
||||
"Native setAlpnSelectCb() failed, ret = " + ret +
|
||||
", not setting selector");
|
||||
return;
|
||||
}
|
||||
|
||||
/* called from within ALPNSelectCallback during the handshake */
|
||||
this.alpnSelector = selector;
|
||||
|
||||
} catch (WolfSSLJNIException e) {
|
||||
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
||||
"Exception while calling ssl.setAlpnSelectCb, not setting");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inner class that implement the ALPN select callback which is registered
|
||||
* with our com.wolfssl.WolfSSLSession when
|
||||
* setHandshakeApplicationProtocolSelector() has been called.
|
||||
*/
|
||||
class ALPNSelectCallback implements WolfSSLALPNSelectCallback
|
||||
{
|
||||
public int alpnSelectCallback(WolfSSLSession ssl, String[] out,
|
||||
String[] in, Object arg) {
|
||||
|
||||
SSLEngine engine = (SSLEngine)arg;
|
||||
List<String> peerProtos = new ArrayList<String>();
|
||||
String selected = null;
|
||||
|
||||
if (alpnSelector == null) {
|
||||
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
||||
"alpnSelector null inside ALPNSelectCallback");
|
||||
return WolfSSL.SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
}
|
||||
|
||||
if (!(arg instanceof SSLEngine)) {
|
||||
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
||||
"alpnSelectCallback arg not type of SSLEngine");
|
||||
return WolfSSL.SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
}
|
||||
|
||||
if (in.length == 0) {
|
||||
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
||||
"peer protocol list is 0 inside alpnSelectCallback");
|
||||
return WolfSSL.SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
}
|
||||
|
||||
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
||||
"ALPN protos sent by peer: " + Arrays.toString(in));
|
||||
|
||||
for (String s: in) {
|
||||
peerProtos.add(s);
|
||||
}
|
||||
selected = alpnSelector.apply(engine, peerProtos);
|
||||
|
||||
if (selected == null) {
|
||||
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
||||
"ALPN protocol string is null, no peer match");
|
||||
return WolfSSL.SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
}
|
||||
else {
|
||||
if (selected.isEmpty()) {
|
||||
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
||||
"ALPN not being used, selected proto empty");
|
||||
return WolfSSL.SSL_TLSEXT_ERR_NOACK;
|
||||
}
|
||||
|
||||
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
||||
"ALPN protocol selected by callback: " + selected);
|
||||
out[0] = selected;
|
||||
return WolfSSL.SSL_TLSEXT_ERR_OK;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the SSLParameters for this SSLEngine.
|
||||
*
|
||||
|
|
|
@ -455,19 +455,22 @@ public class WolfSSLEngineHelper {
|
|||
/**
|
||||
* Get selected ALPN protocol string
|
||||
*
|
||||
* @return String representation of selected ALPN protocol or null
|
||||
* if handshake has not finished
|
||||
* @return String representation of selected ALPN protocol, null
|
||||
* if protocol is not available yet, or empty String if
|
||||
* ALPN will not be used for this connection.
|
||||
*/
|
||||
protected String getAlpnSelectedProtocolString() {
|
||||
if (this.ssl.handshakeDone()) {
|
||||
String proto = ssl.getAlpnSelectedString();
|
||||
String proto = ssl.getAlpnSelectedString();
|
||||
|
||||
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
||||
"selected ALPN protocol = " + proto);
|
||||
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
||||
"selected ALPN protocol = " + proto);
|
||||
|
||||
return proto;
|
||||
if (proto == null && this.ssl.handshakeDone()) {
|
||||
/* ALPN not used if proto is null and handshake is done */
|
||||
return "";
|
||||
}
|
||||
return null;
|
||||
|
||||
return proto;
|
||||
}
|
||||
|
||||
/********** Calls to transfer over parameter to wolfSSL before connection */
|
||||
|
@ -800,9 +803,9 @@ public class WolfSSLEngineHelper {
|
|||
"\t" + i + ": " + applicationProtocols[i]);
|
||||
}
|
||||
|
||||
/* continue on mismatch */
|
||||
/* fail on mismatch */
|
||||
this.ssl.useALPN(applicationProtocols,
|
||||
WolfSSL.WOLFSSL_ALPN_CONTINUE_ON_MISMATCH);
|
||||
WolfSSL.WOLFSSL_ALPN_FAILED_ON_MISMATCH);
|
||||
}
|
||||
|
||||
if (alpnProtos == null && applicationProtocols == null) {
|
||||
|
|
|
@ -32,6 +32,8 @@ import java.net.SocketException;
|
|||
import java.net.SocketAddress;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.List;
|
||||
import java.util.Arrays;
|
||||
import java.nio.channels.SocketChannel;
|
||||
|
||||
|
@ -47,6 +49,7 @@ import com.wolfssl.WolfSSL;
|
|||
import com.wolfssl.WolfSSLException;
|
||||
import com.wolfssl.WolfSSLIOSendCallback;
|
||||
import com.wolfssl.WolfSSLIORecvCallback;
|
||||
import com.wolfssl.WolfSSLALPNSelectCallback;
|
||||
import com.wolfssl.WolfSSLJNIException;
|
||||
import com.wolfssl.WolfSSLSession;
|
||||
|
||||
|
@ -92,6 +95,9 @@ public class WolfSSLSocket extends SSLSocket {
|
|||
* accessing WolfSSLSession object / WOLFSSL struct */
|
||||
private final Object ioLock = new Object();
|
||||
|
||||
/* ALPN selector callback, if set */
|
||||
protected BiFunction<SSLSocket, List<String>, String> alpnSelector = null;
|
||||
|
||||
/**
|
||||
* Create new WolfSSLSocket object
|
||||
*
|
||||
|
@ -1018,6 +1024,159 @@ public class WolfSSLSocket extends SSLSocket {
|
|||
return EngineHelper.getAlpnSelectedProtocolString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the application protocol value negotiated on a handshake
|
||||
* currently in progress.
|
||||
*
|
||||
* After the handshake has finished, this will return null. To get the
|
||||
* ALPN protocol negotiated during the handshake, after it has completed,
|
||||
* call getApplicationProtocol().
|
||||
*
|
||||
* Not marked at @Override since this API was added as of
|
||||
* Java SE 8 Maintenance Release 3, and Java 7 SSLSocket will not
|
||||
* have this.
|
||||
*
|
||||
* @return String representating the application protocol negotiated
|
||||
*/
|
||||
public synchronized String getHandshakeApplicationProtocol() {
|
||||
|
||||
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
||||
"entered getHandshakeApplicationProtocol()");
|
||||
|
||||
if (this.handshakeStarted && !this.handshakeComplete) {
|
||||
return EngineHelper.getAlpnSelectedProtocolString();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the callback that selects an application protocol during the
|
||||
* SSL/TLS handshake.
|
||||
*
|
||||
* @return the callback function, or null if no callback has been set
|
||||
*/
|
||||
public synchronized BiFunction<SSLSocket,List<String>,String>
|
||||
getHandshakeApplicationProtocolSelector() {
|
||||
|
||||
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
||||
"entered getHandshakeApplicationProtocolSelector()");
|
||||
|
||||
return this.alpnSelector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a callback function that selects an application protocol
|
||||
* value for the SSL/TLS handshake.
|
||||
*
|
||||
* Usage of this callback will override any values set by
|
||||
* SSLParameters.setApplicationProtocols().
|
||||
*
|
||||
* Callback argument descriptions:
|
||||
*
|
||||
* SSLSocket - the current SSLSocket, allows for inspection by the
|
||||
* callback if needed
|
||||
* List<String> - List of Strings representing application protocol
|
||||
* names sent by the peer
|
||||
* String - Result of the callback is an application protocol
|
||||
* name String, or null if none of the peer's protocols
|
||||
* are acceptable. If return value is an empty String,
|
||||
* ALPN will not be used.
|
||||
*
|
||||
* @param selector callback used to select ALPN protocol for handshake
|
||||
*/
|
||||
public synchronized void setHandshakeApplicationProtocolSelector(
|
||||
BiFunction<SSLSocket,List<String>,String> selector) {
|
||||
|
||||
int ret = 0;
|
||||
|
||||
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
||||
"entered setHandshakeApplicationProtocolSelector()");
|
||||
|
||||
if (selector != null) {
|
||||
ALPNSelectCallback alpnCb = new ALPNSelectCallback();
|
||||
|
||||
try {
|
||||
/* Pass in SSLSocket Object for use inside callback */
|
||||
ret = this.ssl.setAlpnSelectCb(alpnCb, this);
|
||||
if (ret != WolfSSL.SSL_SUCCESS) {
|
||||
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
||||
"Native setAlpnSelectCb() failed, ret = " + ret +
|
||||
", not setting selector");
|
||||
return;
|
||||
}
|
||||
|
||||
/* called from within ALPNSelectCallback during the handshake */
|
||||
this.alpnSelector = selector;
|
||||
|
||||
} catch (WolfSSLJNIException e) {
|
||||
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
||||
"Exception while calling ssl.setAlpnSelectCb, not setting");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inner class that implement the ALPN select callback which is registered
|
||||
* with our com.wolfssl.WolfSSLSession when
|
||||
* setHandshakeApplicationProtocolSelector() has been called.
|
||||
*/
|
||||
class ALPNSelectCallback implements WolfSSLALPNSelectCallback
|
||||
{
|
||||
public int alpnSelectCallback(WolfSSLSession ssl, String[] out,
|
||||
String[] in, Object arg) {
|
||||
|
||||
SSLSocket sock = (SSLSocket)arg;
|
||||
List<String> peerProtos = new ArrayList<String>();
|
||||
String selected = null;
|
||||
|
||||
if (alpnSelector == null) {
|
||||
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
||||
"alpnSelector null inside ALPNSelectCallback");
|
||||
return WolfSSL.SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
}
|
||||
|
||||
if (!(arg instanceof SSLSocket)) {
|
||||
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
||||
"alpnSelectCallback arg not type of SSLSocket");
|
||||
return WolfSSL.SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
}
|
||||
|
||||
if (in.length == 0) {
|
||||
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
||||
"peer protocol list is 0 inside alpnSelectCallback");
|
||||
return WolfSSL.SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
}
|
||||
|
||||
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
||||
"ALPN protos sent by peer: " + Arrays.toString(in));
|
||||
|
||||
for (String s: in) {
|
||||
peerProtos.add(s);
|
||||
}
|
||||
selected = alpnSelector.apply(sock, peerProtos);
|
||||
|
||||
if (selected == null) {
|
||||
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
||||
"ALPN protocol string is null, no peer match");
|
||||
return WolfSSL.SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
}
|
||||
else {
|
||||
if (selected.isEmpty()) {
|
||||
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
||||
"ALPN not being used, selected proto empty");
|
||||
return WolfSSL.SSL_TLSEXT_ERR_NOACK;
|
||||
}
|
||||
|
||||
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
||||
"ALPN protocol selected by callback: " + selected);
|
||||
out[0] = selected;
|
||||
return WolfSSL.SSL_TLSEXT_ERR_OK;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns array of protocols supported by this SSLSocket.
|
||||
*
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.junit.runner.RunWith;
|
|||
import org.junit.runners.JUnit4;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.logging.Level;
|
||||
|
@ -43,6 +44,8 @@ import java.io.InputStream;
|
|||
import java.io.OutputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.StringWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.Socket;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.SocketAddress;
|
||||
|
@ -65,6 +68,8 @@ import javax.net.ssl.SSLException;
|
|||
import javax.net.ssl.SSLHandshakeException;
|
||||
import javax.net.ssl.HandshakeCompletedListener;
|
||||
import javax.net.ssl.HandshakeCompletedEvent;
|
||||
import javax.net.ssl.SNIHostName;
|
||||
import javax.net.ssl.SNIServerName;
|
||||
import java.security.Security;
|
||||
import java.security.Provider;
|
||||
import java.security.KeyStore;
|
||||
|
@ -79,6 +84,7 @@ import java.security.NoSuchProviderException;
|
|||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.UnrecoverableKeyException;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
|
||||
import com.wolfssl.provider.jsse.WolfSSLProvider;
|
||||
|
@ -477,9 +483,11 @@ public class WolfSSLSocketTest {
|
|||
ss = (SSLServerSocket)ctx.getServerSocketFactory()
|
||||
.createServerSocket(0);
|
||||
|
||||
server = new TestServer(this, ss);
|
||||
TestArgs sArgs = new TestArgs(null, null, true, true, true, null);
|
||||
TestArgs cArgs = new TestArgs(null, null, false, false, true, null);
|
||||
server = new TestServer(this.ctx, ss, sArgs, 1);
|
||||
server.start();
|
||||
client = new TestClient(this, ss.getLocalPort());
|
||||
client = new TestClient(this.ctx, ss.getLocalPort(), cArgs);
|
||||
client.start();
|
||||
|
||||
srvException = server.getException();
|
||||
|
@ -517,9 +525,11 @@ public class WolfSSLSocketTest {
|
|||
ss = (SSLServerSocket)ctx.getServerSocketFactory()
|
||||
.createServerSocket(0);
|
||||
|
||||
server = new TestServer(this, ss);
|
||||
TestArgs sArgs = new TestArgs(null, null, true, true, true, null);
|
||||
TestArgs cArgs = new TestArgs(null, null, false, false, true, null);
|
||||
server = new TestServer(this.ctx, ss, sArgs, 1);
|
||||
server.start();
|
||||
client = new TestClient(this, ss.getLocalPort());
|
||||
client = new TestClient(this.ctx, ss.getLocalPort(), cArgs);
|
||||
client.start();
|
||||
|
||||
srvException = server.getException();
|
||||
|
@ -557,9 +567,11 @@ public class WolfSSLSocketTest {
|
|||
ss = (SSLServerSocket)ctx.getServerSocketFactory()
|
||||
.createServerSocket(0);
|
||||
|
||||
server = new TestServer(this, ss);
|
||||
TestArgs sArgs = new TestArgs(null, null, true, true, true, null);
|
||||
TestArgs cArgs = new TestArgs(null, null, false, false, true, null);
|
||||
server = new TestServer(this.ctx, ss, sArgs, 1);
|
||||
server.start();
|
||||
client = new TestClient(this, ss.getLocalPort());
|
||||
client = new TestClient(this.ctx, ss.getLocalPort(), cArgs);
|
||||
client.start();
|
||||
|
||||
srvException = server.getException();
|
||||
|
@ -597,9 +609,11 @@ public class WolfSSLSocketTest {
|
|||
ss = (SSLServerSocket)ctx.getServerSocketFactory()
|
||||
.createServerSocket(0);
|
||||
|
||||
server = new TestServer(this, ss);
|
||||
TestArgs sArgs = new TestArgs(null, null, true, true, true, null);
|
||||
TestArgs cArgs = new TestArgs(null, null, false, false, true, null);
|
||||
server = new TestServer(this.ctx, ss, sArgs, 1);
|
||||
server.start();
|
||||
client = new TestClient(this, ss.getLocalPort());
|
||||
client = new TestClient(this.ctx, ss.getLocalPort(), cArgs);
|
||||
client.start();
|
||||
|
||||
srvException = server.getException();
|
||||
|
@ -651,10 +665,13 @@ public class WolfSSLSocketTest {
|
|||
SSLServerSocket ss = (SSLServerSocket)ctx.getServerSocketFactory()
|
||||
.createServerSocket(0);
|
||||
|
||||
TestServer server = new TestServer(this, ss);
|
||||
TestArgs sArgs = new TestArgs(null, null, true, true, true, null);
|
||||
TestArgs cArgs = new TestArgs(null, null, false, false, true, null);
|
||||
|
||||
TestServer server = new TestServer(this.ctx, ss, sArgs, 1);
|
||||
server.start();
|
||||
|
||||
TestClient client = new TestClient(this, ss.getLocalPort());
|
||||
TestClient client = new TestClient(this.ctx, ss.getLocalPort(), cArgs);
|
||||
client.start();
|
||||
|
||||
|
||||
|
@ -680,6 +697,129 @@ public class WolfSSLSocketTest {
|
|||
System.out.println("\t... passed");
|
||||
}
|
||||
|
||||
public void alpnClientServerRunner(TestArgs sArgs, TestArgs cArgs,
|
||||
boolean expectingException) throws Exception {
|
||||
|
||||
if (sArgs == null || cArgs == null) {
|
||||
throw new Exception("client/server TestArgs can not be null");
|
||||
}
|
||||
|
||||
this.ctx = tf.createSSLContext("TLS", ctxProvider);
|
||||
|
||||
/* create SSLServerSocket first to get ephemeral port */
|
||||
SSLServerSocket ss = (SSLServerSocket)ctx.getServerSocketFactory()
|
||||
.createServerSocket(0);
|
||||
|
||||
TestServer server = new TestServer(this.ctx, ss, sArgs, 1);
|
||||
server.start();
|
||||
|
||||
TestClient client = new TestClient(this.ctx, ss.getLocalPort(), cArgs);
|
||||
client.start();
|
||||
|
||||
try {
|
||||
client.join(1000);
|
||||
server.join(1000);
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
System.out.println("interrupt happened");
|
||||
fail("Threaded client/server test failed");
|
||||
}
|
||||
|
||||
Exception srvException = server.getException();
|
||||
Exception cliException = client.getException();
|
||||
|
||||
if (srvException != null || cliException != null) {
|
||||
if (!expectingException) {
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
if (srvException != null) {
|
||||
srvException.printStackTrace(pw);
|
||||
}
|
||||
if (cliException != null) {
|
||||
cliException.printStackTrace(pw);
|
||||
}
|
||||
String traceString = sw.toString();
|
||||
throw new Exception(traceString);
|
||||
}
|
||||
}
|
||||
else if (expectingException) {
|
||||
throw new Exception("Expecting exception but got none");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClientServerThreadedAlpnSelectCallback() throws Exception {
|
||||
|
||||
TestArgs sArgs = null;
|
||||
TestArgs cArgs = null;
|
||||
|
||||
System.out.print("\tTesting ALPN select callback");
|
||||
|
||||
/* wolfSSL_set_alpn_select_cb() added in wolfSSL 5.6.6 */
|
||||
if (WolfSSL.getLibVersionHex() < 0x05006006) {
|
||||
System.out.println("\t... skipped");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Successful test:
|
||||
* Sanity check, no ALPN */
|
||||
sArgs = new TestArgs(null, null, true, true, true, null);
|
||||
sArgs.setExpectedAlpn("");
|
||||
cArgs = new TestArgs(null, null, false, false, true, null);
|
||||
cArgs.setExpectedAlpn("");
|
||||
alpnClientServerRunner(sArgs, cArgs, false);
|
||||
|
||||
/* Successful test:
|
||||
* ALPN callback, server selects matching protocol from client list */
|
||||
sArgs = new TestArgs(null, null, true, true, true, null);
|
||||
sArgs.setAlpnForCallback("h2");
|
||||
sArgs.setExpectedAlpn("h2");
|
||||
cArgs = new TestArgs(null, null, false, false, true, null);
|
||||
cArgs.setAlpnList(new String[] {"h2", "http/1.1"});
|
||||
cArgs.setExpectedAlpn("h2");
|
||||
alpnClientServerRunner(sArgs, cArgs, false);
|
||||
|
||||
/* Successful test:
|
||||
* ALPN callback, server selects matching protocol from client list */
|
||||
sArgs = new TestArgs(null, null, true, true, true, null);
|
||||
sArgs.setAlpnForCallback("http/1.1");
|
||||
sArgs.setExpectedAlpn("http/1.1");
|
||||
cArgs = new TestArgs(null, null, false, false, true, null);
|
||||
cArgs.setAlpnList(new String[] {"h2", "http/1.1"});
|
||||
cArgs.setExpectedAlpn("http/1.1");
|
||||
alpnClientServerRunner(sArgs, cArgs, false);
|
||||
|
||||
/* Successful test:
|
||||
* ALPN callback, client list is empty so callback not called */
|
||||
sArgs = new TestArgs(null, null, true, true, true, null);
|
||||
sArgs.setAlpnForCallback("h2");
|
||||
sArgs.setExpectedAlpn("");
|
||||
cArgs = new TestArgs(null, null, false, false, true, null);
|
||||
cArgs.setAlpnList(null);
|
||||
cArgs.setExpectedAlpn("");
|
||||
alpnClientServerRunner(sArgs, cArgs, false);
|
||||
|
||||
/* Successful test:
|
||||
* ALPN set on client and server without callback */
|
||||
sArgs = new TestArgs(null, null, true, true, true, null);
|
||||
sArgs.setAlpnList(new String[] {"h2"});
|
||||
sArgs.setExpectedAlpn("h2");
|
||||
cArgs = new TestArgs(null, null, false, false, true, null);
|
||||
cArgs.setAlpnList(new String[] {"h2", "http/1.1"});
|
||||
cArgs.setExpectedAlpn("h2");
|
||||
alpnClientServerRunner(sArgs, cArgs, false);
|
||||
|
||||
/* Failure test:
|
||||
* ALPN callback, server selects protocol not from client list */
|
||||
sArgs = new TestArgs(null, null, true, true, true, null);
|
||||
sArgs.setAlpnForCallback("invalid");
|
||||
cArgs = new TestArgs(null, null, false, false, true, null);
|
||||
cArgs.setAlpnList(new String[] {"h2", "http/1.1"});
|
||||
alpnClientServerRunner(sArgs, cArgs, true);
|
||||
|
||||
System.out.println("\t... passed");
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal multi-threaded SSLSocket-based server.
|
||||
* Used when testing concurrent threaded SSLSocket client connections
|
||||
|
@ -2432,36 +2572,219 @@ public class WolfSSLSocketTest {
|
|||
System.out.println("\t... passed");
|
||||
}
|
||||
|
||||
/**
|
||||
* Inner class used to hold configuration options for
|
||||
* TestServer and TestClient classes.
|
||||
*/
|
||||
protected class TestArgs
|
||||
{
|
||||
private String endpointIDAlg = null;
|
||||
private String sniName = null;
|
||||
private boolean wantClientAuth = true;
|
||||
private boolean needClientAuth = true;
|
||||
private boolean callStartHandshake = true;
|
||||
private X509Certificate expectedPeerCert = null;
|
||||
private String[] alpnList = null;
|
||||
private String callbackAlpn = null;
|
||||
private String expectedAlpn = null;
|
||||
|
||||
public TestArgs() { }
|
||||
|
||||
public TestArgs(String endpointID, String sni,
|
||||
boolean wantClientAuth, boolean needClientAuth,
|
||||
boolean callStartHandshake, X509Certificate expectedPeerCert) {
|
||||
|
||||
this.endpointIDAlg = endpointID;
|
||||
this.sniName = sni;
|
||||
this.wantClientAuth = wantClientAuth;
|
||||
this.needClientAuth = needClientAuth;
|
||||
this.callStartHandshake = callStartHandshake;
|
||||
this.expectedPeerCert = expectedPeerCert;
|
||||
}
|
||||
|
||||
public void setEndpointIdentificationAlg(String alg) {
|
||||
this.endpointIDAlg = alg;
|
||||
}
|
||||
|
||||
public String getEndpointIdentificationAlg() {
|
||||
return this.endpointIDAlg;
|
||||
}
|
||||
|
||||
public void setSNIName(String sni) {
|
||||
this.sniName = sni;
|
||||
}
|
||||
|
||||
public String getSNIName() {
|
||||
return this.sniName;
|
||||
}
|
||||
|
||||
public void setWantClientAuth(boolean want) {
|
||||
this.wantClientAuth = want;
|
||||
}
|
||||
|
||||
public boolean getWantClientAuth() {
|
||||
return this.wantClientAuth;
|
||||
}
|
||||
|
||||
public void setExpectedPeerCert(X509Certificate cert) {
|
||||
this.expectedPeerCert = cert;
|
||||
}
|
||||
|
||||
public X509Certificate getExpectedPeerCert() {
|
||||
return this.expectedPeerCert;
|
||||
}
|
||||
|
||||
public void setNeedClientAuth(boolean need) {
|
||||
this.needClientAuth = need;
|
||||
}
|
||||
|
||||
public boolean getNeedClientAuth() {
|
||||
return this.needClientAuth;
|
||||
}
|
||||
|
||||
public void setCallStartHandshake(boolean call) {
|
||||
this.callStartHandshake = call;
|
||||
}
|
||||
|
||||
public boolean getCallStartHandshake() {
|
||||
return this.callStartHandshake;
|
||||
}
|
||||
|
||||
public void setAlpnList(String[] alpns) {
|
||||
this.alpnList = alpns;
|
||||
}
|
||||
|
||||
public String[] getAlpnList() {
|
||||
return this.alpnList;
|
||||
}
|
||||
|
||||
public void setAlpnForCallback(String alpn) {
|
||||
this.callbackAlpn = alpn;
|
||||
}
|
||||
|
||||
public String getAlpnForCallback() {
|
||||
return this.callbackAlpn;
|
||||
}
|
||||
|
||||
public void setExpectedAlpn(String alpn) {
|
||||
this.expectedAlpn = alpn;
|
||||
}
|
||||
|
||||
public String getExpectedAlpn() {
|
||||
return this.expectedAlpn;
|
||||
}
|
||||
}
|
||||
|
||||
protected class TestServer extends Thread
|
||||
{
|
||||
private SSLContext ctx;
|
||||
private int port;
|
||||
private Exception exception = null;
|
||||
private TestArgs args = null;
|
||||
private int numConnections = 1;
|
||||
WolfSSLSocketTest wst;
|
||||
SSLServerSocket ss = null;
|
||||
|
||||
public TestServer(WolfSSLSocketTest in, SSLServerSocket ss) {
|
||||
this.ctx = in.ctx;
|
||||
this.wst = in;
|
||||
public TestServer(SSLContext ctx, SSLServerSocket ss,
|
||||
TestArgs args, int numConnections) {
|
||||
this.ctx = ctx;
|
||||
this.ss = ss;
|
||||
this.args = args;
|
||||
this.numConnections = numConnections;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
try {
|
||||
SSLSocket sock = (SSLSocket)ss.accept();
|
||||
sock.startHandshake();
|
||||
int in = sock.getInputStream().read();
|
||||
assertEquals(in, (int)'A');
|
||||
sock.getOutputStream().write('B');
|
||||
sock.close();
|
||||
for (int i = 0; i < numConnections; i++) {
|
||||
SSLSocket sock = (SSLSocket)ss.accept();
|
||||
sock.setUseClientMode(false);
|
||||
|
||||
SSLParameters params = sock.getSSLParameters();
|
||||
|
||||
params.setWantClientAuth(this.args.getWantClientAuth());
|
||||
params.setNeedClientAuth(this.args.getNeedClientAuth());
|
||||
|
||||
/* Set ALPN list of supported */
|
||||
if (this.args.getAlpnList() != null) {
|
||||
params.setApplicationProtocols(this.args.getAlpnList());
|
||||
}
|
||||
|
||||
sock.setSSLParameters(params);
|
||||
|
||||
if (sock.getHandshakeApplicationProtocol() != null) {
|
||||
throw new Exception(
|
||||
"getHandshakeApplicationProtocol() should be " +
|
||||
"null before handshake");
|
||||
}
|
||||
|
||||
if (sock.getHandshakeApplicationProtocolSelector()
|
||||
!= null) {
|
||||
throw new Exception(
|
||||
"getHandshakeApplicationProtocolSelector() " +
|
||||
"should be null before being set");
|
||||
}
|
||||
|
||||
/* wolfSSL_set_alpn_select_cb() added in wolfSSL 5.6.6 */
|
||||
if (WolfSSL.getLibVersionHex() >= 0x05006006) {
|
||||
/* Set ALPN selector callback if needed, Calls
|
||||
* chooseAppProtocol during handshake to let server
|
||||
* pick desired ALPN value */
|
||||
if (this.args.getAlpnForCallback() != null) {
|
||||
sock.setHandshakeApplicationProtocolSelector(
|
||||
(serverSocket, clientProtocols) -> {
|
||||
SSLSession s =
|
||||
serverSocket.getHandshakeSession();
|
||||
return chooseAppProtocol(
|
||||
serverSocket,
|
||||
clientProtocols,
|
||||
s.getProtocol(),
|
||||
s.getCipherSuite());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (this.args.getCallStartHandshake()) {
|
||||
sock.startHandshake();
|
||||
}
|
||||
|
||||
int in = sock.getInputStream().read();
|
||||
assertEquals(in, (int)'A');
|
||||
sock.getOutputStream().write('B');
|
||||
|
||||
if (this.args.getExpectedAlpn() != null) {
|
||||
if (!sock.getApplicationProtocol().equals(
|
||||
this.args.getExpectedAlpn())) {
|
||||
throw new Exception(
|
||||
"Expected getApplicationProtocol() " +
|
||||
"did not match actual\n" +
|
||||
"expected: " + this.args.getExpectedAlpn() +
|
||||
"\nactual: " + sock.getApplicationProtocol());
|
||||
}
|
||||
}
|
||||
|
||||
sock.close();
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
this.exception = e;
|
||||
}
|
||||
}
|
||||
|
||||
public String chooseAppProtocol(SSLSocket serverSock,
|
||||
List<String> clientProtocols, String protocol,
|
||||
String cipherSuite) {
|
||||
|
||||
if (this.args.getAlpnForCallback() == null) {
|
||||
/* empty string will ignore ALPN and continue handshake */
|
||||
return "";
|
||||
}
|
||||
|
||||
return this.args.getAlpnForCallback();
|
||||
}
|
||||
|
||||
public Exception getException() {
|
||||
return this.exception;
|
||||
}
|
||||
|
@ -2472,12 +2795,13 @@ public class WolfSSLSocketTest {
|
|||
private SSLContext ctx;
|
||||
private int srvPort;
|
||||
private Exception exception = null;
|
||||
private TestArgs args = null;
|
||||
WolfSSLSocketTest wst;
|
||||
|
||||
public TestClient(WolfSSLSocketTest in, int port) {
|
||||
this.ctx = in.ctx;
|
||||
public TestClient(SSLContext ctx, int port, TestArgs args) {
|
||||
this.ctx = ctx;
|
||||
this.srvPort = port;
|
||||
this.wst = in;
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -2486,11 +2810,58 @@ public class WolfSSLSocketTest {
|
|||
try {
|
||||
SSLSocket sock = (SSLSocket)ctx.getSocketFactory()
|
||||
.createSocket();
|
||||
sock.setUseClientMode(true);
|
||||
sock.connect(new InetSocketAddress(srvPort));
|
||||
sock.startHandshake();
|
||||
|
||||
SSLParameters params = sock.getSSLParameters();
|
||||
|
||||
/* Enable Endpoint Identification for hostname verification */
|
||||
if (this.args.getEndpointIdentificationAlg() != null) {
|
||||
params.setEndpointIdentificationAlgorithm(
|
||||
this.args.getEndpointIdentificationAlg());
|
||||
}
|
||||
|
||||
/* Set SNI, used for hostname verification of server cert */
|
||||
if (this.args.getSNIName() != null) {
|
||||
SNIHostName sniName = new SNIHostName(
|
||||
this.args.getSNIName());
|
||||
List<SNIServerName> sniNames = new ArrayList<>(1);
|
||||
sniNames.add(sniName);
|
||||
params.setServerNames(sniNames);
|
||||
}
|
||||
|
||||
/* Set client ALPN list to include in ClientHello */
|
||||
if (this.args.getAlpnList() != null) {
|
||||
params.setApplicationProtocols(this.args.getAlpnList());
|
||||
}
|
||||
|
||||
sock.setSSLParameters(params);
|
||||
|
||||
if (sock.getHandshakeApplicationProtocol() != null) {
|
||||
throw new Exception(
|
||||
"getHandshakeApplicationProtocol() should be " +
|
||||
"null before handshake");
|
||||
}
|
||||
|
||||
if (this.args.getCallStartHandshake()) {
|
||||
sock.startHandshake();
|
||||
}
|
||||
|
||||
sock.getOutputStream().write('A');
|
||||
int in = sock.getInputStream().read();
|
||||
assertEquals(in, (int)'B');
|
||||
|
||||
if (this.args.getExpectedAlpn() != null) {
|
||||
if (!sock.getApplicationProtocol().equals(
|
||||
this.args.getExpectedAlpn())) {
|
||||
throw new Exception(
|
||||
"Expected getApplicationProtocol() " +
|
||||
"did not match actual\n" +
|
||||
"expected: " + this.args.getExpectedAlpn() +
|
||||
"\nactual: " + sock.getApplicationProtocol());
|
||||
}
|
||||
}
|
||||
|
||||
sock.close();
|
||||
|
||||
} catch (Exception e) {
|
||||
|
|
Loading…
Reference in New Issue