
org.simpleframework.transport.NegotiationState Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of simple Show documentation
Show all versions of simple Show documentation
Simple is a high performance asynchronous HTTP server for Java
The newest version!
/*
* NegotiationCertificate.java June 2013
*
* Copyright (C) 2013, Niall Gallagher
*
* Licensed 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 org.simpleframework.transport;
import static org.simpleframework.transport.TransportEvent.CERTIFICATE_CHALLENGE;
import static org.simpleframework.transport.TransportEvent.ERROR;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.RunnableFuture;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLSession;
import javax.security.cert.X509Certificate;
import org.simpleframework.transport.trace.Trace;
/**
* The NegotiationCertificate
represents the certificate
* that is sent by a client during a secure HTTPS conversation. This
* may or may not contain an X509 certificate chain from the client.
* If it does not a CertificateChallenge
may be used to
* issue a renegotiation of the connection. One completion of the
* renegotiation the challenge executes a completion operation.
*
* @author Niall Gallagher
*/
class NegotiationState implements Certificate {
/**
* This is used to hold the completion task for the challenge.
*/
private final RunnableFuture future;
/**
* This is the handshake used to acquire the certificate details.
*/
private final Negotiation negotiation;
/**
* This is the challenge used to request the client certificate.
*/
private final Challenge challenge;
/**
* This is the runnable task that is executed on task completion.
*/
private final Delegate delegate;
/**
* This is the socket representing the underlying TCP connection.
*/
private final Socket socket;
/**
* Constructor for the NegotiationCertificate
object.
* This creates an object used to provide certificate details and
* a means to challenge for certificate details for the connected
* client if required.
*
* @param negotiation the negotiation associated with this
* @param socket the underlying TCP connection to the client
*/
public NegotiationState(Negotiation negotiation, Socket socket) {
this.delegate = new Delegate(socket);
this.future = new FutureTask(delegate, this);
this.challenge = new Challenge(socket);
this.negotiation = negotiation;
this.socket = socket;
}
/**
* This is used to determine if the state is in challenge mode.
* In challenge mode a challenge future will be executed on
* completion of the challenge. This will the completion task.
*
* @return this returns true if the state is in challenge mode
*/
public boolean isChallenge() {
return delegate.isSet();
}
/**
* This returns the completion task associated with any challenge
* made for the client certificate. If this returns null then no
* challenge has been made for the client certificate.
*
* @return this returns the challenge completion task if any
*/
public RunnableFuture getFuture() {
return future;
}
/**
* This returns a challenge for the certificate. A challenge is
* issued by providing a Runnable
task which is to
* be executed when the challenge has completed. Typically this
* task should be used to drive completion of an HTTPS request.
*
* @return this returns a challenge for the client certificate
*/
public CertificateChallenge getChallenge() throws Exception {
return challenge;
}
/**
* This will return the X509 certificate chain, if any, that
* has been sent by the client. A certificate chain is typically
* only send when the server explicitly requests the certificate
* on the initial connection or when it is challenged for.
*
* @return this returns the clients X509 certificate chain
*/
public X509Certificate[] getChain() throws Exception {
SSLSession session = getSession();
if(session != null) {
return session.getPeerCertificateChain();
}
return null;
}
/**
* This is used to acquire the SSL session associated with the
* handshake. The session makes all of the details associated
* with the handshake available, including the cipher suites
* used and the SSL context used to create the session.
*
* @return the SSL session associated with the connection
*/
public SSLSession getSession() throws Exception{
SSLEngine engine = socket.getEngine();
if(engine != null) {
return engine.getSession();
}
return null;
}
/**
* This is used to determine if the X509 certificate chain is
* present for the request. If it is not present then a challenge
* can be used to request the certificate.
*
* @return true if the certificate chain is present
*/
public boolean isChainPresent() {
try {
return getChain() != null;
} catch(Exception e) {
return false;
}
}
/**
* The Challenge
object is used to enable the server
* to challenge for the client X509 certificate if desired. It
* performs the challenge by performing an SSL renegotiation to
* request that the client sends the
*/
private class Challenge implements CertificateChallenge {
/**
* This is the SSL engine that is used to begin the handshake.
*/
private final SSLEngine engine;
/**
* This is used to trace the certificate challenge request.
*/
private final Trace trace;
/**
* Constructor for the Challenge
object. This can
* be used to challenge the client for their X509 certificate.
* It does this by performing an SSL renegotiation on the
* existing TCP connection.
*
* @param socket this is the TCP connection to the client
*/
public Challenge(Socket socket) {
this.engine = socket.getEngine();
this.trace = socket.getTrace();
}
/**
* This method will challenge the client for their certificate.
* It does so by performing an SSL renegotiation. Successful
* completion of the SSL renegotiation results in the client
* providing their certificate, and execution of the task.
*/
public Future challenge() {
return challenge(null);
}
/**
* This method will challenge the client for their certificate.
* It does so by performing an SSL renegotiation. Successful
* completion of the SSL renegotiation results in the client
* providing their certificate, and execution of the task.
*
* @param completion task to be run on successful challenge
*/
public Future challenge(Runnable task) {
try {
if(!isChainPresent()) {
resume(task);
} else {
future.run();
}
} catch(Exception cause) {
trace.trace(ERROR, cause);
}
return future;
}
/**
* This method will challenge the client for their certificate.
* It does so by performing an SSL renegotiation. Successful
* completion of the SSL renegotiation results in the client
* providing their certificate, and execution of the task.
*
* @param completion task to be run on successful challenge
*/
private void resume(Runnable task) {
try {
trace.trace(CERTIFICATE_CHALLENGE);
delegate.set(task);
engine.setNeedClientAuth(true);
engine.beginHandshake();
negotiation.resume();
} catch(Exception cause) {
trace.trace(ERROR, cause);
negotiation.cancel();
}
}
}
/**
* The Delegate
is basically a settable runnable object.
* It enables the challenge to set an optional runnable that will
* be executed when the challenge has completed. If the challenge
* has not been given a completion task this runs straight through
* without any state change or action on the certificate.
*/
private class Delegate implements Runnable {
/**
* This is the reference to the runnable that is to be executed.
*/
private final AtomicReference task;
/**
* This is used to determine if the challenge is ready to run.
*/
private final AtomicBoolean ready;
/**
* This is used to trace any errors when running the task.
*/
private final Trace trace;
/**
* Constructor for the Delegate
object. This is
* used to create a wrapper for the completion task so that it
* can be executed safely and have any errors traced.
*
* @param socket this socket the handshake is associated with
*/
public Delegate(Socket socket) {
this.task = new AtomicReference();
this.ready = new AtomicBoolean();
this.trace = socket.getTrace();
}
/**
* This is used to determine if the delegate is ready to be
* used. It is ready only after the completion task has been
* set. When ready a challenge can be executed.
*
* @return this returns true if a completion task is set
*/
public boolean isSet() {
return ready.get();
}
/**
* This is used to set the completion task that is to be executed
* when the challenge has finished. This can be set to null if
* no task is to be executed on completion.
*
* @param runnable the task to run when the challenge finishes
*/
public void set(Runnable runnable) {
ready.set(true);
task.set(runnable);
}
/**
* This is used to run the completion task. If no completion
* task has been set this will run through without any change to
* the state of the certificate. All errors thrown by the task
* will be caught and traced.
*/
public void run() {
try {
Runnable runnable = task.get();
if(runnable != null) {
runnable.run();
}
} catch(Exception cause) {
trace.trace(ERROR, cause);
} finally {
task.set(null);
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy