tomcat搭建android https双向认证

来源:互联网 发布:北宋 知乎 编辑:程序博客网 时间:2024/05/29 07:06
模拟场景:
     Server端和Client(android)端通信,需要进行授权和身份的验证,即Client只能接受Server的消息,Server只能接受Client的消息。

实现技术:
    JSSE(Java Security Socket Extension)
            是Sun为了解决在Internet上的安全通讯而推出的解决方案。它实现了SSL和TSL(传输层安全)协议。在JSSE中包含了数据加密,服务器验证,消息完整性和客户端验证等技术。通过使用JSSE,开发人员可以在客户机和服务器之间通过TCP/IP协议安全地传输数据

(一) 为了实现消息认证。
   Server需要:
      1)KeyStore:  其中保存服务端的私钥
      2)Trust KeyStore:  其中保存客户端的授权证书, 同样,Client端(android)需要:
            1)KeyStore:其中保存客户端的私钥

            2)Trust KeyStore:其中保存服务端的授权证书

  我们可以使用Java自带的keytool命令,去生成这样信息文件
       1)生成服务端私钥,并且导入到服务端KeyStore文件中
                keytool -genkey -v -alias serverkey -keyalg RSA  -validity 3650 -keystore k_tomcat.keystore -dname "CN=192.168.1.100,OU=IT        Dept,O=flyingman,L=cn,ST=guangdong,c=CH"  -storepass 123456 -keypass 123456   (注:CN=192.168.1.100 (可以根据自己需要绑定对应的IP)
    就可以生成k_tomcat.keystore文件 , k_tomcat.keystore是给服务端用的,其中保存着自己的私钥

      2)根据私钥,导出服务端证书
           keytool -export -alias serverkey -keystore k_tomcat.keystore -file server.crt
        此时server.crt就是服务端的证书

      3)将服务端证书,导入到客户端的Trust KeyStore中
         keytool -importcert  -v -noprompt -trustcacerts -alias  serverkey  -file server.crt -keypass 123456 -keystore  t_client.keystore  -storepass 123456 -storetype BKS -providername "BC"  -provider org.bouncycastle.jce.provider.BouncyCastleProvider
  此t_client.keystore是给客户端用的,其中保存着受信任的证书

     { 注:

        [1] -storetype 需指定为"BKS", 否则android端无法使用, 因为其默认是这个, 而非"JKS";

       [2] -provider 也需要指定成org.bouncycastle.jce.provider.BouncyCastleProvider, 所以为此你需要下载对应jdk版本的bouncy的jar, 原因: jdk默认不支持, 所需jar包:  bcprov-  jdk16-143.jar,  下载OK后, 添加至jdk的 \jre\lib\ext目录下, 并修改jdk目录\jre\lib\security中的"java.security" 该文件, 在该文件添加一行:

       security.provider.1=sun.security.provider.Sun
       ......
       security.provider.11=org.bouncycastle.jce.provider.BouncyCastleProvider (添加此行代码)

      [3] 如果提示...policy...问题, 则需要下载新的jce_policy, 下载后解压, 将local_policy.jar 和 US_export_policy.jar 替换jdk目录jre\lib\security中的jar.

   }


(二) 生成客户端的私钥,客户端的证书,并且导入到服务端的Trust KeyStore中

     (1)  keytool -genkeypair -alias clientkey -keyalg RSA  -keysize 1024 -dname "CN=192.168.1.100,OU=IT Dept,O=flyingman,L=cn,ST=guangdong,c=CH" -validity 3650  -keypass 123456 -keystore k_client.keystore  -storepass 123456 -storetype BKS  -providername "BC" -provider org.bouncycastle.jce.provider.BouncyCastleProvider

    (2) keytool -exportcert  -v -rfc  -alias clientkey -file client.crt  -keystore k_client.keystore  -storepass 123456 -storetype BKS  -providername "BC"  -provider org.bouncycastle.jce.provider.BouncyCastleProvider

   (3) keytool -import -alias clientkey -file client.crt -keystore t_tomcat.keystore

