您的位置:首页 > 理论基础 > 计算机网络

【android安全】之使用ssl验证保护网络数据传输安全。

2015-07-15 23:25 751 查看
SSL pinning 限定受信SSL的范围。

https协议验证服务器身份的方式通常有三种,一是根据浏览器或者说操作系统(Android)自带的证书链;二是使用自签名证书;三是自签名证书加上SSL Pinning特性。第一种需要到知名证书机构购买证书,需要一定预算。第二种多见于内网使用。第三种在是安全性最高的,但是需要浏览器插件或客户端使用了SSL Pinning特性。

Android应用程序在使用https协议时也使用类似的3种方式验证服务器身份,分别是系统证书库、自带证书库、自带证书库 + SSL Pinning特性。

所以SSL Pinning,即SSL证书绑定,是验证服务器身份的一种方式,是在https协议建立通信时增加的代码逻辑,它通过自己的方式验证服务器身份,然后决定通信是否继续下去。它唯一指定了服务器的身份,所以安全性较高。

一、先介绍一种使用服务器证书pin码验证方式的安全传输(本方式在apk不被篡改后重编译的情况下安全,上篇文章讲了如何防止apk被篡改的方法),步骤如下:


1,获取服务器的证书pin码。

2,将pin码复制到android app的代码里面。可写在Const类里面,例如:

package com.test;

public final class Const {

public static final String[] SSL_PINS={"f30012bbc18c231ac1a44b788e410ce754182513","bb0012bbc18c231ac1a44b788e410ce754182513"};

}


