我正在尝试使用 SSLEngine 实现 SSL 握手,我需要作为服务器和客户端同时进行,但是我被困住了,我无法弄清楚为什么。
握手正确开始,交换了 Hello,交换了密钥,但随后我进入了NEED_UNWRAP状态。
这是我正在使用的握手代码:
protected boolean doHandshake(InputStream inputStream, OutputStream outputStream, SSLEngine engine, Socket socket) throws IOException {
Log.d(TAG,"About to do handshake...");
Log.d(TAG,engine.getHandshakeStatus().toString());
int dataSize;
SSLEngineResult result;
Log.d(TAG,"Line 1");
HandshakeStatus handshakeStatus;
Log.d(TAG,"Line 2");
// NioSslPeer's fields myAppData and peerAppData are supposed to be large enough to hold all message data the peer
// will send and expects to receive from the other peer respectively. Since the messages to be exchanged will usually be less
// than 16KB long the capacity of these fields should also be smaller. Here we initialize these two local buffers
// to be used for the handshake, while keeping client's buffers at the same size.
if (socket!=null)
{
inputStream=socket.getInputStream();
outputStream=socket.getOutputStream();
}
Log.d(TAG,"Line 3");
int appBufferSize = engine.getSession().getApplicationBufferSize();
Log.d(TAG,"Line 4");
ByteBuffer myAppData = ByteBuffer.allocate(appBufferSize);
Log.d(TAG,"Line 5");
ByteBuffer peerAppData = ByteBuffer.allocate(appBufferSize);
Log.d(TAG,"Line 6");
try {
myNetData.clear();
peerNetData.clear();
}
catch (Exception e){Log.e(TAG,e.getMessage());}
Log.d(TAG,"Line 7");
Log.d(TAG,"Line 8");
handshakeStatus = engine.getHandshakeStatus();
Log.d(TAG,"Line 9");
Log.d(TAG,"Before the while: " + (handshakeStatus != SSLEngineResult.HandshakeStatus.FINISHED && handshakeStatus != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING));
byte[] buffer=new byte[16384];
while (handshakeStatus != SSLEngineResult.HandshakeStatus.FINISHED && handshakeStatus != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
Log.d(TAG,handshakeStatus.toString());
switch (handshakeStatus) {
case NEED_UNWRAP:
Log.d(TAG,"Got here...");
buffer=new byte[16384];
peerAppData.clear();
int readdata=inputStream.read(buffer);
Log.d(TAG,"Read data amount: " + readdata);
if ( readdata < 0) {
Log.d(TAG,"No data....");
if (engine.isInboundDone() && engine.isOutboundDone()) {
return false;
}
try {
engine.closeInbound();
} catch (SSLException e) {
Log.e(TAG,"This engine was forced to close inbound, without having received the proper SSL/TLS close notification message from the peer, due to end of stream.");
}
engine.closeOutbound();
// After closeOutbound the engine will be set to WRAP state, in order to try to send a close message to the client.
handshakeStatus = engine.getHandshakeStatus();
break;
}
HackerService.bytesToHex(buffer);
peerNetData.put(buffer,6,readdata-6);
Log.d(TAG,"before data flipped...");
peerNetData.flip();
Log.d(TAG,"data flipped...");
try {
result = engine.unwrap(peerNetData, peerAppData);
Log.d(TAG,"data unwrapped...");
peerNetData.compact();
Log.d(TAG,"data compacted...");
handshakeStatus = result.getHandshakeStatus();
Log.d(TAG,"Handshake status: " + handshakeStatus);
} catch (SSLException sslException) {
Log.e(TAG,"A problem was encountered while processing the data that caused the SSLEngine to abort. Will try to properly close connection..." + sslException.getMessage());
engine.closeOutbound();
handshakeStatus = engine.getHandshakeStatus();
break;
}
switch (result.getStatus()) {
case OK:
break;
case BUFFER_OVERFLOW:
// Will occur when peerAppData's capacity is smaller than the data derived from peerNetData's unwrap.
peerAppData = enlargeApplicationBuffer(engine, peerAppData);
break;
case BUFFER_UNDERFLOW:
// Will occur either when no data was read from the peer or when the peerNetData buffer was too small to hold all peer's data.
peerNetData = handleBufferUnderflow(engine, peerNetData);
break;
case CLOSED:
if (engine.isOutboundDone()) {
return false;
} else {
engine.closeOutbound();
handshakeStatus = engine.getHandshakeStatus();
break;
}
default:
throw new IllegalStateException("Invalid SSL status: " + result.getStatus());
}
break;
case NEED_WRAP:
myNetData.clear();
Log.d(TAG,"Enetering need wrap");
try {
result = engine.wrap(myAppData, myNetData);
Log.d(TAG,"Got a result" + myAppData.toString());
handshakeStatus = result.getHandshakeStatus();
Log.d(TAG,"Handskes is: " + handshakeStatus.toString());
} catch (SSLException sslException) {
Log.e(TAG,"A problem was encountered while processing the data that caused the SSLEngine to abort. Will try to properly close connection...");
engine.closeOutbound();
handshakeStatus = engine.getHandshakeStatus();
break;
}
switch (result.getStatus()) {
case OK :
Log.d(TAG,"Case WRAP, OK");
myNetData.flip();
// while (myNetData.hasRemaining()) {
byte[] arr = new byte[myNetData.remaining()+6];
myNetData.get(arr,6,myNetData.remaining());
arr[0]=0;
arr[1]=3;
arr[2]=(byte) ((arr.length-4)/256);
arr[3]=(byte) ((arr.length-4)%256);
arr[4]=buffer[4];
arr[5]=buffer[5];
HackerService.bytesToHex(arr);
outputStream.write(arr);
// }
break;
case BUFFER_OVERFLOW:
Log.d(TAG,"Case WRAP,OverFlow");
// Will occur if there is not enough space in myNetData buffer to write all the data that would be generated by the method wrap.
// Since myNetData is set to session's packet size we should not get to this point because SSLEngine is supposed
// to produce messages smaller or equal to that, but a general handling would be the following:
myNetData = enlargePacketBuffer(engine, 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:
try {
Log.d(TAG,"Before WRAP FLIP");
myNetData.flip();
Log.d(TAG,"After WRAP FLIP");
while (myNetData.hasRemaining()) {
Log.d(TAG,myNetData.toString());
arr = new byte[myNetData.remaining()];
myNetData.get(arr);
outputStream.write(arr);
}
// At this point the handshake status will probably be NEED_UNWRAP so we make sure that peerNetData is clear to read.
peerNetData.clear();
} catch (Exception e) {
Log.e(TAG,"Failed to send server's CLOSE message due to socket channel's failure.");
handshakeStatus = engine.getHandshakeStatus();
}
break;
default:
throw new IllegalStateException("Invalid SSL status: " + result.getStatus());
}
break;
case NEED_TASK:
Log.d(TAG,"Need task");
Runnable task;
while ((task = engine.getDelegatedTask()) != null) {
executor.execute(task);
}
handshakeStatus = engine.getHandshakeStatus();
break;
case FINISHED:
break;
case NOT_HANDSHAKING:
break;
default:
throw new IllegalStateException("Invalid SSL status: " + handshakeStatus);
}
}
Log.d(TAG,"Handshake completed");
return true;
}
这是我的SSLEngine创建类:
public static SSLEngine Builder(Context context) throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException, UnrecoverableKeyException, KeyManagementException {
InputStream openRawResource = context.getResources().openRawResource(context.getResources().getIdentifier("mykey", "raw", context.getPackageName()));
KeyStore instance = KeyStore.getInstance("PKCS12");
instance.load(openRawResource, "passcode".toCharArray());
KeyManagerFactory instance2 = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
instance2.init(instance, "passcode".toCharArray());
SSLContext instance3 = SSLContext.getInstance("TLSv1.2");
instance3.init(instance2.getKeyManagers(), new TrustManager[]{new TrustmanagerHelper()}, new SecureRandom());
SSLEngine createSSLEngine = instance3.createSSLEngine();
createSSLEngine.setNeedClientAuth(true);
return createSSLEngine;
}
当我尝试作为客户握手时。正如您从日志中看到的那样,我从NEED_WRAP开始,将数据发送到服务器,状态到达NEED_UNWRAP(这是正确的(服务器响应,我解析答案没有任何错误,但不是前进到NEED_WRAP我陷入了NEED_UNWRAP......
About to do handshake...
NEED_WRAP
Line 1
Line 2
Line 4
Line 5
Line 6
Line 7
Line 8
Line 9
Before the while: true
NEED_WRAP
Enetering need wrap
Got a resultjava.nio.HeapByteBuffer[pos=0 lim=16384 cap=16384]
Handskes is: NEED_UNWRAP
Case WRAP, OK
ByteTohex: 00030088000316030100810100007D03030E62BFCFF988.......
NEED_UNWRAP
Got here...
Read data amount: 2296 (THIS MATCHES THE NUMBER OF BYTES SENT BY THE SERVER!!!!)
ByteTohex: 000308F40003160303005B0200005703035B203DA285349B7C88A76CA6AA3.....
before data flipped...
data flipped...
data unwrapped...
data compacted...
Handshake status: NEED_UNWRAP
NEED_UNWRAP
Got here...
如果我尝试作为服务器进行握手,日志如下所示。正如你从日志中看到的,第一次读取很好,我响应客户端,我从客户端获取第二位数据,而不是NEED_WRAP并能够继续握手,我被NEED_UNWRAP消息击中,但当然没有更多的数据要从客户端读取......
About to do handshake...
NEED_UNWRAP
Line 1
Line 2
Line 3
Line 4
Line 5
Line 6
Line 7
Line 8
Line 9
Before the while: true
NEED_UNWRAP
Got here...
06-12 23:42:14.017 7523-7620/uk.co.borconi,emil.myapp D/MyApp: Read data amount: 297
06-12 23:42:14.020 7523-7620/uk.co.borconi,emil.myapp D/MyApp: ByteTohex: 00030125000316....
before data flipped...
data flipped...
06-12 23:42:14.029 7523-7620/uk.co.borconi,emil.myapp D/MyApp: data unwrapped...
data compacted...
Handshake status: NEED_WRAP
NEED_WRAP
Enetering need wrap
Got a resultjava.nio.HeapByteBuffer[pos=0 lim=16384 cap=16384]
Handskes is: NEED_UNWRAP
Case WRAP, OK
06-12 23:42:14.030 7523-7620/uk.co.borconi,emil.myapp D/MyApp: ByteTohex: 00030881000316.....
NEED_UNWRAP
Got here...
06-12 23:42:14.038 7523-7620/uk.co.borconi,emil.myapp D/MyApp: Read data amount: 132
06-12 23:42:14.039 7523-7620/uk.co.borconi,emil.myapp D/MyApp: ByteTohex: 00030080000316....
before data flipped...
data flipped...
06-12 23:42:14.040 7523-7620/uk.co.borconi,emil.myapp D/MyApp: data unwrapped...
data compacted...
Handshake status: NEED_UNWRAP
NEED_UNWRAP
Got here...
在发布之前,我确实在 StackOverflow 上看过一些类似的问题,但它们主要是关于顺序错误,我认为在这种情况下我是正确的......我很确定我错过了显而易见的东西,但我似乎无法弄清楚......
经过2天追逐自己的故事,我终于找到了这里描述的问题:https://github.com/netty/netty/issues/5975
我发现我们围绕SSLEngine的基于流的包装器无法读取 来自 SSL 的所有传入数据没有新传入数据时 来自网络的数据,因此应用程序陷入等待 传入数据。经过一些调试,我发现 netty 的 openssl SSLEngine 解包似乎以较小的方式生成纯文本数据 块(可能是单个 TLS 帧(并继续缓冲其余部分 内部数据。src 缓冲区已完全消耗,但调用 使用空的 SRC 缓冲区再次解包仍会产生更多数据。 这与JDK SSLEngine在两点上有所不同:
- JDK SSLEngine一次性消耗和生成尽可能多的数据,而openssl SSL在一次调用中产生较少的输出。
- JDK SSLEngine 不会在解包调用之间内部缓冲加密数据,而是将它们"放回"到 src 缓冲区中
。
所以即使我的代码是"正确的",我也需要做多个循环,所以现在我的解包代码看起来像这样:
peerNetData.put(buffer,6,readdata-6);
Log.d(TAG,"before data flipped...");
peerNetData.flip();
Log.d(TAG,"data flipped...");
try {
do {
result = engine.unwrap(peerNetData, peerAppData);
Log.d(TAG,"data unwrapped...");
Log.d(TAG,"Handskes is: " + result.getHandshakeStatus().toString() +" Current Status: " +result.getStatus() + " Bytes consumed: " + result.bytesConsumed() + " bytes produce: " + result.bytesProduced());
} while (peerNetData.hasRemaining() || result.bytesProduced()>0);
peerNetData.compact();
Log.d(TAG,"data compacted...");
handshakeStatus = result.getHandshakeStatus();
Log.d(TAG,"Handshake status: " + handshakeStatus);
.....................................................
这对我有帮助
do {
result = engine.unwrap(peerNetData, peerAppData);
System.out.println("data unwrapped...");
System.out.println("Handskes is: " + result.getHandshakeStatus().toString() +" Current Status: " +result.getStatus() + " Bytes consumed: " + result.bytesConsumed() + " bytes produce: " + result.bytesProduced());
if(result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK){
Runnable task;
while ((task = engine.getDelegatedTask()) != null) {
task.run();
}
handshakeStatus = engine.getHandshakeStatus();
}
} while (result.bytesProduced()>0 || peerNetData.hasRemaining());
输出:
data unwrapped...
Handskes is: NEED_TASK Current Status: OK Bytes consumed: 267 bytes produce: 0
data unwrapped...
Handskes is: NEED_UNWRAP Current Status: OK Bytes consumed: 6 bytes produce: 0
data unwrapped...
Handskes is: NEED_WRAP Current Status: OK Bytes consumed: 45 bytes produce: 0
OK
我想它需要在展开之间进行一些处理