如此一来,生成的文件分成两组
      服务端保存:k_tomcat.keystore 和 t_tomcat.keystore
      客户端保存:k_client.keystore  t_client.kyestore

(三) 配置tomcat的https

   (1) 找到代码段, 去掉xml注释, 启用https, 为了方便, 我建k_tomcat.keystore 和 t_tomcat.keystore放在conf目录下, https默认端口是443, 你可以修改成其它端口.

     <Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
         maxThreads="150" scheme="https" secure="true"
         clientAuth="true" sslProtocol="TLS"
         keystoreFile="conf/k_tomcat.keystore" keystorePass="123456"
         truststoreFile="conf/t_tomcat.keystore" truststorePass="123456"
        />

 (2)如果你想同时使用http, 则你启用https时, 无需要注释:

     <Connector port="8080" protocol="HTTP/1.1"  connectionTimeout="20000"   redirectPort="8443" />

而且你可以指定项目某些路径使用https, 具体在项目的web.xml中添加如下代码:

    <login-config>
         <auth-method>CLIENT-CERT</auth-method>
         <realm-name>Client Cert Users-only Area</realm-name>
    </login-config>
    <security-constraint>
        <web-resource-collection>
             <web-resource-name>SSL</web-resource-name>
             <url-pattern>/webservice</url-pattern> <!--指定webservice请求, 使用https, 即https://localhost:8443,  其它路径则可以使用: http://localhost:8080进行访问-->
        </web-resource-collection>
        <user-data-constraint>
             <transport-guarantee>CONFIDENTIAL</transport-guarantee>
        </user-data-constraint>
    </security-constraint>

(四)android端加载双向认证密钥

import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509KeyManager;
import javax.net.ssl.X509TrustManager;

 
import org.apache.http.HttpVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.params.ConnManagerParams;
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.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
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;

public class HttpClientTool {
    private static HttpClient httpClient;

    private HttpClientTool() {
    }

    public static synchronized HttpClient getHttpClient() {
        if (null == httpClient) {
            try {
                KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
                trustStore.load(HttpClientTool.class.getResourceAsStream("t_client.keystore"), "123456".toCharArray());
                KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
                keyStore.load(HttpClientTool.class.getResourceAsStream("k_client.keystore"), "123456".toCharArray());
                
            
                SSLSocketFactory sf = new SSLSocketFactoryEx(keyStore, "123456", trustStore); //双向认证
                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, 10000);
                // 设置socket超时
                HttpConnectionParams.setSoTimeout(params, 10000);

                // 设置http https支持
                SchemeRegistry schReg = new SchemeRegistry();
                schReg.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
                schReg.register(new Scheme("https", sf, 8083));

                ClientConnectionManager conManager = new ThreadSafeClientConnManager(params, schReg);
                httpClient = new DefaultHttpClient(conManager, params);
            } catch (Exception e) {
                e.printStackTrace();
                return new DefaultHttpClient();
            }
        }
        return httpClient;
    }

}

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

    public MySSLSocketFactory(KeyStore ks, String s , KeyStore tks) throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException,    UnrecoverableKeyException, CertificateException, IOException{
        super(ks, s, tks);
        
        KeyManagerFactory keyManager = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());   
        TrustManagerFactory trustManager = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
 
        keyManager.init(ks,  s.toCharArray());   
        trustManager.init(tks);
        sslContext.init(keyManager.getKeyManagers(), trustManager.getTrustManagers(), null);   
    }
   

public static void main(String[] args){

     try {
                HttpClient client = HttpClientTool.getHttpClient();
                HttpGet request = new HttpGet();
                request.setURI(new URI("https://192.168.1.100:8443/web_pro/xxxx"));  //192.168.1.100需与密钥中的对应
               
                HttpResponse response = client.execute(request);
                if (response.getStatusLine().getStatusCode() != 200) {
                    request.abort();

                     System.out.println("Request error....");

                 //   return result;
                }

                BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
                String line = null;
                while ((line = reader.readLine()) != null) {
                     System.out.println(line);
                }
                result = buffer.toString();
           } catch (Exception e) {
               e.printStackTrace();
           } 

      }
}

0 0