android ssl、https验证

准备知识:
Keytool工具的使用。
在用Android平台上使用SSL,第一步就是生成证书。
1、证书的生成
1.1生成服务器端的证书py
keytool -genkey -alias test -keystore test.jks 
1.2 将keystore中的cert导出来,用来生成客户端的验证证书
[html] 
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] 
security.provider.11=org.bouncycastle.jce.provider.BouncyCastleProvider 
1.3.3 生成android平台的证书
[html]
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)通信完毕可以关闭服务套接字。
  服务端代码: 
[java]
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=new 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客户端代码:    
[java] 
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方式
[java]
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方式:
[java] 
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); 
        } 
  } 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值