序言
HTTPS是在HTTP的基础上添加了加密功能的安全办HTTP,通过在协议层使用SSL/TSL来实现数据的加密,在握手阶段,服务器会将证书和公钥传递给客户端,客户端会通过CA的公钥对证书进行认证,如果通过的话就生成一个随机数字用来做对称加密算法的秘钥,而这个秘钥会用服务器给的公钥进行加密再返回给服务器。之后客户端与服务器之间的加密就是对称加密了,因为这样性能会比公钥加密高。
更多HTTPS相关,请看Java安全通信:HTTPS与SSL。
代码
用于要使用加密功能,所以此处必须使用SSLSocket,关于其用法可以看 Java SSLSocket的使用
package com.example;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
public class MyHttpsClient {
public static class HTTPAddress {
String hostAddress;
int point = 80;
public HTTPAddress(String hostAddress, int point) {
this.hostAddress = hostAddress;
this.point = point;
}
}
public static void main(String[] args) {
//百度
String url = "https://www.baidu.com";
getStringByUrl(url);
}
public static final String HTTP_HEAD = "http://";
public static final String HTTPS_HEAD="https://";
public static HTTPAddress getHTTPAddress(String url) {
if (url == null || url.equals("")) {
return null;
}
String head = "";
//注意SSL的默认端口是443
int point = 443;
if (url.startsWith(HTTP_HEAD)) {
url = url.substring(HTTP_HEAD.length());
}
if (url.startsWith(HTTPS_HEAD)) {
url = url.substring(HTTPS_HEAD.length());
}
//获取域名部分
head = url.split("/")[0];
//如果在url中指定端口,则获取端口
int index = head.indexOf(":");
if (index != -1) {
String pointStr = head.substring(index + 1).trim();
head = head.substring(0, index);
point = Integer.valueOf(pointStr);
}
try {
//更具域名获取ip地址
String hostAddress = InetAddress.getByName(head).getHostAddress();
HTTPAddress httpAddress = new HTTPAddress(hostAddress, point);
return httpAddress;
} catch (UnknownHostException e) {
e.printStackTrace();
}
return null;
}
public static void getStringByUrl(String url) {
try {
HTTPAddress httpAddress = getHTTPAddress(url);
if (httpAddress == null) {
return;
}
SSLContext context = SSLContext.getInstance("SSL");
context.init(null,null,new SecureRandom());
SSLSocketFactory factory = context.getSocketFactory();
SSLSocket socket = (SSLSocket) factory.createSocket(httpAddress.hostAddress, httpAddress.point);
// Socket socket = new Socket(httpAddress.hostAddress, httpAddress.point);
OutputStreamWriter osw = new OutputStreamWriter(socket.getOutputStream());
StringBuffer sb = new StringBuffer();
//拼装请求头,注意最后需要用\r\n结尾
sb.append("GET / HTTP/1.1\r\n");
sb.append("Host: " + httpAddress.hostAddress+"\r\n");
sb.append("Connection: keep-alive\r\n");
//最后的\r\n不可省略表示报文首部与报文主体的分隔
sb.append("\r\n");
osw.write(sb.toString());
osw.flush();
InputStream is = socket.getInputStream();
int count = 0;
byte[] b = new byte[8192];
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while ((count = is.read(b)) != -1) {
baos.write(b, 0, count);
}
is.close();
baos.close();
String result = baos.toString();
//根据空行,分隔出报文首部,与报文主体
String separateStr = "\r\n\r\n";
int index = result.indexOf(separateStr);
String headStr = result.substring(0, index);
String contentStr = result.substring(index);
System.out.print("head:" + headStr + "\n");
System.out.print("content:" + contentStr + "\n");
} catch (Exception e) {
e.printStackTrace();
}
}
}
结果
这是返回的数据
我将其中的内容部分保存为一个html文件。
用Chrome打开就是这样的,除了图片以为,都正常显示,图片是没有下载。
注意事项
1.端口
SSL一般使用的是443端口
2.关于验证服务端证书
由于没有使用自己的服务端,所以直接使用默认实现就可以了。
SSLContext context = SSLContext.getInstance("SSL");
context.init(null,null,new SecureRandom());
3.请求格式
我用Fiddler进行抓包,找到的请求如下,注意它的请求头在资源URL的地方只有/,在Host的地方是域名且没有端口。我把自己的请求头改成这样才成功的。