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

android ssl验证、https验证

2013-10-24 09:40 218 查看
准备知识:

Keytool工具的使用。

在用Android平台上使用SSL,第一步就是生成证书。
1、证书的生成
1.1生成服务器端的证书py

keytool -genkey -alias test -keystore test.jks  

1.2 将keystore中的cert导出来,用来生成客户端的验证证书

[html] view
plaincopy

keytool -exportcert -alias test -file test.cert -keystore test.jks  

1.3 生成Android平台的证书

因为Android 要求要BC证书,而Java的keytool本身不提供BKS格式,因此要自己手动配置。个人在配置的过程到了文件正在使用中,保存失败的情况,我的做法是将文件备份一下,用unlocker删除后将修改好备份放到原位置就好了。
1.3.1 下载 bcprov-ext-jdk15on-146.jar 
到官网下载本地jdk对应的jar,复制到C:\Program
Files\Java\jdk1.6.0_43\jre\lib\ext
1.3.2 配置bcprov
在 jdk_home\jre\lib\security\目录中找到 java.security 在内容增加一行(数字可以自己定义)

[html] view
plaincopy

security.provider.11=org.bouncycastle.jce.provider.BouncyCastleProvider  

1.3.3 生成android平台的证书

[html] view
plaincopy

keytool -importcert -keystore test.bks -file test.cert -storetype BKS -provider org.bouncycastle.jce.provider.BouncyCastleProvider
 

一、什么是SSL?

  SSL(Secure Sockets Layer 安全套接层),及其继任者传输层安全(Transport Layer Security,TLS)是为网络通信提供安全及数据完整性的一种安全协议。TLS与SSL在传输层对网络连接进行加密。

  SSL/TLS协议位于HTTP协议与传输层协议之间,采用公钥技术,为两个应用间的通讯提供加密、完整性校验以及身份认证。

SSL协议提供了三个功能:

使用公钥证书对双端进行认证(一般仅进行服务器认证,客户端认证为可选)

通信加密(Confidentiality)

数据完整性检查(Data integrity)

二、SSL握手

  SSL会话以称之为SSL握手的一系列消息交换开始,在握手过程中服务器通过公钥技术向客户端认证自身(可选,客户端向服务器认证自身),客户端和服务器协商加密消息的对称密钥,SSL会话握手后的消息都使用对称密钥加密后传输,对称密钥还用于消息完整性检查。

 


Client->Server ClientHello

Client向Server发送一个“ClientHello”消息,说明它支持的密码算法列表、压缩方法及最高协议版本,以及稍后将被使用的随机数

Version: 客户端支持TLS协议最高版本号(最高3.3)

RandomNumber:客户端产生的28byte随机数,后续用作加密种子。

CipherSuites 客户端支持的加密算法和密钥大小列表

CompressionMethods 客户可支持的压缩算法

Server->Client ServerHello

服务器向客户端发送“ServerHello”消息,包含服务器选择的密码算法、压缩方法、协议版本以及服务器产生的随机数。

Version: 选择的TLS版本号

RandomeNumber:服务器产生的28字节随机数,后续用作加密种子

CipherSuite: 选择的加密算法和密钥大小。

CompressionMethod: 选择的数据压缩算法

Server->Client Certificate

Server向Client发送自身的证书链,证书链从服务器的公钥证书(public-key

certificate)开始一直到CA的根证书(certificate

authority’s root certificate).

客户端需要对server的证书链进行验证,如果验证不通过,则终止连接。若验证通过,则可从server的公钥证书中获取到server的公钥。验证流程见后续介绍。

Server->Client CertificateRequest

如果Server需要认证Client,则发送此消息请求Client的公钥证书,此消息为可选。消息中包含

Certificate Types: Server可接收的证书类型列表

DistinguishedNames: Server可接收的CA DN列表。

Server->Client ServerHelloDone

Server发送此消息通知Client已完成了初始化协商消息。

Client->Server Certificate

如果Server请求了Client的证书,即存在消息4,则client将自身的证书链信息发送给Server。Server要对Client的证书链进行验证,如果验证不通过,则终止连接,如果验证通过则可从Client的公钥证书中获取到Client的公钥。验证流程见后续介绍.

