Ensure OpenSslSession informations can be retrieved even after shutdown
Motivation: If a user tries to access various informations on the OpenSslSession after the SSLEngine was closed it will not work if these were not accessed before as we lazy init most of them. Modifications: Directly populate the whole OpenSslSession once the handshake is complete and before the user is notified about it. Result: OpenSslSession informations are avaible until it is GC'ed.
This commit is contained in:
parent
0f6559d188
commit
0f95b85ec2
@ -37,7 +37,6 @@ import javax.net.ssl.SSLSession;
|
|||||||
import javax.net.ssl.SSLSessionBindingEvent;
|
import javax.net.ssl.SSLSessionBindingEvent;
|
||||||
import javax.net.ssl.SSLSessionBindingListener;
|
import javax.net.ssl.SSLSessionBindingListener;
|
||||||
import javax.net.ssl.SSLSessionContext;
|
import javax.net.ssl.SSLSessionContext;
|
||||||
import javax.security.cert.CertificateException;
|
|
||||||
import javax.security.cert.X509Certificate;
|
import javax.security.cert.X509Certificate;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.ReadOnlyBufferException;
|
import java.nio.ReadOnlyBufferException;
|
||||||
@ -158,13 +157,6 @@ public final class OpenSslEngine extends SSLEngine {
|
|||||||
@SuppressWarnings("UnusedDeclaration")
|
@SuppressWarnings("UnusedDeclaration")
|
||||||
private volatile int destroyed;
|
private volatile int destroyed;
|
||||||
|
|
||||||
// Use an invalid cipherSuite until the handshake is completed
|
|
||||||
// See http://docs.oracle.com/javase/7/docs/api/javax/net/ssl/SSLEngine.html#getSession()
|
|
||||||
private volatile String cipher;
|
|
||||||
private volatile String applicationProtocol;
|
|
||||||
|
|
||||||
// We store this outside of the SslSession so we not need to create an instance during verifyCertificates(...)
|
|
||||||
private volatile Certificate[] peerCerts;
|
|
||||||
private volatile ClientAuthMode clientAuth = ClientAuthMode.NONE;
|
private volatile ClientAuthMode clientAuth = ClientAuthMode.NONE;
|
||||||
|
|
||||||
private volatile String endPointIdentificationAlgorithm;
|
private volatile String endPointIdentificationAlgorithm;
|
||||||
@ -178,11 +170,10 @@ public final class OpenSslEngine extends SSLEngine {
|
|||||||
|
|
||||||
private final boolean clientMode;
|
private final boolean clientMode;
|
||||||
private final ByteBufAllocator alloc;
|
private final ByteBufAllocator alloc;
|
||||||
private final OpenSslSessionContext sessionContext;
|
|
||||||
private final OpenSslEngineMap engineMap;
|
private final OpenSslEngineMap engineMap;
|
||||||
private final OpenSslApplicationProtocolNegotiator apn;
|
private final OpenSslApplicationProtocolNegotiator apn;
|
||||||
private final boolean rejectRemoteInitiatedRenegation;
|
private final boolean rejectRemoteInitiatedRenegation;
|
||||||
private final SSLSession session = new OpenSslSession();
|
private final OpenSslSession session;
|
||||||
|
|
||||||
// This is package-private as we set it from OpenSslContext if an exception is thrown during
|
// This is package-private as we set it from OpenSslContext if an exception is thrown during
|
||||||
// the verification step.
|
// the verification step.
|
||||||
@ -228,9 +219,9 @@ public final class OpenSslEngine extends SSLEngine {
|
|||||||
this.alloc = checkNotNull(alloc, "alloc");
|
this.alloc = checkNotNull(alloc, "alloc");
|
||||||
this.apn = checkNotNull(apn, "apn");
|
this.apn = checkNotNull(apn, "apn");
|
||||||
ssl = SSL.newSSL(sslCtx, !clientMode);
|
ssl = SSL.newSSL(sslCtx, !clientMode);
|
||||||
|
session = new OpenSslSession(ssl, sessionContext);
|
||||||
networkBIO = SSL.makeNetworkBIO(ssl);
|
networkBIO = SSL.makeNetworkBIO(ssl);
|
||||||
this.clientMode = clientMode;
|
this.clientMode = clientMode;
|
||||||
this.sessionContext = sessionContext;
|
|
||||||
this.engineMap = engineMap;
|
this.engineMap = engineMap;
|
||||||
this.rejectRemoteInitiatedRenegation = rejectRemoteInitiatedRenegation;
|
this.rejectRemoteInitiatedRenegation = rejectRemoteInitiatedRenegation;
|
||||||
}
|
}
|
||||||
@ -458,7 +449,7 @@ public final class OpenSslEngine extends SSLEngine {
|
|||||||
final ByteBuffer[] srcs, final int offset, final int length, final ByteBuffer dst) throws SSLException {
|
final ByteBuffer[] srcs, final int offset, final int length, final ByteBuffer dst) throws SSLException {
|
||||||
|
|
||||||
// Check to make sure the engine has not been closed
|
// Check to make sure the engine has not been closed
|
||||||
if (destroyed != 0) {
|
if (isDestroyed()) {
|
||||||
return CLOSED_NOT_HANDSHAKING;
|
return CLOSED_NOT_HANDSHAKING;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -585,7 +576,7 @@ public final class OpenSslEngine extends SSLEngine {
|
|||||||
final ByteBuffer[] dsts, final int dstsOffset, final int dstsLength) throws SSLException {
|
final ByteBuffer[] dsts, final int dstsOffset, final int dstsLength) throws SSLException {
|
||||||
|
|
||||||
// Check to make sure the engine has not been closed
|
// Check to make sure the engine has not been closed
|
||||||
if (destroyed != 0) {
|
if (isDestroyed()) {
|
||||||
return CLOSED_NOT_HANDSHAKING;
|
return CLOSED_NOT_HANDSHAKING;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -849,7 +840,7 @@ public final class OpenSslEngine extends SSLEngine {
|
|||||||
isOutboundDone = true;
|
isOutboundDone = true;
|
||||||
engineClosed = true;
|
engineClosed = true;
|
||||||
|
|
||||||
if (handshakeState != HandshakeState.NOT_STARTED && destroyed == 0) {
|
if (handshakeState != HandshakeState.NOT_STARTED && !isDestroyed()) {
|
||||||
int mode = SSL.getShutdown(ssl);
|
int mode = SSL.getShutdown(ssl);
|
||||||
if ((mode & SSL.SSL_SENT_SHUTDOWN) != SSL.SSL_SENT_SHUTDOWN) {
|
if ((mode & SSL.SSL_SENT_SHUTDOWN) != SSL.SSL_SENT_SHUTDOWN) {
|
||||||
int err = SSL.shutdownSSL(ssl);
|
int err = SSL.shutdownSSL(ssl);
|
||||||
@ -900,7 +891,7 @@ public final class OpenSslEngine extends SSLEngine {
|
|||||||
public String[] getEnabledCipherSuites() {
|
public String[] getEnabledCipherSuites() {
|
||||||
final String[] enabled;
|
final String[] enabled;
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (destroyed == 0) {
|
if (!isDestroyed()) {
|
||||||
enabled = SSL.getCiphers(ssl);
|
enabled = SSL.getCiphers(ssl);
|
||||||
} else {
|
} else {
|
||||||
return EmptyArrays.EMPTY_STRINGS;
|
return EmptyArrays.EMPTY_STRINGS;
|
||||||
@ -950,7 +941,7 @@ public final class OpenSslEngine extends SSLEngine {
|
|||||||
final String cipherSuiteSpec = buf.toString();
|
final String cipherSuiteSpec = buf.toString();
|
||||||
|
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (destroyed == 0) {
|
if (!isDestroyed()) {
|
||||||
try {
|
try {
|
||||||
SSL.setCipherSuites(ssl, cipherSuiteSpec);
|
SSL.setCipherSuites(ssl, cipherSuiteSpec);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -975,7 +966,7 @@ public final class OpenSslEngine extends SSLEngine {
|
|||||||
|
|
||||||
int opts;
|
int opts;
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (destroyed == 0) {
|
if (!isDestroyed()) {
|
||||||
opts = SSL.getOptions(ssl);
|
opts = SSL.getOptions(ssl);
|
||||||
} else {
|
} else {
|
||||||
return enabled.toArray(new String[1]);
|
return enabled.toArray(new String[1]);
|
||||||
@ -1027,7 +1018,7 @@ public final class OpenSslEngine extends SSLEngine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (destroyed == 0) {
|
if (!isDestroyed()) {
|
||||||
// Enable all and then disable what we not want
|
// Enable all and then disable what we not want
|
||||||
SSL.setOptions(ssl, SSL.SSL_OP_ALL);
|
SSL.setOptions(ssl, SSL.SSL_OP_ALL);
|
||||||
|
|
||||||
@ -1084,7 +1075,7 @@ public final class OpenSslEngine extends SSLEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void checkEngineClosed() throws SSLException {
|
private void checkEngineClosed() throws SSLException {
|
||||||
if (engineClosed || destroyed != 0) {
|
if (engineClosed || isDestroyed()) {
|
||||||
throw ENGINE_CLOSED;
|
throw ENGINE_CLOSED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1113,7 +1104,8 @@ public final class OpenSslEngine extends SSLEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if SSL_do_handshake returns > 0 or sslError == SSL.SSL_ERROR_NAME it means the handshake was finished.
|
// if SSL_do_handshake returns > 0 or sslError == SSL.SSL_ERROR_NAME it means the handshake was finished.
|
||||||
handshakeFinished();
|
session.handshakeFinished();
|
||||||
|
|
||||||
return FINISHED;
|
return FINISHED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1125,62 +1117,6 @@ public final class OpenSslEngine extends SSLEngine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handshakeFinished() throws SSLException {
|
|
||||||
SelectedListenerFailureBehavior behavior = apn.selectedListenerFailureBehavior();
|
|
||||||
List<String> protocols = apn.protocols();
|
|
||||||
String applicationProtocol;
|
|
||||||
switch (apn.protocol()) {
|
|
||||||
case NONE:
|
|
||||||
break;
|
|
||||||
// We always need to check for applicationProtocol == null as the remote peer may not support
|
|
||||||
// the TLS extension or may have returned an empty selection.
|
|
||||||
case ALPN:
|
|
||||||
applicationProtocol = SSL.getAlpnSelected(ssl);
|
|
||||||
if (applicationProtocol != null) {
|
|
||||||
this.applicationProtocol = selectApplicationProtocol(protocols, behavior, applicationProtocol);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case NPN:
|
|
||||||
applicationProtocol = SSL.getNextProtoNegotiated(ssl);
|
|
||||||
if (applicationProtocol != null) {
|
|
||||||
this.applicationProtocol = selectApplicationProtocol(protocols, behavior, applicationProtocol);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case NPN_AND_ALPN:
|
|
||||||
applicationProtocol = SSL.getAlpnSelected(ssl);
|
|
||||||
if (applicationProtocol == null) {
|
|
||||||
applicationProtocol = SSL.getNextProtoNegotiated(ssl);
|
|
||||||
}
|
|
||||||
if (applicationProtocol != null) {
|
|
||||||
this.applicationProtocol = selectApplicationProtocol(protocols, behavior, applicationProtocol);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error();
|
|
||||||
}
|
|
||||||
handshakeState = HandshakeState.FINISHED;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String selectApplicationProtocol(List<String> protocols,
|
|
||||||
SelectedListenerFailureBehavior behavior,
|
|
||||||
String applicationProtocol) throws SSLException {
|
|
||||||
if (behavior == SelectedListenerFailureBehavior.ACCEPT) {
|
|
||||||
return applicationProtocol;
|
|
||||||
} else {
|
|
||||||
int size = protocols.size();
|
|
||||||
assert size > 0;
|
|
||||||
if (protocols.contains(applicationProtocol)) {
|
|
||||||
return applicationProtocol;
|
|
||||||
} else {
|
|
||||||
if (behavior == SelectedListenerFailureBehavior.CHOOSE_MY_LAST_PROTOCOL) {
|
|
||||||
return protocols.get(size - 1);
|
|
||||||
} else {
|
|
||||||
throw new SSLException("unknown protocol " + applicationProtocol);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private SSLEngineResult.Status getEngineStatus() {
|
private SSLEngineResult.Status getEngineStatus() {
|
||||||
return engineClosed? CLOSED : OK;
|
return engineClosed? CLOSED : OK;
|
||||||
}
|
}
|
||||||
@ -1213,7 +1149,7 @@ public final class OpenSslEngine extends SSLEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean needPendingStatus() {
|
private boolean needPendingStatus() {
|
||||||
return handshakeState != HandshakeState.NOT_STARTED && destroyed == 0
|
return handshakeState != HandshakeState.NOT_STARTED && !isDestroyed()
|
||||||
&& (handshakeState != HandshakeState.FINISHED || engineClosed);
|
&& (handshakeState != HandshakeState.FINISHED || engineClosed);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1347,29 +1283,39 @@ public final class OpenSslEngine extends SSLEngine {
|
|||||||
shutdown();
|
shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isDestroyed() {
|
||||||
|
return destroyed != 0;
|
||||||
|
}
|
||||||
|
|
||||||
private final class OpenSslSession implements SSLSession, ApplicationProtocolAccessor {
|
private final class OpenSslSession implements SSLSession, ApplicationProtocolAccessor {
|
||||||
// SSLSession implementation seems to not need to be thread-safe so no need for volatile etc.
|
private final OpenSslSessionContext sessionContext;
|
||||||
|
private final long creationTime;
|
||||||
|
|
||||||
|
// These are guarded by synchronized(OpenSslEngine.this) as handshakeFinished() may be triggered by any
|
||||||
|
// thread.
|
||||||
private X509Certificate[] x509PeerCerts;
|
private X509Certificate[] x509PeerCerts;
|
||||||
|
private String protocol;
|
||||||
|
private String applicationProtocol;
|
||||||
|
private Certificate[] peerCerts;
|
||||||
|
private String cipher;
|
||||||
|
private byte[] id;
|
||||||
|
|
||||||
// lazy init for memory reasons
|
// lazy init for memory reasons
|
||||||
private Map<String, Object> values;
|
private Map<String, Object> values;
|
||||||
|
|
||||||
|
OpenSslSession(long ssl, OpenSslSessionContext sessionContext) {
|
||||||
|
creationTime = SSL.getTime(ssl) * 1000L;
|
||||||
|
this.sessionContext = sessionContext;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] getId() {
|
public byte[] getId() {
|
||||||
final byte[] id;
|
|
||||||
synchronized (OpenSslEngine.this) {
|
synchronized (OpenSslEngine.this) {
|
||||||
if (destroyed == 0) {
|
if (id == null) {
|
||||||
id = SSL.getSessionId(ssl);
|
return EmptyArrays.EMPTY_BYTES;
|
||||||
} else {
|
|
||||||
id = EmptyArrays.EMPTY_BYTES;
|
|
||||||
}
|
}
|
||||||
|
return id.clone();
|
||||||
}
|
}
|
||||||
// We don't cache that to keep memory usage to a minimum.
|
|
||||||
if (id == null) {
|
|
||||||
// The id should never be null, if it was null then the SESSION itself was not valid.
|
|
||||||
throw new IllegalStateException("SSL session ID not available");
|
|
||||||
}
|
|
||||||
return id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -1379,13 +1325,7 @@ public final class OpenSslEngine extends SSLEngine {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getCreationTime() {
|
public long getCreationTime() {
|
||||||
synchronized (OpenSslEngine.this) {
|
return creationTime;
|
||||||
if (destroyed == 0) {
|
|
||||||
// We need ot multiple by 1000 as openssl uses seconds and we need milli-seconds.
|
|
||||||
return SSL.getTime(ssl) * 1000L;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -1463,23 +1403,146 @@ public final class OpenSslEngine extends SSLEngine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
|
* Finish the handshake and so init everything in the {@link OpenSslSession} that should be accessable by
|
||||||
// these are lazy created to reduce memory overhead
|
* the user.
|
||||||
Certificate[] c = peerCerts;
|
*/
|
||||||
if (c == null) {
|
void handshakeFinished() throws SSLException {
|
||||||
synchronized (OpenSslEngine.this) {
|
synchronized (OpenSslEngine.this) {
|
||||||
if (destroyed == 0) {
|
if (!isDestroyed()) {
|
||||||
if (SSL.isInInit(ssl) != 0) {
|
id = SSL.getSessionId(ssl);
|
||||||
throw new SSLPeerUnverifiedException("peer not verified");
|
cipher = toJavaCipherSuite(SSL.getCipherForSSL(ssl));
|
||||||
}
|
protocol = SSL.getVersion(ssl);
|
||||||
c = peerCerts = initPeerCertChain();
|
|
||||||
|
initPeerCerts();
|
||||||
|
selectApplicationProtocol();
|
||||||
|
|
||||||
|
handshakeState = HandshakeState.FINISHED;
|
||||||
|
} else {
|
||||||
|
throw new SSLException("Already closed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Init peer certificates that can be obtained via {@link #getPeerCertificateChain()}
|
||||||
|
* and {@link #getPeerCertificates()}.
|
||||||
|
*/
|
||||||
|
private void initPeerCerts() {
|
||||||
|
// Return the full chain from the JNI layer.
|
||||||
|
byte[][] chain = SSL.getPeerCertChain(ssl);
|
||||||
|
final byte[] clientCert;
|
||||||
|
if (!clientMode) {
|
||||||
|
// if used on the server side SSL_get_peer_cert_chain(...) will not include the remote peer
|
||||||
|
// certificate. We use SSL_get_peer_certificate to get it in this case and add it to our
|
||||||
|
// array later.
|
||||||
|
//
|
||||||
|
// See https://www.openssl.org/docs/ssl/SSL_get_peer_cert_chain.html
|
||||||
|
clientCert = SSL.getPeerCertificate(ssl);
|
||||||
|
} else {
|
||||||
|
clientCert = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chain == null && clientCert == null) {
|
||||||
|
peerCerts = EMPTY_CERTIFICATES;
|
||||||
|
x509PeerCerts = EMPTY_X509_CERTIFICATES;
|
||||||
|
} else {
|
||||||
|
int len = chain != null ? chain.length : 0;
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
Certificate[] peerCerts;
|
||||||
|
if (clientCert != null) {
|
||||||
|
len++;
|
||||||
|
peerCerts = new Certificate[len];
|
||||||
|
peerCerts[i++] = new OpenSslX509Certificate(clientCert);
|
||||||
|
} else {
|
||||||
|
peerCerts = new Certificate[len];
|
||||||
|
}
|
||||||
|
if (chain != null) {
|
||||||
|
X509Certificate[] pCerts = new X509Certificate[chain.length];
|
||||||
|
|
||||||
|
for (int a = 0; a < pCerts.length; ++i, ++a) {
|
||||||
|
byte[] bytes = chain[a];
|
||||||
|
pCerts[a] = new OpenSslJavaxX509Certificate(bytes);
|
||||||
|
peerCerts[i] = new OpenSslX509Certificate(bytes);
|
||||||
|
}
|
||||||
|
x509PeerCerts = pCerts;
|
||||||
|
} else {
|
||||||
|
x509PeerCerts = EMPTY_X509_CERTIFICATES;
|
||||||
|
}
|
||||||
|
this.peerCerts = peerCerts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select the application protocol used.
|
||||||
|
*/
|
||||||
|
private void selectApplicationProtocol() throws SSLException {
|
||||||
|
SelectedListenerFailureBehavior behavior = apn.selectedListenerFailureBehavior();
|
||||||
|
List<String> protocols = apn.protocols();
|
||||||
|
String applicationProtocol;
|
||||||
|
switch (apn.protocol()) {
|
||||||
|
case NONE:
|
||||||
|
break;
|
||||||
|
// We always need to check for applicationProtocol == null as the remote peer may not support
|
||||||
|
// the TLS extension or may have returned an empty selection.
|
||||||
|
case ALPN:
|
||||||
|
applicationProtocol = SSL.getAlpnSelected(ssl);
|
||||||
|
if (applicationProtocol != null) {
|
||||||
|
this.applicationProtocol = selectApplicationProtocol(
|
||||||
|
protocols, behavior, applicationProtocol);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NPN:
|
||||||
|
applicationProtocol = SSL.getNextProtoNegotiated(ssl);
|
||||||
|
if (applicationProtocol != null) {
|
||||||
|
this.applicationProtocol = selectApplicationProtocol(
|
||||||
|
protocols, behavior, applicationProtocol);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NPN_AND_ALPN:
|
||||||
|
applicationProtocol = SSL.getAlpnSelected(ssl);
|
||||||
|
if (applicationProtocol == null) {
|
||||||
|
applicationProtocol = SSL.getNextProtoNegotiated(ssl);
|
||||||
|
}
|
||||||
|
if (applicationProtocol != null) {
|
||||||
|
this.applicationProtocol = selectApplicationProtocol(
|
||||||
|
protocols, behavior, applicationProtocol);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String selectApplicationProtocol(List<String> protocols,
|
||||||
|
SelectedListenerFailureBehavior behavior,
|
||||||
|
String applicationProtocol) throws SSLException {
|
||||||
|
if (behavior == SelectedListenerFailureBehavior.ACCEPT) {
|
||||||
|
return applicationProtocol;
|
||||||
|
} else {
|
||||||
|
int size = protocols.size();
|
||||||
|
assert size > 0;
|
||||||
|
if (protocols.contains(applicationProtocol)) {
|
||||||
|
return applicationProtocol;
|
||||||
|
} else {
|
||||||
|
if (behavior == SelectedListenerFailureBehavior.CHOOSE_MY_LAST_PROTOCOL) {
|
||||||
|
return protocols.get(size - 1);
|
||||||
} else {
|
} else {
|
||||||
c = peerCerts = EMPTY_CERTIFICATES;
|
throw new SSLException("unknown protocol " + applicationProtocol);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return c;
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
|
||||||
|
synchronized (OpenSslEngine.this) {
|
||||||
|
if (peerCerts == null) {
|
||||||
|
throw new SSLPeerUnverifiedException("peer not verified");
|
||||||
|
}
|
||||||
|
return peerCerts;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -1490,35 +1553,12 @@ public final class OpenSslEngine extends SSLEngine {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException {
|
public X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException {
|
||||||
// these are lazy created to reduce memory overhead
|
synchronized (OpenSslEngine.this) {
|
||||||
X509Certificate[] c = x509PeerCerts;
|
if (x509PeerCerts == null) {
|
||||||
if (c == null) {
|
|
||||||
final byte[][] chain;
|
|
||||||
synchronized (OpenSslEngine.this) {
|
|
||||||
if (destroyed == 0) {
|
|
||||||
if (SSL.isInInit(ssl) != 0) {
|
|
||||||
throw new SSLPeerUnverifiedException("peer not verified");
|
|
||||||
}
|
|
||||||
chain = SSL.getPeerCertChain(ssl);
|
|
||||||
} else {
|
|
||||||
c = x509PeerCerts = EMPTY_X509_CERTIFICATES;
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (chain == null) {
|
|
||||||
throw new SSLPeerUnverifiedException("peer not verified");
|
throw new SSLPeerUnverifiedException("peer not verified");
|
||||||
}
|
}
|
||||||
X509Certificate[] peerCerts = new X509Certificate[chain.length];
|
return x509PeerCerts;
|
||||||
for (int i = 0; i < peerCerts.length; i++) {
|
|
||||||
try {
|
|
||||||
peerCerts[i] = X509Certificate.getInstance(chain[i]);
|
|
||||||
} catch (CertificateException e) {
|
|
||||||
throw new IllegalStateException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c = x509PeerCerts = peerCerts;
|
|
||||||
}
|
}
|
||||||
return c;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -1541,39 +1581,34 @@ public final class OpenSslEngine extends SSLEngine {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getCipherSuite() {
|
public String getCipherSuite() {
|
||||||
if (handshakeState != HandshakeState.FINISHED) {
|
synchronized (OpenSslEngine.this) {
|
||||||
return INVALID_CIPHER;
|
if (cipher == null) {
|
||||||
}
|
return INVALID_CIPHER;
|
||||||
if (cipher == null) {
|
|
||||||
final String c;
|
|
||||||
synchronized (OpenSslEngine.this) {
|
|
||||||
if (destroyed == 0) {
|
|
||||||
c = toJavaCipherSuite(SSL.getCipherForSSL(ssl));
|
|
||||||
} else {
|
|
||||||
c = INVALID_CIPHER;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (c != null) {
|
|
||||||
cipher = c;
|
|
||||||
}
|
}
|
||||||
|
return cipher;
|
||||||
}
|
}
|
||||||
return cipher;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getProtocol() {
|
public String getProtocol() {
|
||||||
synchronized (OpenSslEngine.this) {
|
String protocol = this.protocol;
|
||||||
if (destroyed == 0) {
|
if (protocol == null) {
|
||||||
return SSL.getVersion(ssl);
|
synchronized (OpenSslEngine.this) {
|
||||||
} else {
|
if (!isDestroyed()) {
|
||||||
return StringUtil.EMPTY_STRING;
|
protocol = SSL.getVersion(ssl);
|
||||||
|
} else {
|
||||||
|
protocol = StringUtil.EMPTY_STRING;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return protocol;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getApplicationProtocol() {
|
public String getApplicationProtocol() {
|
||||||
return applicationProtocol;
|
synchronized (OpenSslEngine.this) {
|
||||||
|
return applicationProtocol;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -1595,44 +1630,5 @@ public final class OpenSslEngine extends SSLEngine {
|
|||||||
public int getApplicationBufferSize() {
|
public int getApplicationBufferSize() {
|
||||||
return MAX_PLAINTEXT_LENGTH;
|
return MAX_PLAINTEXT_LENGTH;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Certificate[] initPeerCertChain() throws SSLPeerUnverifiedException {
|
|
||||||
byte[][] chain = SSL.getPeerCertChain(ssl);
|
|
||||||
final byte[] clientCert;
|
|
||||||
if (!clientMode) {
|
|
||||||
// if used on the server side SSL_get_peer_cert_chain(...) will not include the remote peer certificate.
|
|
||||||
// We use SSL_get_peer_certificate to get it in this case and add it to our array later.
|
|
||||||
//
|
|
||||||
// See https://www.openssl.org/docs/ssl/SSL_get_peer_cert_chain.html
|
|
||||||
clientCert = SSL.getPeerCertificate(ssl);
|
|
||||||
} else {
|
|
||||||
clientCert = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (chain == null && clientCert == null) {
|
|
||||||
throw new SSLPeerUnverifiedException("peer not verified");
|
|
||||||
}
|
|
||||||
int len = 0;
|
|
||||||
if (chain != null) {
|
|
||||||
len += chain.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
Certificate[] peerCerts;
|
|
||||||
if (clientCert != null) {
|
|
||||||
len++;
|
|
||||||
peerCerts = new Certificate[len];
|
|
||||||
peerCerts[i++] = new OpenSslX509Certificate(clientCert);
|
|
||||||
} else {
|
|
||||||
peerCerts = new Certificate[len];
|
|
||||||
}
|
|
||||||
if (chain != null) {
|
|
||||||
int a = 0;
|
|
||||||
for (; i < peerCerts.length; i++) {
|
|
||||||
peerCerts[i] = new OpenSslX509Certificate(chain[a++]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return peerCerts;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,134 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you under the Apache License,
|
||||||
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at:
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package io.netty.handler.ssl;
|
||||||
|
|
||||||
|
import javax.security.cert.CertificateException;
|
||||||
|
import javax.security.cert.CertificateExpiredException;
|
||||||
|
import javax.security.cert.CertificateNotYetValidException;
|
||||||
|
import javax.security.cert.X509Certificate;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.NoSuchProviderException;
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.security.SignatureException;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
final class OpenSslJavaxX509Certificate extends X509Certificate {
|
||||||
|
private final byte[] bytes;
|
||||||
|
private X509Certificate wrapped;
|
||||||
|
|
||||||
|
public OpenSslJavaxX509Certificate(byte[] bytes) {
|
||||||
|
this.bytes = bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkValidity() throws CertificateExpiredException, CertificateNotYetValidException {
|
||||||
|
unwrap().checkValidity();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkValidity(Date date) throws CertificateExpiredException, CertificateNotYetValidException {
|
||||||
|
unwrap().checkValidity(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getVersion() {
|
||||||
|
return unwrap().getVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BigInteger getSerialNumber() {
|
||||||
|
return unwrap().getSerialNumber();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Principal getIssuerDN() {
|
||||||
|
return unwrap().getIssuerDN();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Principal getSubjectDN() {
|
||||||
|
return unwrap().getSubjectDN();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Date getNotBefore() {
|
||||||
|
return unwrap().getNotBefore();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Date getNotAfter() {
|
||||||
|
return unwrap().getNotAfter();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSigAlgName() {
|
||||||
|
return unwrap().getSigAlgName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSigAlgOID() {
|
||||||
|
return unwrap().getSigAlgOID();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getSigAlgParams() {
|
||||||
|
return unwrap().getSigAlgParams();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getEncoded() {
|
||||||
|
return bytes.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void verify(PublicKey key)
|
||||||
|
throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException,
|
||||||
|
SignatureException {
|
||||||
|
unwrap().verify(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void verify(PublicKey key, String sigProvider)
|
||||||
|
throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException,
|
||||||
|
SignatureException {
|
||||||
|
unwrap().verify(key, sigProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return unwrap().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PublicKey getPublicKey() {
|
||||||
|
return unwrap().getPublicKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
private X509Certificate unwrap() {
|
||||||
|
X509Certificate wrapped = this.wrapped;
|
||||||
|
if (wrapped == null) {
|
||||||
|
try {
|
||||||
|
wrapped = this.wrapped = X509Certificate.getInstance(bytes);
|
||||||
|
} catch (CertificateException e) {
|
||||||
|
throw new IllegalStateException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return wrapped;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user