java中ssl/stl的使用
https即http+ssl/stl组成的网络安全传输协议,目的是防止数据在传输的过程中信息被泄露或篡改。
https在正确使用的时候是可以防止中间人攻击的,由于开发者在开发过程中编码不正确导致https
的错误使用,以至于攻击者可以获取或修改https传输的数据。
Android https的开发过程中常见的安全缺陷:
1.自定义X509TrustManager,checkServerTrusted中不对证书进行校验。
2.重写WebViewClient的onReceivedSslError方法时,调用proceed忽略证书验证错误信息继续加载页面,详情参考webview安全部分。
3.自定义HostnameVerifier,没有在verify函数中校验域名。
4.使用setHostnameVerifier(ALLOW_ALL_HOSTNAME_VERIFIER)信任所以Hostname.
android中使用https协议的漏洞代码
https协议实现的两个类httpsUrlConnection和httpClient,区别就是httpClient比HttpsUrlConnection功能更全面强大,但是实现HTTPS
链接的情况基本相同。下面是开发者为了开发方便没有正确实现X509Trustmanager导致https存在缺陷的例子。
class myX509TrustManager implements X509TrustManager
{
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException{
}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateExcepton{
}
public X509Certificate[] getAcceptedIssuers()
{
return null;
}
}
checkClientTrusted该方法检查客户端的证书,若不信任该证书则抛出异常。由于我们不需要对客户端进行认证,因此我们只需要执行默认的信任管理器的这个方法。JSSE中,默认的信任管理器类为TrustManager。
checkServerTrusted该方法检查服务器的证书,若不信任该证书同样抛出异常。通过自己实现该方法,可以使之信任我们指定的任何证书。在实现该方法时,也可以简单的不做任何处理,即一个空的函数体,由于不会抛出异常,它就会信任任何证书。
getAcceptedIssuers返回受信任的X509证书数组。
X509trustManager的三个方法中,由于开发者重写了checkServerTrusted方法而信任所有的证书,这样会造成中间人攻击从而产生安全问题。
httpsUrlConnection对自定义对hostname的校验缺陷代码
HttpsURLConnection.setDefaultHostnameVerifier(new MyHostnameVerifier());
static class MyHostnameVerifier implements HostnameVerifier{
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
}
}
httpClient中对hostname校验的缺陷代码
SSLSocketFactory sf = new SSLSocketFactoryEx(trustStore);
sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);//关闭host验证,允许和所有的host建立SSL通信
上面的两种方法都信任所有主机名,导致通信的过程中可能存在中间人攻击。
安全使用代码
安全的使用HTTPS编码,我们还是参考android官方指定的安全编码方式
1.如果证书是权威机构频发且存在已信任的证书列表里,一般使用以下代码即可。
package com.pc.httpstest;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.net.URL;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
public class Test_HTTP{
private static void copyInputStreamToOutputStream(java.io.InputStream in, PrintStream out) throws IOException {
byte[] buffer = new byte[1024];
int c = 0;
while ((c = in.read(buffer)) != -1) {
out.write(buffer, 0, c);
}
}
public static void main(String[] args) throws Exception {
URL url = new URL("https://www.baidu.com/");
URLConnection urlConnection = url.openConnection();
java.io.InputStream in = urlConnection.getInputStream();
copyInputStreamToOutputStream(in, System.out);
}
}
2.如果频发证书的机构不是权威机构,或者系统没有内置这个机构的证书,如果使用上面的语句一般会产生SSLHandshakeException异常,正确的使用方法谷歌官方也有相应的例子。如果导入服务器证书的话,一般服务器证书的过期时间较短,如果服务器证书过期了还必须更新到最新签发的证书。 最好的方式是导入根CA证书,这个过期时间较长,一般不用考虑客户端更新证书的问题。下载跟CA证书一般到官网下载对应的证书,像国内的沃通等都会提供跟CA证书的下载。
下面是谷歌官方的一个例子。
package com.pc.httpstest;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.net.URL;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
public class Test_HTTP{
private static void copyInputStreamToOutputStream(java.io.InputStream in, PrintStream out) throws IOException {
byte[] buffer = new byte[1024];
int c = 0;
while ((c = in.read(buffer)) != -1) {
out.write(buffer, 0, c);
}
}
public static void main(String[] args) throws Exception {
// Load CAs from an InputStream
// (could be from a resource or ByteArrayInputStream or ...)
CertificateFactory cf = CertificateFactory.getInstance("X.509");
// From https://www.washington.edu/itconnect/security/ca/load-der.crt
InputStream caInput = new BufferedInputStream(new FileInputStream("C:\\Users\\pc\\Desktop\\WS_CA1_NEW.crt"));
Certificate ca;
try {
ca = (Certificate) cf.generateCertificate(caInput);
} finally {
caInput.close();
}
// Create a KeyStore containing our trusted CAs
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", (java.security.cert.Certificate) ca);
// Create a TrustManager that trusts the CAs in our KeyStore
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
// Create an SSLContext that uses our TrustManager
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
// Tell the URLConnection to use a SocketFactory from our SSLContext
URL url = new URL("https://www.wosign.com/");
HttpsURLConnection urlConnection =
(HttpsURLConnection)url.openConnection();
urlConnection.setSSLSocketFactory(context.getSocketFactory());
java.io.InputStream in = urlConnection.getInputStream();
copyInputStreamToOutputStream(in, System.out);
}
}
3.关于hostname验证,谷歌官方也提供了相应的例子
// Create an HostnameVerifier that hardwires the expected hostname.
// Note that is different than the URL's hostname:
// example.com versus example.org
HostnameVerifier hostnameVerifier = new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
HostnameVerifier hv =
HttpsURLConnection.getDefaultHostnameVerifier();
return hv.verify("example.com", session);
}
};
// Tell the URLConnection to use our HostnameVerifier
URL url = new URL("https://example.org/");
HttpsURLConnection urlConnection =
(HttpsURLConnection)url.openConnection();
urlConnection.setHostnameVerifier(hostnameVerifier);
InputStream in = urlConnection.getInputStream();
copyInputStreamToOutputStream(in, System.out);
对于httpClient中hostname的验证就使用hostname严格验证模式
SSLSocketFactory sf = new MySSLSocketFactory(trustStore);
sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
参考链接
https://developer.android.com/training/articles/security-ssl.html#HttpsExample
http://blog.csdn.net/iispring/article/details/51615631
http://pingguohe.net/2016/02/26/Android-App-secure-ssl.html
http://www.droidsec.cn/浅析https中间人攻击与证书校验/
https://jaq.alibaba.com/community/art/show?spm=a313e.7916646.24000001.14.T9h6Bf&articleid=60
http://yaq.qq.com/blog/13
http://blog.csdn.net/cj649934578/article/details/47042131