Client->Server ClientKeyExchange

Client向Server发送premaster secret,并且用Server的公钥加密。premaster secret用于双方计算出对后续消息加密的对称密钥. 使用Server的公钥加密的目的是确认Server具有消息3中所生成公钥证书的私钥,避免发送给伪造的服务器。

Client->Server CertificateVerify

如果Server请求了Client的证书,则还需要发送CertificateVerify消息,Client将到现在为止发送和接收到的握手消息,使用Client的私钥进行签名发送给Server,用于向Server证明其有消息6中声称Client公钥的私钥数据。Server可使用消息6中获得的Client公钥对消息进行验证。

Client->Server ChangeCipherSpec

Client使用客户端随机数,服务器随机数以及premaster secret产生加密消息的对称密钥Master Secret。然后发送此消息告知Server后续消息将使用对称密钥加密

Client->Server Finished

Client向Server发送此消息通知对端协商成功,此消息使用协商的公钥密钥加密。

Server->Client ChangeCipherSpec

Server使用客户端随机数,服务器随机数以及premaster secret产生加密消息的对称密钥Master Secret。然后发送此消息告知Client后续消息将使用对称密钥加密.

Server->Client Finished

Server向Client发送此消息通知对端协商成功,此消息使用协商的公钥密钥加密。

三、SSL通信模式:

1.服务端:

SSL服务端需要通过SSL服务套接字来提供服务接口,而SSL服务套接字需要通过SSL上下文实例来创建。以下是对SSL服务端的启用过程的描述。

(1)通过指定协议(一般是TLS)获取SSL上下文(SSLContext)实例。

(2)通过指定算法(X.509相关)获取密钥管理器工厂(KeyManagerFactory)实例。

(3)通过指定类型和提供者获取密钥库(KeyStore)实例。

(4)密钥库实例使用约定的密码加载(Load)密钥库文件(.keystore)。

(5)密钥管理器工厂实例使用约定的密码和(4)中密钥库进行初始化(Initialize)。

(6)SSL上下文实例通过密钥管理器工厂实例提供的密钥管理器来初始化(Initialize)。

(7)当SSL上下文实力初始化成功后,就可以获取该上下文势力所关联的服务套接字工厂(ServerSocketFactory)实例

(8)服务套接字工厂实例依据指定的服务端口来创建(Create)服务套接字(ServerSocket)。

(9)当SSL服务套接字创建成功,就可以等待客户端的连接,与客户端进行通信。

(10)通信完毕可以关闭服务套接字。

2.客户端

(1)通过指定协议(一般是TLS)获取SSL上下文(SSLContext)实例。

(2)通过指定算法(X.509相关)获取密钥管理器工厂(KeyManagerFactory)实例。

(3)通过指定算法(X.509相关)获取信任管理器工厂(TrustManagerFactory)实例。

(4)通过指定类型和提供者获取密钥库(KeyStore)实例。

(5)通过指定类型和提供者获取信任密钥库(KeyStore)实例。

(6)(4)中密钥库实例使用约定的密码加载(Load)密钥库文件(.keystore)。

(7)(5)中信任密钥库实例使用约定的密码加载(Load)密钥库文件(.keystore)。

(8)密钥管理器工厂实例使用约定的密码和(4)中密钥库进行初始化(Initialize)。

(9)信任密钥管理器工厂实例使用约定的密码和(5)中密钥库进行初始化(Initialize)。

(10)当SSL上下文实力初始化成功后,就可以获取该上下文实例所关联的套接字工厂(SocketFactory)实例

(11)套接字工厂实例依据指定的主机和端口来创建(Create)客户端套接字(Socket)。

(12)当SSL服务套接字创建成功,就可以向服务端发送请求,与服务端进行通信。

(13)通信完毕可以关闭服务套接字。

 

  服务端代码:  

