java sslengine 访问github.com https协议
关于sslengine,理解为就是你的程序与网站直接的一个门,sslengine负责握手协议handshake与加解密包数据(发送给服务器的数据调用wrap() 解密返回的数据unwrap())
sslengine的两种状态
sslengine.getHandshakeStatus()是握手状态,根据不同状态有不同操作
SSLEngineResult.Status是加解密方法返回的结果,wrap(),unwrap(),不是简单的boolean,是不同结果有不同操作
//MySSLEngineDemo.java
import javax.net.ssl.*;
import java.io.FileInputStream;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.security.KeyStore;
import java.security.Security;
public class MySSLEngineDemo {
public static SSLEngine prepareEngine(String host, int port) throws Exception {
char[] passphrase = "changeit".toCharArray();
SSLContext ctx = SSLContext.getInstance("TLSv1.2");
String algorithm = Security.getProperty("ssl.KeyManagerFactory.algorithm");
if (algorithm == null) {
algorithm = "SunX509";
}
KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
KeyStore ks = ks = KeyStore.getInstance("JKS");
String JAVA_HOME = System.getenv("JAVA_HOME");
ks.load(new FileInputStream(JAVA_HOME + "/jre/lib/security/cacerts"), passphrase);
kmf.init(ks, passphrase);
ctx.init(kmf.getKeyManagers(), null, null);
SSLEngine sslEngine = ctx.createSSLEngine(host, port);
sslEngine.setUseClientMode(true);
return sslEngine;
}
public static SocketChannel prepareChannel(String host, int port) throws Exception {
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress(host, port));
return socketChannel;
}
public static void doHandshake(SocketChannel socketChannel, SSLEngine engine,
ByteBuffer myNetData, ByteBuffer peerNetData) throws Exception {
// Create byte buffers to use for holding application data
int appBufferSize = engine.getSession().getApplicationBufferSize();
ByteBuffer myAppData = ByteBuffer.allocate(appBufferSize);
ByteBuffer peerAppData = ByteBuffer.allocate(appBufferSize);
// Begin handshake
engine.beginHandshake();
SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
// Process handshaking message
while (hs != SSLEngineResult.HandshakeStatus.FINISHED &&
hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
switch (hs) {
case NEED_UNWRAP:
// Receive handshaking data from peer
if (socketChannel.read(peerNetData) < 0) {
// The channel has reached end-of-stream
}
// Process incoming handshaking data
peerNetData.flip();
SSLEngineResult res = engine.unwrap(peerNetData, peerAppData);
// MySSlEngine.print(peerAppData);
peerNetData.compact();
hs = res.getHandshakeStatus();
// Check status
switch (res.getStatus()) {
case OK:
// Handle OK status
break;
// Handle other status: BUFFER_UNDERFLOW, BUFFER_OVERFLOW, CLOSED
}
break;
case NEED_WRAP:
// Empty the local network packet buffer.
myNetData.clear();
// Generate handshaking data
res = engine.wrap(myAppData, myNetData);
// MySSlEngine.print(myNetData);
hs = res.getHandshakeStatus();
// Check status
switch (res.getStatus()) {
case OK:
myNetData.flip();
// Send the handshaking data to peer
while (myNetData.hasRemaining()) {
socketChannel.write(myNetData);
}
break;
// Handle other status: BUFFER_OVERFLOW, BUFFER_UNDERFLOW, CLOSED
}
break;
case NEED_TASK:
// Handle blocking tasks
Runnable task;
while ((task = engine.getDelegatedTask()) != null) {
new Thread(task).start();
}
hs = engine.getHandshakeStatus();
break;
// Handle other status: // FINISHED or NOT_HANDSHAKING
}
}
// Processes after handshaking
System.out.println("after handshaking");
}
private static void runDelegatedTasks(SSLEngineResult result,
SSLEngine engine) throws Exception {
if (result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK) {
Runnable runnable;
while ((runnable = engine.getDelegatedTask()) != null) {
System.out.println("\trunning delegated task...");
runnable.run();
}
SSLEngineResult.HandshakeStatus hsStatus = engine.getHandshakeStatus();
if (hsStatus == SSLEngineResult.HandshakeStatus.NEED_TASK) {
throw new Exception("handshake shouldn't need additional tasks");
}
System.out.println("\tnew HandshakeStatus: " + hsStatus);
}
}
public static void sendRequest(String host, int port, String path,
SSLEngine sslEngine, ByteBuffer myAppData, ByteBuffer myNetData, SocketChannel socketChannel)
throws Exception {
StringBuilder header = new StringBuilder();
header.append("GET " + path + " HTTP/1.1\r\n");
header.append("Host: " + host + "\r\n");
header.append("Connection: keep-alive\r\n");
header.append("\r\n");
myAppData.put(header.toString().getBytes());
myAppData.flip();
myNetData.clear();
SSLEngineResult res = sslEngine.wrap(myAppData, myNetData);
if (res.getStatus() == SSLEngineResult.Status.OK) {
myNetData.flip();
socketChannel.write(myNetData);
}
}
public static void main(String[] args) throws Exception {
// System.setProperty("javax.net.debug", "all");
String host = "github.com";
int port = 443;
String path = "/";
SSLEngine sslEngine = MySSLEngineDemo.prepareEngine(host, port);
SocketChannel socketChannel = MySSLEngineDemo.prepareChannel(host, port);
while (!socketChannel.finishConnect()) {
// System.out.println("wait...............");
}
System.out.println("connected..............");
//prepare the four ByteBuffer
SSLSession session = sslEngine.getSession();
ByteBuffer myAppData = ByteBuffer.allocate(session.getApplicationBufferSize());
ByteBuffer myNetData = ByteBuffer.allocate(session.getPacketBufferSize());
ByteBuffer peerAppData = ByteBuffer.allocate(session.getApplicationBufferSize());
ByteBuffer peerNetData = ByteBuffer.allocate(session.getPacketBufferSize());
MySSLEngineDemo.doHandshake(socketChannel, sslEngine, myNetData, peerNetData);
MySSLEngineDemo.sendRequest(host, port, path, sslEngine, myAppData, myNetData, socketChannel);
int num = 0;
int readCount=0;
while ((num = socketChannel.read(peerNetData)) != -1) {
if (num > 0) {
peerNetData.flip();
SSLEngineResult res = sslEngine.unwrap(peerNetData, peerAppData);
System.out.println(res.getStatus());
if (res.getStatus() == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
peerNetData.compact();
continue;
}
if (res.getStatus() == SSLEngineResult.Status.OK) {
peerNetData.compact();
// Use peerAppData
int len = peerAppData.position();
int count = 0;
while (len > 0) {
System.out.print((char) peerAppData.get(count));
count++;
len--;
}
peerAppData.clear();
//uncomment below will cause SSLException Unsupported record version
//or Tag mismatch
// peerNetData.clear();
System.out.println();
System.out.println("readCount:"+readCount++ +" num:"+num+".......");
System.out.println(peerNetData+"......................"+session.getPacketBufferSize());
}
}
}
}
}
运行后,控制台可以看到大部分html的代码,但是没有返回全部所有数据
sslEngine.unwrap(peerNetData, peerAppData) 会读取peerNetData内从服务器返回的加密数据,【并不是全部读取解密】因为数据是分段加密了,peerNetData内可能是部分加密数据就会报BUFFER_UNDERFLOW,说明加密数据块不完整,需要从服务器继续读数据,