3,app进行网络访问时进行pin码验证。可使用如下工具类,调用时传入自己服务器的pin码集即可(该工具类依赖AndroidPinning库https://github.com/moxie0/AndroidPinning):

package com.test;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;

import javax.net.ssl.HttpsURLConnection;

import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.HttpVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import org.thoughtcrime.ssl.pinning.PinningSSLSocketFactory;
import org.thoughtcrime.ssl.pinning.util.PinningHelper;

import android.content.Context;

public class SSLHttpUtil {

private final static int CONNET_TIMEOUT = 10 * 1000;
private final static int READ_TIMEOUT = 10 * 1000;
private final static String ENCODING = "GBK";

/**
*
* @param urlString
* @param params
* @param files
* @param pins
*            服务器各个证书的pin码集合。
* @param context
* @return
*/
public static String post(String urlString, Map<String, String> params,
Map<String, File> files, String[] pins, Context context) {
HttpsURLConnection conn = null;
DataOutputStream outStream = null;
try {
String BOUNDARY = UUID.randomUUID().toString();
String PREFIX = "--", LINEND = "\r\n";
String MULTIPART_FROM_DATA = "multipart/form-data";
URL url = new URL(urlString);

conn = PinningHelper
.getPinnedHttpsURLConnection(context, pins, url);
conn.setReadTimeout(READ_TIMEOUT);
conn.setConnectTimeout(CONNET_TIMEOUT);
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setUseCaches(false);
conn.setRequestMethod("POST");
conn.setRequestProperty("connection", "keep-alive");
conn.setRequestProperty("Charsert", "UTF-8");
conn.setRequestProperty("Content-Type", MULTIPART_FROM_DATA
+ ";boundary=" + BOUNDARY);
//
outStream = new DataOutputStream(conn.getOutputStream());
if (params != null) {
// 棣栧厛缁勬嫾鏂囨湰绫诲瀷鐨勫弬锟�?
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> entry : params.entrySet()) {
sb.append(PREFIX);
sb.append(BOUNDARY);
sb.append(LINEND);
sb.append("Content-Disposition: form-data; name=\""
+ entry.getKey() + "\"" + LINEND);
sb.append("Content-Type: text/plain; charset=" + ENCODING
+ LINEND);
sb.append("Content-Transfer-Encoding: 8bit" + LINEND);
sb.append(LINEND);
sb.append(entry.getValue());
sb.append(LINEND);
}

outStream.write(sb.toString().getBytes());
}
// 鍙戯拷?锟芥枃浠舵暟锟�?
if (files != null) {
for (Map.Entry<String, File> file : files.entrySet()) {
StringBuilder sb1 = new StringBuilder();
sb1.append(PREFIX);
sb1.append(BOUNDARY);
sb1.append(LINEND);
sb1.append("Content-Disposition: form-data; name=\""
+ file.getKey() + "\"; filename=\""
+ file.getValue().getName() + "\"" + LINEND);
sb1.append("Content-Type: application/octet-stream; charset="
+ ENCODING + LINEND);
sb1.append(LINEND);
outStream.write(sb1.toString().getBytes());

InputStream is = new FileInputStream(file.getValue());
byte[] buffer = new byte[1024];
int len = 0;
while ((len = is.read(buffer)) != -1) {
outStream.write(buffer, 0, len);
}
is.close();
outStream.write(LINEND.getBytes());
}
}
// 璇锋眰缁撴潫鏍囧織
byte[] end_data = (PREFIX + BOUNDARY + PREFIX + LINEND).getBytes();
outStream.write(end_data);
outStream.flush();
String sb2 = null;
if (conn.getResponseCode() == HttpStatus.SC_OK) {
InputStream in = conn.getInputStream();
sb2 = readStreamToString(in);

}

return sb2;
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {

try {
outStream.close();
conn.disconnect();
} catch (Exception e) {
// ignore.
e.printStackTrace();
}
}

}

private static String readStreamToString(InputStream inStream) {
ByteArrayOutputStream outStream = null;
try {
outStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = inStream.read(buffer)) != -1) {
outStream.write(buffer, 0, len);
}
inStream.close();
outStream.flush();
String ret = outStream.toString();
outStream.close();
return ret;
} catch (Exception e) {
try {
inStream.close();
outStream.close();
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
return null;
}
}

/**
*
* @param url
* @param params
* @param context
* @param pins
*            服务器各个证书的pin码集合。
* @return
*/
public static String get(String url, Map<String, String> params,
Context context, String[] pins) {
try {
String realUrl = generateUrl(url, params);
HttpClient client = getNewHttpClient(context, pins);
HttpGet getMethod = new HttpGet(realUrl);
HttpResponse response = client.execute(getMethod);
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
StringBuilder builder = new StringBuilder();
BufferedReader reader = new BufferedReader(
new InputStreamReader(response.getEntity().getContent()));
for (String s = reader.readLine(); s != null; s = reader
.readLine()) {
builder.append(s);
}
String result = builder.toString();
return result;
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

/**
*
* @param url
* @param params
* @param context
* @param pins
*            服务器各个证书的pin码集合。
* @return
*/
public
c2fc
static String post(String url, Map<String, String> params,
Context context, String[] pins) {
try {
HttpClient client = getNewHttpClient(context, pins);
HttpPost postMethod = new HttpPost(url);
List<BasicNameValuePair> pairs = new ArrayList<BasicNameValuePair>();
if (params != null && params.size() > 0) {
Iterator<Entry<String, String>> iterator = params.entrySet()
.iterator();
while (iterator.hasNext()) {
Entry<String, String> param = iterator.next();
String key = param.getKey();
String value = param.getValue();
BasicNameValuePair pair = new BasicNameValuePair(key, value);
pairs.add(pair);
}
postMethod
.setEntity(new UrlEncodedFormEntity(pairs, HTTP.UTF_8));
}
HttpResponse response = client.execute(postMethod);
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
return EntityUtils.toString(response.getEntity());
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

/**
* 鑾峰彇HttpClient
*
* @return
*/
private static HttpClient getNewHttpClient(Context context, String[] pins) {
try {

HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
HttpConnectionParams.setConnectionTimeout(params, CONNET_TIMEOUT);
HttpConnectionParams.setSoTimeout(params, READ_TIMEOUT);

SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory
.getSocketFactory(), 80));
registry.register(new Scheme("https", new PinningSSLSocketFactory(
context, pins, 0), 443));
ClientConnectionManager ccm = new ThreadSafeClientConnManager(
params, registry);
return new DefaultHttpClient(ccm, params);
} catch (Exception e) {
return new DefaultHttpClient();
}
}

private static String generateUrl(String url, Map<String, String> params) {
StringBuilder urlBuilder = new StringBuilder(url);
if (null != params) {
urlBuilder.append("?");
Iterator<Entry<String, String>> iterator = params.entrySet()
.iterator();
while (iterator.hasNext()) {
Entry<String, String> param = iterator.next();
String key = param.getKey();
String value = param.getValue();
urlBuilder.append(key).append('=').append(value);
if (iterator.hasNext()) {
urlBuilder.append('&');
}
}
}
return urlBuilder.toString();
}
}


备注:获取服务器证书pin码的方法:
package com.commonlib.utils;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
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 CalculatePins {

private MessageDigest digest;

public CalculatePins() throws NoSuchAlgorithmException {
digest = MessageDigest.getInstance("SHA1");
}

/**
* 打印服务器的证书公钥(即pin码)
* @param host
* @param port
* @throws Exception
*/
public void fetchPrintServerPinHashs(String host, int port)
throws Exception {
final SSLContext conext = SSLContext.getInstance("TLS");
PubKeyExtractingTrustManager tm = new PubKeyExtractingTrustManager();
conext.init(null, new TrustManager[] { tm }, null);
SSLSocketFactory fac = conext.getSocketFactory();

SSLSocket socket = (SSLSocket) fac.createSocket(host, port);

socket.setSoTimeout(10 * 1000);
socket.startHandshake();
socket.close();
}

private class PubKeyExtractingTrustManager implements X509TrustManager {

@Override
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
// TODO Auto-generated method stub

}

@Override
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
for (X509Certificate cert : chain) {
byte[] keyEncoded = cert.getPublicKey().getEncoded();
byte[] pin = digest.digest(keyEncoded);
String pinHex = bytesToHex(pin);
System.out.println(pinHex);

}

}

private String bytesToHex(byte[] bytes) {
final char[] hexArray = "0123456789ABCDEF".toCharArray();
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}

@Override
public X509Certificate[] getAcceptedIssuers() {
// TODO Auto-generated method stub
return null;
}

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android安全