import java.io.BufferedInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import javax.net.ServerSocketFactory;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
/**
* @author  draem0507@gmail.com
* @TODO    java线程开发之四 SSL加密
* 开发步骤
* 1.生成服务端密钥
* 2.导出服务端证书
* 3.生成客户端密钥
* 4.程序开发测试
* 关于证书的生成请参考readme.txt
* 参考资料:http://chrui.iteye.com/blog/1018778
* @version 1.0
* @date 2013-5-7 23:22:45
* @update 2013-5-8 10:22:45
* @blgos http://www.cnblogs.com/draem0507 */
public class Server {
private ServerSocket serverSocket;
private final static char[] password="1qaz2wsx".toCharArray();
private SSLContext context;
private InputStream inputStream;

public Server() {
inputStream=this.getClass().getResourceAsStream("/test.jks");
initContext();
try {
//直接运行会报 javax.net.ssl.SSLException:
//ServerSocketFactory factory=     SSLServerSocketFactory.getDefault();
ServerSocketFactory factory=     context.getServerSocketFactory();
//            serverSocket = new ServerSocket(10000);
serverSocket=factory.createServerSocket(10000);
System.out.println("======启动安全SocektServer成功=========");
while (true) {
Socket socket = serverSocket.accept();
new ReceiveSocket(socket).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}

//ssl 上下文对象的初始化
private void initContext() {
try {
KeyStore store=KeyStore.getInstance("JKS");
store.load(inputStream, password);
KeyManagerFactory factory=KeyManagerFactory.getInstance("SunX509");
factory.init(store,password);
KeyManager []keyManagers=factory.getKeyManagers();
context=SSLContext.getInstance("SSL");
context.init(keyManagers, null    , null);
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (UnrecoverableKeyException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}

}
public static void main(String[] args) {
new Server();
}
private class ReceiveSocket extends Thread {
private Socket socket;
public ReceiveSocket(Socket socket) {
this.socket = socket;
}
private ObjectInputStream reader;
private ObjectOutputStream writer;
@Override
public void run() {
try {
reader=new ObjectInputStream(new BufferedInputStream(socket.getInputStream()));
//writer=ne
df41
w ObjectOutputStream(socket.getOutputStream());
// 开启无限循环 监控消息

//java.io.EOFException
Object obj=    reader.readUTF();
SocketAddress address = socket.getRemoteSocketAddress();
System.out.println(address.toString() + ">\t" + obj);
//                Object obj=    reader.readObject();
//                    if(obj!=null)
//                    {
//                        User user =(User)obj;
//                        System.out.println("id=="+user.getPassword()+"\tname=="+user.getName());
//                    }
//    while (true) {}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (null != reader) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (null != writer) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}


 Android客户端代码:     

protected Void doInBackground(Void... params) {
Log.i(TAG, "doInBackground");
try {
SSLContext context;
KeyStore ts = KeyStore.getInstance("BKS");
ts.load(getResources().openRawResource(R.raw.test),
"1qaz2wsx".toCharArray());
TrustManagerFactory tmf = TrustManagerFactory
.getInstance("X509");
tmf.init(ts);
TrustManager[] tm = tmf.getTrustManagers();
context = SSLContext.getInstance("SSL");
context.init(null, tm, null);
SocketFactory factory = context.getSocketFactory();
SSLSocket socket = (SSLSocket) factory.createSocket(
"192.168.70.249", 10000);
ObjectOutputStream out = new ObjectOutputStream(
socket.getOutputStream());
out.writeUTF(UUID.randomUUID().toString());
out.flush();
System.out.println("========客户端发送成功=========");
;
socket.close();
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}

四、HTTPS

 HTTPS可以视为HTTP的安全版本(Secure),其安全基础基于SSL协议(Secure Socket Layer,安全套接字层)。HTTPS在HTTP的基础上添加了一个加密和身份验证。其默认端口是443.对于一些对数据安全要求比较高的网络应用,比如网络支付,网上银行,都是采用HTTPS通信机制,其规范:RFC2818

HTTPS URL连接的方式访问HTTPS服务器与HTTP URL访问HTTP服务器的方式基本相同。用到的类:HttpsURLConnection。

代码例子(验证服务器):

HttpsURLConnection方式

public static String getHttpsContent(Map<String, String> paramsMap, String urlPath){
try{

File bksFile = new File("bks文件路径");
if(bksFile.exists()){
InputStream keyStoreInput = new FileInputStream(bksFile);
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(keyStoreInput, KEYSTORE_PASSWORD.toCharArray());

// Create a TrustManager that trusts the CAs in our KeyStore
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);

KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509");
kmf.init(keyStore, KEYSTORE_PASSWORD.toCharArray());

// Create an SSLContext that uses our TrustManager
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

X509HostnameVerifier hostnameVerifier = SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;

HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);

StringBuilder entityBuilder = new StringBuilder("");
if(paramsMap!=null && !paramsMap.isEmpty()){
for(Map.Entry<String, String> entry : paramsMap.entrySet()){
entityBuilder.append(entry.getKey()).append('=');
entityBuilder.append(URLEncoder.encode(entry.getValue(), HTTP.UTF_8));
entityBuilder.append('&');
}
entityBuilder.deleteCharAt(entityBuilder.length() - 1);
}

byte[] entity = entityBuilder.toString().getBytes();

// Tell the URLConnection to use a SocketFactory from our SSLContext
//URL url = new URL("https://172.16.18.109");
URL url = new URL(urlPath);
HttpsURLConnection urlConnection = (HttpsURLConnection) url
.openConnection();
urlConnection.setSSLSocketFactory(sslContext.getSocketFactory());

urlConnection.setConnectTimeout(5 * 1000);
urlConnection.setRequestMethod("POST");
urlConnection.setDoOutput(true);//允许输出数据
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
urlConnection.setRequestProperty("Content-Length", String.valueOf(entity.length));
OutputStream outStream = urlConnection.getOutputStream();
outStream.write(entity);
outStream.flush();
outStream.close();

InputStream in = urlConnection.getInputStream();
StringBuffer sb = new StringBuffer();

String line = null;
char ch = '\u0000';
int temp = 0 ;
while ((temp = in.read()) != -1) {
ch = (char) temp;
sb.append((char) temp);
}
String result = sb.toString();

return result;
}

return "-1";

}catch(Exception e){
e.printStackTrace();
return "-2";
}

}


HttpClient方式:

public static  synchronized HttpClient getHttpClient(Context context) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, KeyManagementException, UnrecoverableKeyException
{
if(null==httpClient)
{
AssetManager sm=	 context.getAssets();
InputStream keyStroreInputStream=sm.open("ca_zx.bks");
KeyStore  keyStore=KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(keyStroreInputStream, KEYSTORE_PASSWORD.toCharArray());

SSLSocketFactory sf=new MySSLSocketFactory(keyStore);
sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

HttpParams params=new BasicHttpParams();

HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
HttpProtocolParams.setUseExpectContinue(params, true);

ConnManagerParams.setTimeout(params, 10000);
HttpConnectionParams.setConnectionTimeout(params, 15000);
HttpConnectionParams.setSoTimeout(params, 20000);

SchemeRegistry schreg=new SchemeRegistry();
schreg.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
schreg.register(new Scheme("https", 	sf , 443));
ClientConnectionManager conman=new ThreadSafeClientConnManager(params, schreg);
httpClient=new DefaultHttpClient(conman, params);

}
return httpClient;
}

private static  class MySSLSocketFactory extends SSLSocketFactory
{
SSLContext sslContext=SSLContext.getInstance("TLS");

public MySSLSocketFactory(KeyStore truststore)
throws NoSuchAlgorithmException, KeyManagementException,
KeyStoreException, UnrecoverableKeyException {
super(truststore);

TrustManager tm=new X509TrustManager() {
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
}
};

KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509");
kmf.init(truststore, KEYSTORE_PASSWORD.toCharArray());

sslContext.init(kmf.getKeyManagers(), new TrustManager[]{ tm}, null);
}

@Override
public Socket createSocket() throws IOException {
// TODO Auto-generated method stub
return sslContext.getSocketFactory().createSocket();
}

@Override
public Socket createSocket(Socket socket, String host, int port,
boolean autoClose) throws IOException, UnknownHostException {

return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
}
}


参考:http://blog.csdn.net/acnt3w/article/details/9169995 Android SSL 过程记录(证书的生成+例子代码)
http://www.winddisk.com/2013/02/16/httpsssl%E4%BB%8B%E7%BB%8D/  https/SSL介绍
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息