SSL概念
SSL(Secure Sockets Layer,安全套接层),安全套接层是Netscape公司率先采用的网络安全协议。它是在传输通信协议(TCP/IP)上实现的一种安全协议,采用公开密钥技术。SSL广泛支持各种类型的网络,同时提供三种基本的安全服务,它们都使用公开密钥技术。
其继任者传输层安全(Transport Layer Security,TLS)是为网络通信提供安全及数据完整性的一种安全协议。TLS与SSL在传输层对网络连接进行加密。
Socket概念
网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。
建立网络通信连接至少要一对端口号(socket)。socket本质是编程接口(API),对TCP/IP的封装,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口;HTTP是轿车,提供了封装或者显示数据的具体形式;Socket是发动机,提供了网络通信的能力。
Socket的英文原义是“孔”或“插座”。作为BSD UNIX的进程通信机制,取后一种意思。通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。在Internet上的主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务。Socket正如其英文原义那样,像一个多孔插座。一台主机犹如布满各种插座的房间,每个插座有一个编号,有的插座提供220伏交流电, 有的提供110伏交流电,有的则提供有线电视节目。 客户软件将插头插到不同编号的插座,就可以得到不同的服务。
客户端证书生成
服务端给我们一个服务端的.pem格式的证书,通过Java指令将服务端证书添加到我们的密钥库中,生成我们的证书
keytool -import -alias 别名 -file 服务端证书路径 -storetype 密钥库(如 :JKS、BKS) -storepass 密码 -keystore (路径非必需)+ 文件名 -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath bcprov-ext-jdk15on-1.46.jar(换成自己的jar)
这里的storetype如果是Java服务用JKS,Android用BKS
后边的provider如果不用的话可能会在加载密钥库的时候出错,并且后边用的jar包如果1.46不可以的话可以换换版本试试,比如1.45。
代码实现
- 准备好必要的参数
private static final int SERVER_PORT = 0000;//端口号
private static final String SERVER_IP = "00.000.000.00";//连接IP
private static final String CLIENT_KET_PASSWORD = "000000";//私钥密码
private static final String CLIENT_TRUST_PASSWORD = "000000";//信任证书密码
private static final String CLIENT_AGREEMENT = "TLS";//使用协议
private static final String CLIENT_KEY_MANAGER = "X509";//密钥管理器(如果是Java服务此处为SunX509)
private static final String CLIENT_TRUST_MANAGER = "X509";//
private static final String CLIENT_KEY_KEYSTORE = "BKS";//密库,这里用的是BouncyCastle密库(Java服务为JKS)
private static final String CLIENT_TRUST_KEYSTORE = "BKS";//
- 生成SSLSocket对象
public void init(Context context) {
try {
//取得KeyManagerFactory和TrustManagerFactory的X509密钥管理器实例
KeyManagerFactory keyManager = KeyManagerFactory.getInstance(CLIENT_KEY_MANAGER);
TrustManagerFactory trustManager = TrustManagerFactory.getInstance(CLIENT_TRUST_MANAGER);
//取得BKS密库实例
KeyStore kks = KeyStore.getInstance(CLIENT_KEY_KEYSTORE);
KeyStore tks = KeyStore.getInstance(CLIENT_TRUST_KEYSTORE);
//加客户端载证书和私钥,通过读取资源文件的方式读取密钥和信任证书
kks.load(context
.getResources()
.openRawResource(R.raw.keyclient), CLIENT_KET_PASSWORD.toCharArray());
tks.load(context
.getResources()
.openRawResource(R.raw.keyclient), CLIENT_TRUST_PASSWORD.toCharArray());
//初始化密钥管理器
keyManager.init(kks, CLIENT_KET_PASSWORD.toCharArray());
trustManager.init(tks);
//取得SSL的SSLContext实例
SSLContext sslContext = SSLContext.getInstance(CLIENT_AGREEMENT);
//初始化SSLContext
sslContext.init(keyManager.getKeyManagers(), trustManager.getTrustManagers(), null);
//生成SSLSocket
Client_sslSocket = (SSLSocket) sslContext.getSocketFactory().createSocket(SERVER_IP, SERVER_PORT);
Client_sslSocket.setNeedClientAuth(true);
} catch (Exception e) {
e.printStackTrace();
}
}
- 发送数据
public void write(String body){
try {
byte data[] = dataProcessing(body);
if (data == null || data.length == 0)
throw new NullPointerException("数据处理出错!");
//取出长度
// int length = bys[0] << 8 & 0xFF00 | bys[1] & 0xFF;
OutputStream outputStream = Client_sslSocket.getOutputStream();
outputStream.write(data);
// 发送读取的数据到服务端
outputStream.flush();
outputStream.close();
} catch (IOException e) {
Log.e(this.getClass().getName(),"OutputStream获取失败!");
}
}
- 读取数据
public String read(){
try{
InputStream in = Client_sslSocket.getInputStream();
byte buffer[] = new byte[1024];
int temp = 0;
int count = 0;
temp = in.read(buffer);
String str = "";
if(temp != -1){
//前两位表示返回数据的长度 11是拼接的固定头数据,如果没有不用减11
int length = buffer[0] << 8 & 0xFF00 | buffer[1] & 0xFF;
byte[] result = new byte[length - 11];
if (length < 1022){
System.arraycopy(buffer,13,result,0,length - 11);
}else {
System.arraycopy(buffer,13,result,0,1011);//1011 = 1024 - 11 - 2;
while ((temp = in.read(buffer)) != -1){
System.arraycopy(buffer,0,result,1011 + count * 1024,temp);
count++;
}
}
str = new String(result, "UTF-8");
}
in.close();
return str;
} catch (UnsupportedEncodingException e) {
Log.e(this.getClass().getName(),"数据编码格式错误!");
} catch (IOException e) {
Log.e(this.getClass().getName(),"InputStream获取失败!");
}
return "";
}
5.关闭连接
public void close(){
try {
Client_sslSocket.close();
} catch (IOException e) {
Log.e(this.getClass().getName(),"连接关闭失败" + " :" + e.getMessage().toString());
}
}