wolfssljni/src/java/com/wolfssl/provider/jsse/WolfSSLEngineHelper.java

1690 lines
63 KiB
Java

/* WolfSSLEngineHelper.java
*
* Copyright (C) 2006-2025 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.provider.jsse;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SNIMatcher;
import javax.net.ssl.SNIServerName;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException;
import javax.net.ssl.X509TrustManager;
import javax.net.ssl.X509KeyManager;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.SSLHandshakeException;
import java.security.Security;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.security.cert.CertificateEncodingException;
import com.wolfssl.WolfSSL;
import com.wolfssl.WolfSSLDebug;
import com.wolfssl.WolfSSLSession;
import com.wolfssl.WolfSSLException;
import com.wolfssl.WolfSSLJNIException;
/**
* This is a helper class to account for similar methods between SSLSocket
* and SSLEngine.
*
* This class wraps a new WOLFSSL object that is created (inside
* WolfSSLSession). All methods are protected or private because this class
* should only be used internally to wolfJSSE.
*
* @author wolfSSL
*/
public class WolfSSLEngineHelper {
/* Cache system and security properties to reduce thread contention */
private boolean jsseEnableSniExtension;
private boolean jdkTlsTrustNameService;
private boolean wolfjsseAutoSni;
private volatile WolfSSLSession ssl = null;
private WolfSSLImplementSSLSession session = null;
private WolfSSLParameters params = null;
/* Peer hostname, used for session cache lookup (combined with port),
* and SNI as secondary if user has not set via SSLParameters */
private String hostname = null;
/* Peer port, used for session cache lookup (combined with hostname) */
private int port;
/* Peer InetAddress, may be set when creating SSLSocket, otherwise
* will be null if String host constructor was used instead.
* If hostname above is null, and user has not set SSLParameters,
* if 'jdk.tls.trustNameService' property has been set will try to set
* SNI based on this using peerAddr.getHostName() */
private InetAddress peerAddr = null;
/* Reference to WolfSSLAuthStore, comes from WolfSSLContext */
private WolfSSLAuthStore authStore = null;
/* Is this client side (true) or server (false) */
private boolean clientMode;
/* Is session creation allowed for this object */
private boolean sessionCreation = true;
/* Has setUseClientMode() been called on this object */
private boolean modeSet = false;
/* wolfSSL verification mode, set inside setLocalAuth() */
private int verifyMask = WolfSSL.SSL_VERIFY_PEER;
/* Internal Java verify callback, used when user/app is not using
* com.wolfssl.provider.jsse.WolfSSLTrustX509 and instead using their
* own TrustManager to perform verification via checkClientTrusted()
* and/or checkServerTrusted().
*
* This object is stored at the native level as a global reference
* created in Java_com_wolfssl_WolfSSLSession_setVerify()
* of com_wolfssl_WolfSSLSession.c and deleted in native
* Java_com_wolfssl_WolfSSLSession_freeSSL(). Deleting the native
* global reference allows the Java object to be garbage collected. */
private WolfSSLInternalVerifyCb wicb = null;
/**
* Private helper method to get System and Security properties.
* Called once up front by constructor.
*/
private void getSystemAndSecurityProperties() {
this.jsseEnableSniExtension =
checkBooleanProperty("jsse.enableSNIExtension", true);
this.jdkTlsTrustNameService =
checkBooleanProperty("jdk.tls.trustNameService", false);
this.wolfjsseAutoSni =
checkBooleanProperty("wolfjsse.autoSNI", false);
}
/**
* Always creates a new session
* @param ssl WOLFSSL session
* @param store main auth store holding session tables and managers
* @param params default parameters to use on connection
* @throws WolfSSLException if an exception happens during session creation
*/
protected WolfSSLEngineHelper(WolfSSLSession ssl, WolfSSLAuthStore store,
WolfSSLParameters params) throws WolfSSLException {
if (params == null || ssl == null || store == null) {
throw new WolfSSLException("Bad argument");
}
getSystemAndSecurityProperties();
this.ssl = ssl;
this.params = params;
this.authStore = store;
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "created new WolfSSLEngineHelper()");
}
/**
* Allows for new session and resume session by default
* @param ssl WOLFSSL session
* @param store main auth store holding session tables and managers
* @param params default parameters to use on connection
* @param port port number as hint for resume
* @param hostname hostname as hint for resume and for default SNI
* @throws WolfSSLException if an exception happens during session resume
*/
protected WolfSSLEngineHelper(WolfSSLSession ssl, WolfSSLAuthStore store,
WolfSSLParameters params, int port, String hostname)
throws WolfSSLException {
if (params == null || ssl == null || store == null || port < 0) {
throw new WolfSSLException("Bad argument");
}
getSystemAndSecurityProperties();
this.ssl = ssl;
this.params = params;
this.port = port;
this.hostname = hostname;
this.authStore = store;
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "created new WolfSSLEngineHelper(peer port: " + port +
", peer hostname: " + hostname + ")");
}
/**
* Allows for new session and resume session by default
* @param ssl WOLFSSL session
* @param store main auth store holding session tables and managers
* @param params default parameters to use on connection
* @param port port number as hint for resume
* @param peerAddr InetAddress of peer, used for session resumption and
* SNI if system property is set
* @throws WolfSSLException if an exception happens during session resume
*/
protected WolfSSLEngineHelper(WolfSSLSession ssl, WolfSSLAuthStore store,
WolfSSLParameters params, int port, InetAddress peerAddr)
throws WolfSSLException {
if (params == null || ssl == null || store == null ||
peerAddr == null || port < 0) {
throw new WolfSSLException("Bad argument");
}
getSystemAndSecurityProperties();
this.ssl = ssl;
this.params = params;
this.port = port;
this.peerAddr = peerAddr;
this.authStore = store;
this.session = new WolfSSLImplementSSLSession(store);
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "created new WolfSSLEngineHelper(peer port: " + port +
", peer IP: " + peerAddr.getHostAddress() + ")");
}
/**
* Get the alias from the X509KeyManager to use for finding and loading
* the private key and certificate chain for this endpoint.
*
* @param km X509KeyManager or X509ExtendedKeyManager to poll for
* client/server alias name
* @param socket Socket or SSLSocket from which this peer is being
* created, may be null if engine is being used instead
* @param engine SSLEngine from which this peer is being created, may be
* null if socket is being used instead
*
* @return alias String, or null if none found
*/
private String GetKeyAndCertChainAlias(X509KeyManager km, Socket sock,
SSLEngine engine) {
String alias = null;
String javaVersion = System.getProperty("java.version");
if (sock == null && engine == null) {
return null;
}
/* If javaVersion is null, set to empty string */
if (javaVersion == null) {
javaVersion = "";
}
/* We only load keys from algorithms enabled in native wolfSSL,
* and in the priority order of ECC first, then RSA. JDK 1.7.0_201
* and 1.7.0_171 have a bug that causes PrivateKey.getEncoded() to
* fail for EC keys. This has been fixed in later JDK versions,
* but skip adding EC here if we're running on those versions . */
ArrayList<String> keyAlgos = new ArrayList<String>();
if (WolfSSL.EccEnabled() &&
(!javaVersion.equals("1.7.0_201") &&
!javaVersion.equals("1.7.0_171"))) {
keyAlgos.add("EC");
}
if (WolfSSL.RsaEnabled()) {
keyAlgos.add("RSA");
if (WolfSSL.RsaPssEnabled()) {
keyAlgos.add("RSASSA-PSS");
}
}
String[] keyTypes = new String[keyAlgos.size()];
keyTypes = keyAlgos.toArray(keyTypes);
if (clientMode) {
if (sock != null) {
alias = km.chooseClientAlias(keyTypes, null, sock);
}
else if (engine != null) {
if (km instanceof X509ExtendedKeyManager) {
alias = ((X509ExtendedKeyManager)km).
chooseEngineClientAlias(keyTypes, null, engine);
}
else {
alias = km.chooseClientAlias(keyTypes, null, null);
}
}
}
else {
/* Loop through available key types until we find an alias
* that works, or none that do and return null */
for (String type : keyTypes) {
if (sock != null) {
alias = km.chooseServerAlias(type, null, sock);
}
else if (engine != null) {
if (km instanceof X509ExtendedKeyManager) {
alias = ((X509ExtendedKeyManager)km).
chooseEngineServerAlias(type, null, engine);
}
else {
alias = km.chooseServerAlias(type, null, null);
}
}
if (alias != null) {
break;
}
}
}
return alias;
}
/**
* Loads the private key and certificate chain for this
* SSLSocket/SSLEngine to be used for performing authentication of
* this peer during the handshake.
*
* If there is no X509KeyManager in our WolfSSLAuthStore, skips loading
* private key and certificate. This means SSLContext.init() was
* initialized with a null KeyManager.
*
* @param sock Socket or SSLSocket associated with this connection, may
* be null if engine is used instead
* @param engine SSLEngine associated with this connection, may be null
* if sock used instead
*
* @throws WolfSSLException if private key is not correct format,
* WolfSSLAuthStore is null, or native error when loading
* private key or certificate.
* @throws CertificateEncodingException on error getting Certificate
* encoding before loading into native WOLFSSL
* @throws IOException on error concatenating certificate chain into
* single byte array
*/
protected synchronized void LoadKeyAndCertChain(
Socket sock, SSLEngine engine)
throws WolfSSLException, CertificateEncodingException, IOException {
int ret;
int offset;
final String alias; /* KeyStore alias holding private key */
X509KeyManager km = null; /* X509KeyManager from KeyStore */
if (this.authStore == null) {
throw new WolfSSLException("WolfSSLAuthStore is null");
}
km = this.authStore.getX509KeyManager();
if (km == null) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.ERROR,
() -> "internal KeyManager is null, no cert/key to load");
return;
}
/* Ask X509KeyManager to choose correct client alias from which
* to load private key / cert chain */
alias = GetKeyAndCertChainAlias(km, sock, engine);
authStore.setCertAlias(alias);
/* Load private key into WOLFSSL session */
PrivateKey privKey = km.getPrivateKey(alias);
if (privKey != null) {
byte[] privKeyEncoded = privKey.getEncoded();
if (!privKey.getFormat().equals("PKCS#8")) {
throw new WolfSSLException(
"Private key is not in PKCS#8 format");
}
/* Skip past PKCS#8 offset */
offset = WolfSSL.getPkcs8TraditionalOffset(privKeyEncoded, 0,
privKeyEncoded.length);
byte[] privKeyTraditional = Arrays.copyOfRange(privKeyEncoded,
offset, privKeyEncoded.length);
try {
ret = this.ssl.usePrivateKeyBuffer(privKeyTraditional,
privKeyTraditional.length, WolfSSL.SSL_FILETYPE_ASN1);
} catch (WolfSSLJNIException e) {
throw new WolfSSLException(e);
}
if (ret != WolfSSL.SSL_SUCCESS) {
throw new WolfSSLException("Failed to load private key " +
"buffer into WOLFSSL, err = " + ret);
}
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "loaded private key from X509KeyManager " +
"(alias: " + alias + ")");
} else {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "no private key found in X509KeyManager " +
"(alias: " + alias + "), skipped loading");
}
/* Load certificate chain */
X509Certificate[] cert = km.getCertificateChain(alias);
if (cert != null) {
ByteArrayOutputStream certStream = new ByteArrayOutputStream();
int chainLength = 0;
for (int i = 0; i < cert.length; i++) {
/* concatenate certs into single byte array */
certStream.write(cert[i].getEncoded());
chainLength++;
}
byte[] certChain = certStream.toByteArray();
certStream.close();
try {
ret = this.ssl.useCertificateChainBufferFormat(certChain,
certChain.length, WolfSSL.SSL_FILETYPE_ASN1);
} catch (WolfSSLJNIException e) {
throw new WolfSSLException(e);
}
if (ret != WolfSSL.SSL_SUCCESS) {
throw new WolfSSLException("Failed to load certificate " +
"chain buffer into WOLFSSL, err = " + ret);
}
final int tmpChainLength = chainLength;
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "loaded certificate chain from KeyManager (alias: " +
alias + ", length: " +
tmpChainLength + ")");
} else {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "no certificate or chain found " +
"(alias: " + alias + "), skipped loading");
}
}
/**
* Set hostname and port
* Used internally by SSLSocket.connect(SocketAddress)
*
* @param hostname peer hostname String
* @param port peer port number
*/
protected synchronized void setHostAndPort(String hostname, int port) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "entered setHostAndPort()");
this.hostname = hostname;
this.port = port;
}
/**
* Set peer InetAddress.
* Used by SSLSocket.connect() when InetAddress is passed in from user.
*
* @param peerAddr InetAddress of peer
*/
protected synchronized void setPeerAddress(InetAddress peerAddr) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "entered setPeerAddress()");
this.peerAddr = peerAddr;
}
/**
* Get the com.wolfssl.WolfSSLSession for this object
*
* @return com.wolfssl.WolfSSLSession for this object
*/
protected synchronized WolfSSLSession getWolfSSLSession() {
return ssl;
}
/**
* Get WolfSSLImplementSession for this object
*
* @return WolfSSLImplementSession for this object
*/
protected synchronized WolfSSLImplementSSLSession getSession() {
if (this.session == null) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "this.session is null, creating new " +
"WolfSSLImplementSSLSession");
this.session = new WolfSSLImplementSSLSession(authStore);
}
return this.session;
}
/**
* Get all supported cipher suites in native wolfSSL library, which
* are also allowed by "wolfjsse.enabledCipherSuites" system Security
* property, if set.
*
* @return String array of all supported cipher suites
*/
protected static synchronized String[] getAllCiphers() {
return WolfSSLUtil.sanitizeSuites(WolfSSL.getCiphersIana());
}
/**
* Get all enabled cipher suites, and allowed via
* wolfjsse.enabledCipherSuites system Security property (if set).
*
* @return String array of all enabled cipher suites
*/
protected synchronized String[] getCiphers() {
return WolfSSLUtil.sanitizeSuites(this.params.getCipherSuites());
}
/**
* Set cipher suites enabled in WolfSSLParameters
*
* Sanitizes input array for invalid suites
*
* @param suites String array of cipher suites to be enabled
*
* @throws IllegalArgumentException if input array contains invalid
* cipher suites, input array is null, or input array has length
* zero
*/
protected synchronized void setCiphers(String[] suites)
throws IllegalArgumentException {
if (suites == null) {
throw new IllegalArgumentException("input array is null");
}
if (suites.length == 0) {
throw new IllegalArgumentException("input array has length zero");
}
/* sanitize cipher array for unsupported strings */
List<String> supported = Arrays.asList(getAllCiphers());
for (int i = 0; i < suites.length; i++) {
if (!supported.contains(suites[i])) {
throw new IllegalArgumentException("Unsupported CipherSuite: " +
suites[i] + "(Supported: " +
Arrays.toString(getAllCiphers()) + ")");
}
}
this.params.setCipherSuites(WolfSSLUtil.sanitizeSuites(suites));
}
/**
* Set protocols enabled in WolfSSLParameters
*
* Sanitizes protocol array for invalid protocols
*
* @param p String array of SSL/TLS protocols to be enabled
*
* @throws IllegalArgumentException if input array is null,
* has length zero, or contains invalid/unsupported protocols
*/
protected synchronized void setProtocols(String[] p)
throws IllegalArgumentException {
if (p == null) {
throw new IllegalArgumentException("input array is null");
}
if (p.length == 0) {
throw new IllegalArgumentException("input array has length zero");
}
/* sanitize protocol array for unsupported strings */
List<String> supported = Arrays.asList(getAllProtocols());
for (int i = 0; i < p.length; i++) {
if (!supported.contains(p[i])) {
throw new IllegalArgumentException("Unsupported protocol: " +
p[i]);
}
}
this.params.setProtocols(
WolfSSLUtil.sanitizeProtocols(p, WolfSSL.TLS_VERSION.INVALID));
}
/**
* Get enabled SSL/TLS protocols from WolfSSLParameters
*
* @return String array of enabled SSL/TLS protocols
*/
protected synchronized String[] getProtocols() {
return WolfSSLUtil.sanitizeProtocols(
this.params.getProtocols(), WolfSSL.TLS_VERSION.INVALID);
}
/**
* Get all supported SSL/TLS protocols in native wolfSSL library,
* which are also allowed by 'jdk.tls.client.protocols' or
* 'jdk.tls.server.protocols' if set.
*
* @return String array of supported protocols
*/
protected static synchronized String[] getAllProtocols() {
return WolfSSLUtil.sanitizeProtocols(
WolfSSL.getProtocols(), WolfSSL.TLS_VERSION.INVALID);
}
/**
* Set client mode for associated WOLFSSL session
*
* @param mode client mode (true/false)
*
* @throws IllegalArgumentException if called after SSL/TLS handshake
* has been completed. Only allowed before.
*/
protected synchronized void setUseClientMode(boolean mode)
throws IllegalArgumentException {
if (this.ssl.handshakeDone()) {
throw new IllegalArgumentException("setUseClientMode() not " +
"allowed after handshake is completed");
}
this.clientMode = mode;
if (this.clientMode) {
this.ssl.setConnectState();
}
else {
this.ssl.setAcceptState();
}
this.modeSet = true;
}
/**
* Get clientMode for associated session
*
* @return boolean value of clientMode set for this session
*/
protected synchronized boolean getUseClientMode() {
return this.clientMode;
}
/**
* Set if session needs client authentication
*
* @param need boolean if session needs client authentication
*/
protected synchronized void setNeedClientAuth(boolean need) {
this.params.setNeedClientAuth(need);
}
/**
* Get value of needClientAuth for this session
*
* @return boolean value for needClientAuth
*/
protected synchronized boolean getNeedClientAuth() {
return this.params.getNeedClientAuth();
}
/**
* Set value of wantClientAuth for this session
*
* @param want boolean value of wantClientAuth for this session
*/
protected synchronized void setWantClientAuth(boolean want) {
this.params.setWantClientAuth(want);
}
/**
* Get value of wantClientAuth for this session
*
* @return boolean value for wantClientAuth
*/
protected synchronized boolean getWantClientAuth() {
return this.params.getWantClientAuth();
}
/**
* Set ability to create sessions
*
* @param flag boolean to set enable session creation
*/
protected synchronized void setEnableSessionCreation(boolean flag) {
this.sessionCreation = flag;
}
/**
* Get boolean if session creation is allowed
*
* @return boolean value for enableSessionCreation
*/
protected synchronized boolean getEnableSessionCreation() {
return this.sessionCreation;
}
/**
* Enable use of session tickets
*
* @param flag boolean to enable/disable session tickets
*/
protected synchronized void setUseSessionTickets(boolean flag) {
this.params.setUseSessionTickets(flag);
}
/**
* Set ALPN protocols
*
* @param alpnProtos encoded byte array of ALPN protocols
*/
protected synchronized void setAlpnProtocols(byte[] alpnProtos) {
this.params.setAlpnProtocols(alpnProtos);
}
/**
* Get selected ALPN protocol
*
* Used by some versions of Android, non-standard ALPN API.
*
* @return encoded byte array for selected ALPN protocol or null if
* handshake has not finished
*/
protected synchronized byte[] getAlpnSelectedProtocol() {
if (this.ssl.handshakeDone()) {
return ssl.getAlpnSelected();
}
return null;
}
/**
* Get selected ALPN protocol string
*
* @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 synchronized String getAlpnSelectedProtocolString() {
String proto = ssl.getAlpnSelectedString();
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "selected ALPN protocol = " + proto);
if (proto == null && this.ssl.handshakeDone()) {
/* ALPN not used if proto is null and handshake is done */
return "";
}
return proto;
}
/********** Calls to transfer over parameter to wolfSSL before connection */
/*transfer over cipher suites right before establishing a connection */
private void setLocalCiphers(String[] suites)
throws IllegalArgumentException {
try {
String list;
StringBuilder sb = new StringBuilder();
if (suites == null || suites.length == 0) {
/* use default cipher suites */
return;
}
for (String s : suites) {
sb.append(s);
sb.append(":");
}
if (sb.length() > 0) {
/* remove last : */
sb.deleteCharAt(sb.length() - 1);
list = sb.toString();
if (this.ssl.setCipherList(list) != WolfSSL.SSL_SUCCESS) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "error setting cipher list " + list);
}
}
} catch (IllegalStateException e) {
throw new IllegalArgumentException(e);
}
}
/* sets the protocol to use with WOLFSSL connections */
private void setLocalProtocol(String[] p)
throws SSLException {
int i;
long mask = 0;
boolean[] set = new boolean[5];
Arrays.fill(set, false);
if (p == null) {
/* if null then just use wolfSSL default */
return;
}
if (p.length == 0) {
throw new SSLException("No protocols enabled or available");
}
for (i = 0; i < p.length; i++) {
/* TLS 1.3 needs to be enabled for DTLS 1.3 */
if (p[i].equals("TLSv1.3") || p[i].equals("DTLSv1.3")) {
set[0] = true;
}
/* TLS 1.2 needs to be enabled for DTLS 1.2 */
if (p[i].equals("TLSv1.2") || p[i].equals("DTLSv1.2")) {
set[1] = true;
}
if (p[i].equals("TLSv1.1")) {
set[2] = true;
}
if (p[i].equals("TLSv1")) {
set[3] = true;
}
if (p[i].equals("SSLv3")) {
set[4] = true;
}
}
/* Note: No SSL_OP_NO_* for DTLS in native wolfSSL */
if (set[0] == false) {
mask |= WolfSSL.SSL_OP_NO_TLSv1_3;
}
if (set[1] == false) {
mask |= WolfSSL.SSL_OP_NO_TLSv1_2;
}
if (set[2] == false) {
mask |= WolfSSL.SSL_OP_NO_TLSv1_1;
}
if (set[3] == false) {
mask |= WolfSSL.SSL_OP_NO_TLSv1;
}
if (set[4] == false) {
mask |= WolfSSL.SSL_OP_NO_SSLv3;
}
this.ssl.setOptions(mask);
}
/* sets client auth on or off if needed / wanted */
private void setLocalAuth(SSLSocket socket, SSLEngine engine) {
int mask = WolfSSL.SSL_VERIFY_NONE;
/* default to client side authenticating the server connecting to */
if (this.clientMode) {
mask = WolfSSL.SSL_VERIFY_PEER;
}
if (this.params.getWantClientAuth()) {
mask |= WolfSSL.SSL_VERIFY_PEER;
}
if (this.params.getNeedClientAuth()) {
mask |= (WolfSSL.SSL_VERIFY_PEER |
WolfSSL.SSL_VERIFY_FAIL_IF_NO_PEER_CERT);
}
X509TrustManager tm = authStore.getX509TrustManager();
wicb = new WolfSSLInternalVerifyCb(authStore.getX509TrustManager(),
this.clientMode, socket, engine,
this.params);
if (tm instanceof com.wolfssl.provider.jsse.WolfSSLTrustX509) {
/* use internal peer verification logic */
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "X509TrustManager is of type WolfSSLTrustX509");
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "Using native internal peer verification logic");
/* Register Java verify callback for additional hostname
* verification when SSLParameters Endpoint Identification
* Algorithm has been set. To get this callback to be called,
* native wolfSSL should be compiled with the following define:
* WOLFSSL_ALWAYS_VERIFY_CB */
this.verifyMask = mask;
} else {
/* not our own TrustManager, set up callback so JSSE can use
* TrustManager.checkClientTrusted/checkServerTrusted() */
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "X509TrustManager is not of type WolfSSLTrustX509");
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "Using checkClientTrusted/ServerTrusted() " +
"for verification");
this.verifyMask = WolfSSL.SSL_VERIFY_PEER;
}
this.ssl.setVerify(this.verifyMask, wicb);
}
/**
* Get the value of a boolean system property.
* If not set (property is null), use the default value given.
*
* @param prop System property to check
* @param defaultVal Default value to use if property is null
* @return Boolean value of the property, true/false
*/
private static boolean checkBooleanProperty(String prop,
boolean defaultVal) {
String enabled = System.getProperty(prop);
if (enabled == null) {
return defaultVal;
}
if (enabled.equalsIgnoreCase("true")) {
return true;
}
return false;
}
/**
* Set SNI server names on client side.
*
* SNI names are only set if the 'jsse.enableSNIExtension' system
* property has not been set to false. Default for this property
* is defined by Oracle to be true.
*
* We first try to set SNI names from SSLParameters if set by the user.
* If not set in SSLParameters, try to set using InetAddress.getHostName()
* IFF 'jdk.tls.trustNameService` System property has been set to true.
* Otherwise fall back and set based on hostname String if not null.
* hostname String may be either IP address or fully qualified domain
* name depending on what createSocket() API the user has called and with
* what String.
*/
private void setLocalServerNames() {
boolean autoSNI = this.wolfjsseAutoSni;
/* Detect HttpsURLConnection usage by checking:
* - Client mode is set (client-side connection)
* - Has hostname from URL
* - Has peer address from socket
* - No explicit SNI configuration
* This pattern is unique to HttpsURLConnection initialization
*/
boolean isHttpsConnection = this.clientMode &&
this.hostname != null &&
this.peerAddr != null &&
this.params.getServerNames() == null;
/* Enable SNI if explicitly requested via property or if
* HttpsURLConnection is detected */
autoSNI = autoSNI || isHttpsConnection;
if (!this.jsseEnableSniExtension) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "jsse.enableSNIExtension property set to false, " +
"not adding SNI to ClientHello");
}
else if (this.clientMode) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "jsse.enableSNIExtension property set to true, " +
"enabling SNI");
/* Explicitly set if user has set through SSLParameters */
List<WolfSSLSNIServerName> names = this.params.getServerNames();
if (names != null && names.size() > 0) {
/* Should only be one server name */
WolfSSLSNIServerName sni = names.get(0);
if (sni != null) {
this.ssl.useSNI((byte)sni.getType(), sni.getEncoded());
}
} else if (autoSNI) {
if (this.peerAddr != null && this.jdkTlsTrustNameService) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "setting SNI extension with " +
"InetAddress.getHostName(): " +
this.peerAddr.getHostName());
this.ssl.useSNI((byte)0,
this.peerAddr.getHostName().getBytes());
} else if (this.hostname != null) {
if (peerAddr != null) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "jdk.tls.trustNameService not set to true, " +
"not doing reverse DNS lookup to set SNI");
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "setting SNI extension with hostname: " +
this.hostname);
}
else {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "peerAddr is null, setting SNI extension " +
"with hostname: " + this.hostname);
}
this.ssl.useSNI((byte)0, this.hostname.getBytes());
} else {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "hostname and peerAddr are null, " +
"not setting SNI");
}
} else {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "No SNI configured through SSLParameters, " +
"not setting SNI");
}
}
}
/* Session tickets are enabled in different ways depending on the JDK
* implementation we are running on. For Oracle/OpenJDK, the following
* system properties enable session tickets and were added in JDK 13:
*
* -Djdk.tls.client.enableSessionTicketExtension=true
* -Djdk.tls.server.enableSessionTicketExtension=true
*
* wolfJSSE currently supports client-side session ticket support, but
* not yet enabling of server-side support.
*
* On Android, some libraries/frameworks (ex: okhttp) expect to enable
* session tickets per SSLSocket by calling a custom SSLSocket extension
* method called SSLSocket.setUseSessionTickets().
*
* Note that for session ticket support in wolfJSSE, underlying native
* wolfSSL must be compiled with session ticket support enabled. This
* is done via "--enable-session-ticket" or "-DHAVE_SESSION_TICKET".
*/
private void setLocalSessionTicket() {
if (this.clientMode) {
boolean enableFlag = this.params.getUseSessionTickets();
String enableProperty = System.getProperty(
"jdk.tls.client.enableSessionTicketExtension");
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "SSLSocket.setUseSessionTickets() set to: " +
String.valueOf(enableFlag));
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "jdk.tls.client.enableSessionTicketExtension property: " +
enableProperty);
if ((enableFlag == true) ||
((enableProperty != null) &&
(enableProperty.equalsIgnoreCase("true")))) {
/* enable client-side session ticket support */
this.ssl.useSessionTicket();
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "session tickets enabled for this session");
} else {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "session tickets not enabled on this session");
}
}
}
/* Set the ALPN to be used for this session */
private void setLocalAlpnProtocols() {
/* ALPN protocol list could be stored in either of the following,
* depending on what platform/JDK we are being used on:
* this.params.getAlpnProtos() or
* this.params.getApplicationProtocols()
* For example, Conscrypt consumers on older Android versions with
* JDK 7 will be in params.getAlpnProtos(). JDK versions > 8, with
* support for params.getApplicationProtocols() will likely use that
* instead. */
int i;
byte[] alpnProtos = this.params.getAlpnProtos();
String[] applicationProtocols = this.params.getApplicationProtocols();
if ((alpnProtos != null && alpnProtos.length > 0) &&
(applicationProtocols != null && applicationProtocols.length > 0)) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "ALPN protocols found in both params.getAlpnProtos() " +
"and params.getApplicationProtocols()");
}
/* try to set from byte[] first, then overwrite with String[] if
* both have been set */
if (alpnProtos != null && alpnProtos.length > 0) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "Setting ALPN protocols for WOLFSSL session from byte[" +
alpnProtos.length + "]");
this.ssl.useALPN(alpnProtos);
}
if (applicationProtocols != null && applicationProtocols.length > 0) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "Setting Application Protocols for WOLFSSL session " +
"from String[]:");
for (i = 0; i < applicationProtocols.length; i++) {
final int idx = i;
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "\t" + idx + ": " + applicationProtocols[idx]);
}
/* fail on mismatch */
this.ssl.useALPN(applicationProtocols,
WolfSSL.WOLFSSL_ALPN_FAILED_ON_MISMATCH);
}
if (alpnProtos == null && applicationProtocols == null) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "No ALPN protocols set, not setting for this " +
"WOLFSSL session");
}
}
private void setLocalSecureRenegotiation() {
/* Enable secure renegotiation if native wolfSSL has been compiled
* with HAVE_SECURE_RENEGOTIATION. Some JSSE consuming apps
* expect that secure renegotiation will be supported. */
int ret = this.ssl.useSecureRenegotiation();
if (ret == WolfSSL.SSL_SUCCESS) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "enabled secure renegotiation support for session");
}
else if (ret == WolfSSL.NOT_COMPILED_IN) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "native secure renegotiation not compiled in");
}
else {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "error enabling secure renegotiation, ret = " + ret);
}
}
private void setLocalSigAlgorithms() {
int ret = 0;
if (this.clientMode) {
/* Get restricted signature algorithms for ClientHello if set by
* user in "wolfjsse.enabledSigAlgorithms" Security property */
String sigAlgos = WolfSSLUtil.getSignatureAlgorithms();
if (sigAlgos != null) {
ret = this.ssl.setSignatureAlgorithms(sigAlgos);
if (ret != WolfSSL.SSL_SUCCESS &&
ret != WolfSSL.NOT_COMPILED_IN) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "error restricting signature algorithms based " +
"on wolfjsse.enabledSignatureAlgorithms property");
} else if (ret == WolfSSL.SSL_SUCCESS) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "restricted signature algorithms based on " +
"wolfjsse.enabledSignatureAlgorithms property");
}
}
}
}
private void setLocalSupportedCurves() throws SSLException {
int ret = 0;
if (this.clientMode) {
/* Get restricted supported curves for ClientHello if set by
* user in "wolfjsse.enabledSupportedCurves" Security property */
String[] curves = WolfSSLUtil.getSupportedCurves();
if (curves != null) {
ret = this.ssl.useSupportedCurves(curves);
if (ret != WolfSSL.SSL_SUCCESS) {
if (ret == WolfSSL.NOT_COMPILED_IN) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "Unable to set requested TLS Supported " +
"Curves, native support not compiled in.");
}
else {
throw new SSLException(
"Error setting TLS Supported Curves based on " +
"wolfjsse.enabledSupportedCurves property, ret = " +
ret + ", curves: " + Arrays.toString(curves));
}
}
else {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "set TLS Supported Curves based on " +
"wolfjsse.enabledSupportedCurves property");
}
}
}
}
private void setLocalMaximumPacketSize() {
/* Set maximum packet size, currently only makes a differnce if
* DTLS is enabled and used. Calling application will set this via
* SSLParameters.setMaximumPacketSize(). */
int ret;
int maxPacketSize = this.params.getMaximumPacketSize();
if (maxPacketSize != 0) {
/* Zero size means use implicit sizing logic of implementation,
* take no special action here if 0. */
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "Maximum packet size found in SSLParameters: " +
maxPacketSize);
ret = this.ssl.dtlsSetMTU(maxPacketSize);
if (ret == WolfSSL.SSL_SUCCESS) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "set maximum packet size (DTLS MTU): " +
maxPacketSize);
}
else if (ret == WolfSSL.NOT_COMPILED_IN) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "DTLS or MTU not compiled in, skipping setting " +
"max packet size");
}
else {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "error setting DTLS MTU, ret = " + ret);
}
}
}
private void setLocalExtendedMasterSecret() {
/* Native wolfSSL enables TLS Extended Master Secret by default.
* Check the Java System property (jdk.tls.useExtendedMasterSecret)
* to see if the user has explicitly disabled it. */
int ret;
boolean useEMS = WolfSSLUtil.useExtendedMasterSecret();
if (!useEMS) {
ret = this.ssl.disableExtendedMasterSecret();
if (ret == WolfSSL.SSL_SUCCESS) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "TLS Extended Master Secret disabled due to " +
"jdk.tls.useExtendedMasterSecret System property");
}
else {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "Failed to disable TLS Extended Master Secret, " +
"ret = " + ret);
}
}
else {
if (WolfSSL.isEnabledTLSExtendedMasterSecret() == 1) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "using TLS Extended Master Secret");
}
else {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "not using TLS Extended Master Secret, " +
"not compiled in");
}
}
}
private void setLocalParams(SSLSocket socket, SSLEngine engine)
throws SSLException {
this.setLocalCiphers(
WolfSSLUtil.sanitizeSuites(this.params.getCipherSuites()));
this.setLocalProtocol(
WolfSSLUtil.sanitizeProtocols(
this.params.getProtocols(), WolfSSL.TLS_VERSION.INVALID));
this.setLocalAuth(socket, engine);
this.setLocalServerNames();
this.setLocalSessionTicket();
this.setLocalAlpnProtocols();
this.setLocalSecureRenegotiation();
this.setLocalSigAlgorithms();
this.setLocalSupportedCurves();
this.setLocalMaximumPacketSize();
this.setLocalExtendedMasterSecret();
}
/**
* Sets all parameters from WolfSSLParameters into native WOLFSSL object
* and creates session. Accepts reference to SSLSocket which is calling
* this, to be used in ExtendedX509TrustManager hostname verification
* during handshake.
*
* This should be called before doHandshake()
*
* @param socket SSLSocket from which this method is being called.
*
* @throws SSLException if setUseClientMode() has not been called or
* on native socket error
* @throws SSLHandshakeException session creation is not allowed
*
*/
protected synchronized void initHandshake(SSLSocket socket)
throws SSLException {
initHandshakeInternal(socket, null);
}
/**
* Sets all parameters from WolfSSLParameters into native WOLFSSL object
* and creates session. Accepts reference to SSLEngine which is calling
* this, to be used in ExtendedX509TrustManager hostname verification
* during handshake.
*
* This should be called before doHandshake()
*
* @param engine SSLEngine from which this method is being called.
*
* @throws SSLException if setUseClientMode() has not been called or
* on native socket error
* @throws SSLHandshakeException session creation is not allowed
*
*/
protected synchronized void initHandshake(SSLEngine engine)
throws SSLException {
initHandshakeInternal(null, engine);
}
/**
* Private internal method called by initHandshake() variants which
* accept either SSLSocket or SSLEngine.
*
* Only one or the other between SSLSocket or SSLEngien should be provided
* at one time, not both. The other should be set to null.
*
* @param socket SSLSocket from which this method is being called.
* @param engine SSLEngine from which this method is being called.
* @throws SSLHandshakeException session creation is not allowed
*
*/
private void initHandshakeInternal(SSLSocket socket, SSLEngine engine)
throws SSLException {
String sessCacheHostname = this.hostname;
if (!modeSet) {
throw new SSLException("setUseClientMode has not been called");
}
/* If InetAddress was used to create SSLSocket, use IP address for
* session resumption to avoid DNS lookup with
* InetAddress.getHostName(). Can cause performance issues if DNS server
* is not available and timeout is long. */
if (sessCacheHostname == null && this.peerAddr != null) {
sessCacheHostname = this.peerAddr.getHostAddress();
}
/* create non null session */
this.session = this.authStore.getSession(ssl, this.port,
sessCacheHostname, this.clientMode, getCiphers(), getProtocols());
if (this.session != null) {
if (this.clientMode) {
this.session.setSessionContext(authStore.getClientContext());
this.session.setSide(WolfSSL.WOLFSSL_CLIENT_END);
}
else {
this.session.setSessionContext(authStore.getServerContext());
this.session.setSide(WolfSSL.WOLFSSL_SERVER_END);
}
if (this.sessionCreation == false && !this.session.isFromTable) {
/* new handshakes can not be made in this case. */
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "session creation not allowed");
/* send CloseNotify */
/* TODO: SunJSSE sends a Handshake Failure alert instead here */
try {
this.ssl.shutdownSSL();
} catch (SocketException | SocketTimeoutException e) {
throw new SSLException(e);
}
throw new SSLHandshakeException("Session creation not allowed");
}
}
this.setLocalParams(socket, engine);
}
/**
* Start or continue handshake
*
* Callers should not loop on WANT_READ/WRITE when used with SSLEngine.
*
* @param isSSLEngine specifies if this is being called by an SSLEngine
* or not.
* @param timeout socket timeout (milliseconds) for connect(), or 0 for
* infinite/no timeout.
* @return WolfSSL.SSL_SUCCESS on success or either WolfSSL.SSL_FAILURE
* or WolfSSL.SSL_HANDSHAKE_FAILURE on error
*
* @throws SSLException if setUseClientMode() has not been called or
* on native socket error
* @throws SocketTimeoutException if socket timed out
*
* @throws WolfSSLException if it fails to check the DH key size after
* the handshake.
*/
protected synchronized int doHandshake(int isSSLEngine, int timeout)
throws SSLException, SocketTimeoutException, WolfSSLException {
int ret, err;
byte[] serverId = null;
String hostAddress = null;
String sessCacheHostname = this.hostname;
if (!modeSet) {
throw new SSLException("setUseClientMode has not been called");
}
if (this.sessionCreation == false && !this.session.isFromTable) {
/* new handshakes can not be made in this case. */
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "session creation not allowed");
try {
/* send CloseNotify */
/* TODO: SunJSSE sends a Handshake Failure alert instead here */
this.ssl.shutdownSSL();
} catch (SocketException e) {
throw new SSLException(e);
}
return WolfSSL.SSL_HANDSHAKE_FAILURE;
}
if ((this.session == null) || !this.session.isValid()) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "session is marked as invalid, try creating a " +
"new session");
if (this.sessionCreation == false) {
/* new handshakes can not be made in this case. */
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "session creation not allowed");
return WolfSSL.SSL_HANDSHAKE_FAILURE;
}
if (sessCacheHostname == null && this.peerAddr != null) {
sessCacheHostname = this.peerAddr.getHostAddress();
}
this.session = this.authStore.getSession(ssl, this.clientMode,
sessCacheHostname, this.port);
}
if (this.clientMode) {
/* Associate host:port as serverID for client session cache,
* helps native wolfSSL for TLS 1.3 sessions with no session ID.
* If host is null and Socket was created with InetAddress only,
* try to use IP address:port instead. If both are null, skip
* setting serverID. Setting newSession to 1 for setServerID since
* we are controlling get/set session from Java */
if (hostname != null) {
serverId = this.hostname.concat(
Integer.toString(this.port)).getBytes();
}
else if (peerAddr != null) {
hostAddress = this.peerAddr.getHostAddress();
if (hostAddress != null) {
serverId = hostAddress.concat(
Integer.toString(this.port)).getBytes();
}
}
if (serverId == null) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "null serverId when trying to generate, not setting");
} else {
ret = this.ssl.setServerID(serverId, 1);
if (ret != WolfSSL.SSL_SUCCESS) {
return WolfSSL.SSL_HANDSHAKE_FAILURE;
}
}
}
do {
/* call connect() or accept() to do handshake, looping on
* WANT_READ/WANT_WRITE errors in case underlying Socket is
* non-blocking */
try {
if (this.clientMode) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "calling native wolfSSL_connect()");
/* may throw SocketTimeoutException on socket timeout */
ret = this.ssl.connect(timeout);
checkKeySize(ssl, this.clientMode);
} else {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "calling native wolfSSL_accept()");
ret = this.ssl.accept(timeout);
checkKeySize(ssl, this.clientMode);
}
err = ssl.getError(ret);
} catch (SocketException e) {
/* SocketException may be thrown if native socket
* select/poll() fails. Propogate errno back inside new
* SSLException. */
throw new SSLException(e);
}
} while (ret != WolfSSL.SSL_SUCCESS && isSSLEngine == 0 &&
(err == WolfSSL.SSL_ERROR_WANT_READ ||
err == WolfSSL.SSL_ERROR_WANT_WRITE));
/* Update cached values in WolfSSLImplementSSLSession from
* WolfSSLSession, in case that goes out of scope and is garbage
* collected (ex: protocol version). */
this.session.updateStoredSessionValues();
if (!this.clientMode && !matchSNI()) {
throw new SSLHandshakeException(
"Unrecognized Server Name");
}
return ret;
}
private void checkKeySize(WolfSSLSession ssl, boolean clientMode)
throws SSLException, WolfSSLException {
int keySize = this.ssl.getKeySize();
/*
* Before we update the cached values, and return from the handshake,
* we check if we are running a legacy cipher suite, if so, we make sure
* that the actual key size is at least 1024 bits.
*/
String[] cipherSuites = getCiphers();
if (containsDHECiphers(cipherSuites)) {
/* Get the minimum DH key size from security settings. */
int minDHEKeySize;
try {
minDHEKeySize =
WolfSSLUtil.getDisabledAlgorithmsKeySizeLimit("DH");
/*
* If we're trying to use DHE with
* insufficient key size, throw early. */
if (isLegacyDHEnabled() && keySize < minDHEKeySize) {
if (clientMode) {
throw new SSLHandshakeException(
"DH ServerKeyExchange does not comply to " +
"algorithm constraints");
} else {
throw new SSLHandshakeException(
"Received fatal alert: insufficient_security");
}
}
} catch (WolfSSLException e) {
throw new WolfSSLException(
"Failed to check DH key size constraints: ", e);
}
}
}
private boolean containsDHECiphers(String[] cipherSuites) {
for (String suite : cipherSuites) {
if (suite.contains("_DHE_")) {
return true;
}
}
return false;
}
private boolean isLegacyDHEnabled() {
/* Check if legacy DH is enabled through system properties. */
String dhKeySize = System.getProperty("jdk.tls.ephemeralDHKeySize");
return "legacy".equals(dhKeySize);
}
/**
* Validates Server Name Indication (SNI) match between client request and
* server matchers.
*
* This helper method is used only on the server side during the TLS
* handshake to check if there is a server name in the list of requested
* server names that matches the SNI matcher parameter. The check will be
* ignored (return true) if no requested server name were sent by client
* or if the SNI matcher parameter has not been set.
*
* Triggers an SSLHandshakeException on server side during handshake when
* false.
*
* @return true on success or false if no match was found
*/
protected synchronized boolean matchSNI(){
List <SNIMatcher> matchers = this.params.getSNIMatchers();
if (matchers != null && !matchers.isEmpty()) {
/* Match a server name to SNI requested by Client */
List <SNIServerName> serverNames = this.session
.getRequestedServerNames();
if (serverNames != null && !serverNames.isEmpty()) {
for (SNIServerName serverName : serverNames) {
if (serverName.getType() == WolfSSL.WOLFSSL_SNI_HOST_NAME) {
/* If the SNI is of type WOLFSSL_SNI_HOST_NAME, compare
* the name to matchers list */
for (SNIMatcher matcher : matchers) {
if (matcher.matches(serverName)) {
/* If a match is found, accept the server name
* and return true value, break from loop */
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "Accepted SNI: " + serverName);
return true;
}
}
}
}
} else {
/* If server names are null or empty, ignore server name
* indication */
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "No server names found, ignoring SNI");
return true;
}
} else {
/* If matchers are null or empty, ignore server name indication */
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "No SNIMatchers set");
return true;
}
return false;
}
/**
* Unset the native verify callback and reset internal verify
* callback state.
*
* This helper method is called by SSLEngine to reset the native
* wolfSSL verify callback back to null. Since a pointer to that verify
* callback is stored as a global JNI variable, it can prevent garbage
* collection from being done. This helper can be called when an SSLEngine
* or SSLSocket is closed/done to reset the verify callback.
*
* The verify callback will be set again if needed when
* initHandshake() is called.
*/
protected synchronized void unsetVerifyCallback() {
/* Set native callback to null, releases JNI global and allows for
* garbage collection if needed */
if (this.ssl != null) {
this.ssl.setVerify(this.verifyMask, null);
}
/* Reset internal state of WolfSSLInternalVerifyCallback, removes
* references to SSLSocket/SSLEngine to allow garbage collection if
* needed */
if (this.wicb != null) {
this.wicb.clearInternalVars();
this.wicb = null;
}
}
/**
* Saves session on connection close for resumption
*
* @return WolfSSL.SSL_SUCCESS if session was saved into cache, otherwise
* WolfSSL.SSL_FAILURE
*/
protected synchronized int saveSession() {
if (this.session != null && this.session.isValid()) {
/* Update values from WOLFSSL which are stored in
* WolfSSLImplementSSLSession (ex: protocol) */
this.session.updateStoredSessionValues();
if (this.clientMode) {
/* Only need to set resume on client side, server-side
* maintains session cache at native level. */
this.session.setResume();
}
if (WolfSSLUtil.sessionCacheDisabled()) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "not storing session in cache, cache has " +
"been disabled");
} else {
return this.authStore.addSession(this.session);
}
}
return WolfSSL.SSL_FAILURE;
}
/**
* Clear internal state of this WolfSSLEngineHelper.
*/
protected synchronized void clearObjectState() {
this.ssl = null;
this.session = null;
this.params = null;
this.authStore = null;
}
@SuppressWarnings("deprecation")
@Override
protected synchronized void finalize() throws Throwable {
/* Reset this.ssl to null, but don't explicitly free. This object
* may be used by wrapper object to WolfSSLEngineHelper and should
* be freed there */
this.ssl = null;
this.wicb = null;
this.session = null;
this.params = null;
this.authStore = null;
super.finalize();
}
}