下面列出了怎么用javax.net.ssl.SSLEngineResult的API类实例代码及写法,或者点击链接到github查看源代码。
private void checkBufferOverflowOnUnWrap(SSLEngine wrappingEngine,
SSLEngine unwrappingEngine)
throws SSLException {
String wrapperMode = wrappingEngine.getUseClientMode() ? "client"
: "server";
String unwrapperMode = unwrappingEngine.getUseClientMode() ? "client"
: "server";
if (wrapperMode.equals(unwrapperMode)) {
throw new Error("Test error: both engines are in the same mode!");
}
System.out.println("================================================="
+ "===========");
System.out.println("Testing SSLEngine buffer overflow"
+ " on unwrap by " + unwrapperMode);
ByteBuffer app = ByteBuffer.wrap(MESSAGE.getBytes());
ByteBuffer net = ByteBuffer
.allocate(wrappingEngine.getSession().getPacketBufferSize());
SSLEngineResult r = wrappingEngine.wrap(app, net);
checkResult(r, SSLEngineResult.Status.OK);
//Making app buffer size less than required by 1 byte.
app = ByteBuffer.allocate(MESSAGE.length() - 1);
net.flip();
r = unwrappingEngine.unwrap(net, app);
checkResult(r, SSLEngineResult.Status.BUFFER_OVERFLOW);
System.out.println("Passed");
}
/**
* Performs the WRAP function
* @param doWrite boolean
* @return the result
* @throws IOException An IO error occurred
*/
protected SSLEngineResult handshakeWrap(boolean doWrite) throws IOException {
//this should never be called with a network buffer that contains data
//so we can clear it here.
netOutBuffer.clear();
//perform the wrap
getBufHandler().configureWriteBufferForRead();
SSLEngineResult result = sslEngine.wrap(getBufHandler().getWriteBuffer(), netOutBuffer);
//prepare the results to be written
netOutBuffer.flip();
//set the status
handshakeStatus = result.getHandshakeStatus();
//optimization, if we do have a writable channel, write it now
if (doWrite) {
flush(netOutBuffer);
}
return result;
}
/**
* 解密
* @return
* @throws SSLException
*/
private SSLEngineResult doUnwrap() throws SSLException {
ByteBuffer cipherText = _buffers.get(BufferType.IN_CIPHER);
ByteBuffer plainText = _buffers.get(BufferType.IN_PLAIN);
try {
log.info("{}, doUnwrap(解密): 密文buffer:{}, 明文buffer: {}", channelContext, cipherText, plainText);
return _engine.unwrap(cipherText, plainText);
} catch (SSLException e) {
if (log.isInfoEnabled()) {
byte[] bs = new byte[cipherText.limit()];
System.arraycopy(cipherText.array(), 0, bs, 0, bs.length);
log.error(channelContext + ", 解密Error:" + e.toString() + ", byte:" + StrUtil.arrayToString(bs) + ", string:" + new String(bs) + ", buffer:" + cipherText, e);
}
throw e;
}
}
/**
* Test for <code>toString()</code> method
*/
public void test_toString() {
int[] pos = { 0, 1, 1000, Integer.MAX_VALUE, (Integer.MAX_VALUE - 1) };
SSLEngineResult.Status [] enS =
SSLEngineResult.Status.values();
SSLEngineResult.HandshakeStatus [] enHS =
SSLEngineResult.HandshakeStatus.values();
for (int i = 0; i < enS.length; i++) {
for (int j = 0; j < enHS.length; j++) {
for (int n = 0; n < pos.length; n++) {
for (int l = 0; l < pos.length; ++l) {
SSLEngineResult res = new SSLEngineResult(enS[i],
enHS[j], pos[n], pos[l]);
assertNotNull("Result of toSring() method is null",
res.toString());
}
}
}
}
}
public SSLSocketChannel2(SocketChannel channel, SSLEngine sslEngine, ExecutorService exec,
SelectionKey key)
throws IOException {
if (channel == null || sslEngine == null || exec == null) {
throw new IllegalArgumentException("parameter must not be null");
}
this.socketChannel = channel;
this.sslEngine = sslEngine;
this.exec = exec;
readEngineResult = writeEngineResult =
new SSLEngineResult(Status.BUFFER_UNDERFLOW, sslEngine.getHandshakeStatus(), 0,
0); // init to prevent NPEs
tasks = new ArrayList<Future<?>>(3);
if (key != null) {
key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
this.selectionKey = key;
}
createBuffers(sslEngine.getSession());
// kick off handshake
socketChannel.write(wrap(emptybuffer));// initializes res
processHandshake();
}
/**
* {@link #read(ByteBuffer)} may not be to leave all buffers(inData, inCrypt)
**/
private int readRemaining( ByteBuffer dst ) throws SSLException {
if( inData.hasRemaining() ) {
return transfereTo( inData, dst );
}
if( !inData.hasRemaining() )
inData.clear();
// test if some bytes left from last read (e.g. BUFFER_UNDERFLOW)
if( inCrypt.hasRemaining() ) {
unwrap();
int amount = transfereTo( inData, dst );
if (readEngineResult.getStatus() == SSLEngineResult.Status.CLOSED) {
return -1;
}
if( amount > 0 )
return amount;
}
return 0;
}
/** Returns last {@link HandshakeStatus} of the loop */
private void wrapLoop(ByteBufferSet source) throws SSLException {
while (true) {
SSLEngineResult result = callEngineWrap(source);
switch (result.getStatus()) {
case OK:
case CLOSED:
return;
case BUFFER_OVERFLOW:
Util.assertTrue(result.bytesConsumed() == 0);
outEncrypted.enlarge();
break;
case BUFFER_UNDERFLOW:
throw new IllegalStateException();
}
}
}
@Override
SSLEngineResult unwrap(SslHandler handler, ByteBuf in, int readerIndex, int len, ByteBuf out)
throws SSLException {
int nioBufferCount = in.nioBufferCount();
int writerIndex = out.writerIndex();
final SSLEngineResult result;
if (nioBufferCount > 1) {
/*
* Use a special unwrap method without additional memory copies.
*/
try {
handler.singleBuffer[0] = toByteBuffer(out, writerIndex, out.writableBytes());
result = ((ConscryptAlpnSslEngine) handler.engine).unwrap(
in.nioBuffers(readerIndex, len),
handler.singleBuffer);
} finally {
handler.singleBuffer[0] = null;
}
} else {
result = handler.engine.unwrap(toByteBuffer(in, readerIndex, len),
toByteBuffer(out, writerIndex, out.writableBytes()));
}
out.writerIndex(writerIndex + result.bytesProduced());
return result;
}
public SSLSocketChannel2( SocketChannel channel , SSLEngine sslEngine , ExecutorService exec , SelectionKey key ) throws IOException {
if( channel == null || sslEngine == null || exec == null )
throw new IllegalArgumentException( "parameter must not be null" );
this.socketChannel = channel;
this.sslEngine = sslEngine;
this.exec = exec;
readEngineResult = writeEngineResult = new SSLEngineResult( Status.BUFFER_UNDERFLOW, sslEngine.getHandshakeStatus(), 0, 0 ); // init to prevent NPEs
tasks = new ArrayList<Future<?>>( 3 );
if( key != null ) {
key.interestOps( key.interestOps() | SelectionKey.OP_WRITE );
this.selectionKey = key;
}
createBuffers( sslEngine.getSession() );
// kick off handshake
socketChannel.write( wrap( emptybuffer ) );// initializes res
processHandshake();
}
private SSLEngineResult callEngineUnwrap(ByteBufferSet dest) throws SSLException {
inEncrypted.buffer.flip();
try {
SSLEngineResult result =
engine.unwrap(inEncrypted.buffer, dest.array, dest.offset, dest.length);
if (logger.isTraceEnabled()) {
logger.trace(
"engine.unwrap() result [{}]. Engine status: {}; inEncrypted {}; inPlain: {}",
Util.resultToString(result),
result.getHandshakeStatus(),
inEncrypted,
dest);
}
return result;
} catch (SSLException e) {
// something bad was received from the underlying channel, we cannot
// continue
invalid = true;
throw e;
} finally {
inEncrypted.buffer.compact();
}
}
/**
* Sends a SSL close message, will not physically close the connection here.<br>
* To close the connection, you could do something like
* <pre><code>
* close();
* while (isOpen() && !myTimeoutFunction()) Thread.sleep(25);
* if ( isOpen() ) close(true); //forces a close if you timed out
* </code></pre>
* @throws IOException if an I/O error occurs
* @throws IOException if there is data on the outgoing network buffer and we are unable to flush it
* TODO Implement this java.io.Closeable method
*/
@Override
public void close() throws IOException {
if (closing) return;
closing = true;
sslEngine.closeOutbound();
if (!flush(netOutBuffer)) {
throw new IOException("Remaining data in the network buffer, can't send SSL close message, force a close with close(true) instead");
}
//prep the buffer for the close message
netOutBuffer.clear();
//perform the close, since we called sslEngine.closeOutbound
SSLEngineResult handshake = sslEngine.wrap(getEmptyBuf(), netOutBuffer);
//we should be in a close state
if (handshake.getStatus() != SSLEngineResult.Status.CLOSED) {
throw new IOException("Invalid close state, will not send network data.");
}
//prepare the buffer for writing
netOutBuffer.flip();
//if there is data to be written
flush(netOutBuffer);
//is the channel closed?
closed = (!netOutBuffer.hasRemaining() && (handshake.getHandshakeStatus() != HandshakeStatus.NEED_WRAP));
}
private void checkResult(SSLEngineResult result, boolean wrap)
throws SSLException {
handshakeStatus = result.getHandshakeStatus();
resultStatus = result.getStatus();
if (resultStatus != Status.OK &&
(wrap || resultStatus != Status.BUFFER_UNDERFLOW)) {
throw new SSLException("TODO");
}
if (wrap && result.bytesConsumed() != 0) {
throw new SSLException("TODO");
}
if (!wrap && result.bytesProduced() != 0) {
throw new SSLException("TODO");
}
}
/**
* performs the unwrap operation by unwrapping from {@link #inCrypt} to {@link #inData}
**/
private synchronized ByteBuffer unwrap() throws SSLException {
int rem;
//There are some ssl test suites, which get around the selector.select() call, which cause an infinite unwrap and 100% cpu usage (see #459 and #458)
if(readEngineResult.getStatus() == SSLEngineResult.Status.CLOSED && sslEngine.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING){
try {
close();
} catch (IOException e) {
//Not really interesting
}
}
do {
rem = inData.remaining();
readEngineResult = sslEngine.unwrap( inCrypt, inData );
} while ( readEngineResult.getStatus() == SSLEngineResult.Status.OK && ( rem != inData.remaining() || sslEngine.getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP ) );
inData.flip();
return inData;
}
private SSLEngineResult callEngineWrap(ByteBufferSet source) throws SSLException {
try {
SSLEngineResult result =
engine.wrap(source.array, source.offset, source.length, outEncrypted.buffer);
if (logger.isTraceEnabled()) {
logger.trace(
"engine.wrap() result: [{}]; engine status: {}; srcBuffer: {}, outEncrypted: {}",
Util.resultToString(result),
result.getHandshakeStatus(),
source,
outEncrypted);
}
return result;
} catch (SSLException e) {
invalid = true;
throw e;
}
}
SSLEngineResult wrap(ByteBuffer plainData) throws SSLException
{
_buffers.prepareForWrap(plainData);
SSLEngineResult result = doWrap();
emitWrappedData(result);
switch (result.getStatus()) {
case BUFFER_UNDERFLOW:
throw new RuntimeException("BUFFER_UNDERFLOW while wrapping!");
case BUFFER_OVERFLOW:
_buffers.grow(BufferType.OUT_CIPHER);
if (plainData != null && plainData.hasRemaining()) {
plainData.position(result.bytesConsumed());
ByteBuffer remainingData = BufferUtils.slice(plainData);
wrap(remainingData);
}
break;
case OK:
break;
case CLOSED:
_sessionClosedListener.onSessionClosed();
break;
}
return result;
}
/**
* Sends a SSL close message, will not physically close the connection here.<br>
* To close the connection, you could do something like
* <pre><code>
* close();
* while (isOpen() && !myTimeoutFunction()) Thread.sleep(25);
* if ( isOpen() ) close(true); //forces a close if you timed out
* </code></pre>
* @throws IOException if an I/O error occurs
* @throws IOException if there is data on the outgoing network buffer and we are unable to flush it
* TODO Implement this java.io.Closeable method
*/
@Override
public void close() throws IOException {
if (closing) return;
closing = true;
sslEngine.closeOutbound();
if (!flush(netOutBuffer)) {
throw new IOException("Remaining data in the network buffer, can't send SSL close message, force a close with close(true) instead");
}
//prep the buffer for the close message
netOutBuffer.clear();
//perform the close, since we called sslEngine.closeOutbound
SSLEngineResult handshake = sslEngine.wrap(getEmptyBuf(), netOutBuffer);
//we should be in a close state
if (handshake.getStatus() != SSLEngineResult.Status.CLOSED) {
throw new IOException("Invalid close state, will not send network data.");
}
//prepare the buffer for writing
netOutBuffer.flip();
//if there is data to be written
flush(netOutBuffer);
//is the channel closed?
closed = (!netOutBuffer.hasRemaining() && (handshake.getHandshakeStatus() != HandshakeStatus.NEED_WRAP));
}
void handleUnwrapResult(SSLEngineResult result) throws SSLException {
if (result.getHandshakeStatus().equals(SSLEngineResult.HandshakeStatus.FINISHED)) {
handshakeFinished(); //客户端会走到这一行
} else {
shakehands();
}
}
private static void decryptOne(final SqSSL ssl, final ByteBuffer target) throws SSLException {
ssl.buffer.flip();
final SSLEngineResult result = unwrap(ssl, ssl.buffer, target);
checkStatus("Decrypt status", result, Status.OK, Status.BUFFER_UNDERFLOW, Status.CLOSED);
if (result.getStatus() == Status.OK || result.getStatus() == Status.BUFFER_UNDERFLOW) {
ssl.buffer.compact();
}
if (result.getStatus() == Status.CLOSED) {
connectionClosed(ssl);
}
}
@Override
protected void write(ByteBuffer gelfBuffer) throws IOException {
while (gelfBuffer.hasRemaining()) {
read();
ByteBuffer myNetData = getNetworkBuffer();
// Generate SSL/TLS encoded data (handshake or application data)
gelfBuffer.mark();
SSLEngineResult res = sslEngine.wrap(gelfBuffer, myNetData);
// Process status of call
if (res.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) {
this.sslNetworkBuffers.set(enlargeBuffer(gelfBuffer, myNetData));
gelfBuffer.reset();
}
if (res.getStatus() == SSLEngineResult.Status.OK) {
myNetData.flip();
// Send SSL/TLS encoded data to peer
while (myNetData.hasRemaining()) {
int written = channel().write(myNetData);
if (written == -1) {
throw new SocketException("Channel closed");
}
}
}
}
}
private SSLEngineResult.HandshakeStatus handleHandshakeStatus(SSLEngineResult sslEngineResult) {
SSLEngineResult.HandshakeStatus handshakeStatus = sslEngineResult.getHandshakeStatus();
switch (handshakeStatus) {
case NEED_TASK:
while (true) {
final Runnable delegatedTask = engine.getDelegatedTask();
if (delegatedTask == null) {
break;
}
sslEngineDelegatedTasks++;
int currentPendingDelegatedTasks = pendingDelegatedTasks.incrementAndGet();
if (currentPendingDelegatedTasks > maxPendingSslEngineDelegatedTasks) {
maxPendingSslEngineDelegatedTasks = currentPendingDelegatedTasks;
}
Runnable wrappedDelegatedTask = () -> {
delegatedTask.run();
int wrappedCurrentPendingDelegatedTasks = pendingDelegatedTasks.decrementAndGet();
if (wrappedCurrentPendingDelegatedTasks == 0) {
callChannelSelectedCallback(true, true);
}
};
connectionInternal.asyncGo(wrappedDelegatedTask);
}
break;
case FINISHED:
onHandshakeFinished();
break;
default:
break;
}
SSLEngineResult.HandshakeStatus afterHandshakeStatus = engine.getHandshakeStatus();
return afterHandshakeStatus;
}
/**
* Test for <code> SSLEngineResult.HandshakeStatus.values() </code>
*/
public void test_SSLEngineResultHandshakeStatus_values() {
String[] str = {"NOT_HANDSHAKING", "FINISHED", "NEED_TASK", "NEED_WRAP", "NEED_UNWRAP"};
SSLEngineResult.HandshakeStatus[] enS = SSLEngineResult.HandshakeStatus.values();
if (enS.length == str.length) {
for (int i = 0; i < enS.length; i++) {
//System.out.println("enS[" + i + "] = " + enS[i]);
assertEquals("Incorrect Status", enS[i].toString(), str[i]);
}
} else {
fail("Incorrect number of enum constant was returned");
}
}
@Override
public synchronized int write(ByteBuffer output) throws IOException {
int num = 0;
while (output.hasRemaining()) {
// The loop has a meaning for (outgoing) messages larger than 16KB.
// Every wrap call will remove 16KB from the original message and send it to the
// remote peer.
myNetData.clear();
SSLEngineResult result = engine.wrap(output, myNetData);
switch (result.getStatus()) {
case OK:
myNetData.flip();
while (myNetData.hasRemaining()) {
num += socketChannel.write(myNetData);
}
break;
case BUFFER_OVERFLOW:
myNetData = enlargePacketBuffer(myNetData);
break;
case BUFFER_UNDERFLOW:
throw new SSLException("Buffer underflow occured after a wrap. I don't think " +
"we should ever get here.");
case CLOSED:
closeConnection();
return 0;
default:
throw new IllegalStateException("Invalid SSL status: " + result.getStatus());
}
}
return num;
}
private void renegotiateIfNeeded(NextFilter nextFilter, SSLEngineResult res) throws SSLException {
if ((res.getStatus() != SSLEngineResult.Status.CLOSED)
&& (res.getStatus() != SSLEngineResult.Status.BUFFER_UNDERFLOW)
&& (res.getHandshakeStatus() != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)) {
// Renegotiation required.
handshakeComplete = false;
handshakeStatus = res.getHandshakeStatus();
handshake(nextFilter);
}
}
/**
* Convert a {@link SSLEngineResult} into a {@link String}, this is needed because the supplied
* method includes a log-breaking newline.
*
* @param result the SSLEngineResult
* @return the resulting string
*/
public static String resultToString(SSLEngineResult result) {
return String.format(
"status=%s,handshakeStatus=%s,bytesProduced=%d,bytesConsumed=%d",
result.getStatus(),
result.getHandshakeStatus(),
result.bytesProduced(),
result.bytesConsumed());
}
private int applicationDataWrap(ByteBuffer src) throws IOException {
SSLEngineResult result = sslEngineWrap(src);
if (result.getHandshakeStatus() != HandshakeStatus.NOT_HANDSHAKING) throw new SSLException("Renegotiation detected");
switch (result.getStatus()) {
case OK:
return result.bytesConsumed();
case BUFFER_OVERFLOW:
return 0;
default:
throw unexpectedStatusException(result.getStatus());
}
}
/**
* This method is used for handling handshake routine as well as sending close_notify message to recipient
*/
private void doHandshake() throws SSLException {
SSLEngineResult result = null;
while (!isClosed()) {
if (result != null && result.getStatus() == CLOSED) {
close();
return;
}
HandshakeStatus handshakeStatus = engine.getHandshakeStatus();
if (handshakeStatus == NEED_WRAP) {
result = tryToWrap();
} else if (handshakeStatus == NEED_UNWRAP) {
result = tryToUnwrap();
if (result.getStatus() == BUFFER_UNDERFLOW) {
doRead();
return;
}
} else if (handshakeStatus == NEED_TASK) {
executeTasks();
return;
} else {
doSync();
return;
}
}
}
private void doWrap(ByteBuffer writeBuffer) {
try {
ByteBuffer netBuffer = netWriteBuffer.buffer();
netBuffer.compact();
int limit = writeBuffer.limit();
if (adaptiveWriteSize > 0 && writeBuffer.remaining() > adaptiveWriteSize) {
writeBuffer.limit(writeBuffer.position() + adaptiveWriteSize);
}
SSLEngineResult result = sslEngine.wrap(writeBuffer, netBuffer);
while (result.getStatus() != SSLEngineResult.Status.OK) {
switch (result.getStatus()) {
case BUFFER_OVERFLOW:
netBuffer.clear();
writeBuffer.limit(writeBuffer.position() + ((writeBuffer.limit() - writeBuffer.position() >> 1)));
adaptiveWriteSize = writeBuffer.remaining();
// logger.info("doWrap BUFFER_OVERFLOW maybeSize:{}", maybeWriteSize);
break;
case BUFFER_UNDERFLOW:
logger.info("doWrap BUFFER_UNDERFLOW");
break;
default:
logger.warn("doWrap Result:" + result.getStatus());
}
result = sslEngine.wrap(writeBuffer, netBuffer);
}
writeBuffer.limit(limit);
netBuffer.flip();
} catch (SSLException e) {
throw new RuntimeException(e);
}
}
/**
* Do all the outstanding handshake tasks in the current Thread.
*/
private SSLEngineResult.HandshakeStatus doTasks() {
/*
* We could run this in a separate thread, but I don't see the need for
* this when used from SSLFilter. Use thread filters in MINA instead?
*/
Runnable runnable;
while ((runnable = sslEngine.getDelegatedTask()) != null) {
// TODO : we may have to use a thread pool here to improve the
// performances
runnable.run();
}
return sslEngine.getHandshakeStatus();
}
@Override
SSLEngineResult unwrap(SslHandler handler, ByteBuf in, int readerIndex, int len, ByteBuf out)
throws SSLException {
int nioBufferCount = in.nioBufferCount();
int writerIndex = out.writerIndex();
final SSLEngineResult result;
if (nioBufferCount > 1) {
/*
* If {@link OpenSslEngine} is in use,
* we can use a special {@link OpenSslEngine#unwrap(ByteBuffer[], ByteBuffer[])} method
* that accepts multiple {@link ByteBuffer}s without additional memory copies.
*/
ReferenceCountedOpenSslEngine opensslEngine = (ReferenceCountedOpenSslEngine) handler.engine;
try {
handler.singleBuffer[0] = toByteBuffer(out, writerIndex,
out.writableBytes());
result = opensslEngine.unwrap(in.nioBuffers(readerIndex, len), handler.singleBuffer);
} finally {
handler.singleBuffer[0] = null;
}
} else {
result = handler.engine.unwrap(toByteBuffer(in, readerIndex, len),
toByteBuffer(out, writerIndex, out.writableBytes()));
}
out.writerIndex(writerIndex + result.bytesProduced());
return result;
}
@Override
SSLEngineResult unwrap(SslHandler handler, ByteBuf in, int readerIndex, int len, ByteBuf out)
throws SSLException {
int writerIndex = out.writerIndex();
final SSLEngineResult result = handler.engine.unwrap(toByteBuffer(in, readerIndex, len),
toByteBuffer(out, writerIndex, out.writableBytes()));
out.writerIndex(writerIndex + result.bytesProduced());
return result;
}