HttpClient 4.5 利用HTTPS提交POST請求
最近在寫一個測試伺服器的程式,其中需要使用HTTPS協議,向伺服器提交多個請求,由於伺服器的證書是自己做的,因此要做證書的驗證等工作。在網上查了許多例子,發現都不太合適,因為大部分程式碼都沒做證書驗證,所以容易受到中間人攻擊。這裡查閱了許多文件,並編寫了自己的程式碼,做一個總結。
我這裡使用了HttpClient 4.5版本。
接下來直接上程式碼了。
首先需要實現一個X509TrustManager介面,這個介面是用來判斷伺服器提供的證書是否可以被客戶端信任。這個類需要一個證書,我們可以通過瀏覽器的匯出證書功能,將伺服器上我們自己建立的證書匯出在本地。在MyX509TrustManager的構造方法中,利用CertificateFactory生成一個Certificate例項。checkClientTrusted是用來檢查客戶端的證書的,這裡我們只需要檢測伺服器端的證書就可以了,因此checkClientTrusted方法體就不新增程式碼了。checkServerTrusted是用來檢查伺服器端證書的合法性的,我們在這裡對它進行一些處理。我這裡用了一個非常簡單的方法,就是比較伺服器端傳送來的證書和自己本地的證書是否一致。如果沒有一樣的證書,就直接丟擲異常。
package com.uestc.test.upload; import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io.IOException; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import javax.net.ssl.X509TrustManager; public class MyX509TrustManager implements X509TrustManager { private Certificate cert = null; public MyX509TrustManager() { try { FileInputStream fis = new FileInputStream( "/Users/justyoung/Desktop/upload/Cloud3"); BufferedInputStream bis = new BufferedInputStream(fis); CertificateFactory cf = CertificateFactory.getInstance("X.509"); while (bis.available() > 0) { cert = cf.generateCertificate(bis); // System.out.println(cert.toString()); } bis.close(); } catch (CertificateException | IOException e) { e.printStackTrace(); } } @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { for (X509Certificate cert : chain) { if (cert.toString().equals(this.cert.toString())) return; } throw new CertificateException("certificate is illegal"); } @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[] { (X509Certificate) cert }; } }
然後,又實現了一個HostnameVerifier介面,這個類主要是在SSL握手時驗證對方主機名稱的,這樣做的一個目的也是防止連結被重定向到其他的不安全的地址上去,並且若出現伺服器證書上的Hostname和實際的URL不匹配時,也能做一些處理,否則會丟擲這樣的異常:javax.net.ssl.SSLPeerUnverifiedException: Host name '192.168.2.177' does not match the certificate subject provided by the peer,因此實現HostnameVerifier介面,我們能做一些hostname確認的工作,提高安全性。
package com.uestc.test;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
public class MyVerifyHostname implements HostnameVerifier {
@Override
public boolean verify(String arg0, SSLSession arg1) {
if (arg0.equals("192.168.2.177") || arg0.equals("cyber-space2015.imwork.net"))
return true;
else
return false;
}
}
最後就是利用HttpClient來完成Post請求的提交了。這裡我們自定義了SSLContext,需要使用init方法,並傳遞進我們自己建立的TrustManager例項陣列。然後利用HttpClients的custom方法自定義了一些引數,並生成HttpClient例項。其他程式碼就沒什麼特殊的了。
package com.uestc.test;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.BufferedHttpEntity;
import org.apache.http.entity.FileEntity;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
public class UploadTest {
private String host;
private String cookie;
private String path;
private int port;
private SSLContext sslContext;
private X509TrustManager tm;
private HttpClient httpClient;
public UploadTest(String host, String cookie, String path, int port)
throws Exception {
this.host = host;
this.cookie = cookie;
this.path = path;
this.port = port;
sslContext = SSLContext.getInstance("TLS");
tm = new MyX509TrustManager();
sslContext.init(null, new TrustManager[] { tm },
new java.security.SecureRandom());
httpClient = HttpClients.custom()
.setSSLHostnameVerifier(new MyVerifyHostname())
.setSSLContext(sslContext).build();
}
public String uploadFile(String localPath, String filename)
throws URISyntaxException, ClientProtocolException, IOException {
URI uri = new URIBuilder().setScheme("https").setHost(host)
.setPath(path).setParameter("filename", filename).setPort(port)
.build();
HttpPost httpPost = new HttpPost(uri);
FileEntity fileEntity = new FileEntity(new File(localPath));
fileEntity.setChunked(true);
httpPost.setEntity(fileEntity);
httpPost.addHeader("Cookie", cookie);
HttpResponse response = httpClient.execute(httpPost);
HttpEntity httpEntity = response.getEntity();
httpEntity = new BufferedHttpEntity(httpEntity);
String resultString = EntityUtils.toString(httpEntity);
return resultString;
}
public static void main(String[] args) {
try {
UploadTest test = new UploadTest(
"192.168.2.177",
"XXXXX",
"XXXXX", 443);
String resultString = test.uploadFile(
"/Users/justyoung/Desktop/upload/test.java", "mytest1");
System.out.println(resultString);
} catch (URISyntaxException | IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
完。