java web https 单向认证 双向认证

时间:2024-04-02 17:12:40

        研究了有一天了,最后一博讲解https,https接口也是以后接口开发中必定会碰到的,这里只是简单介绍一下简单的java工程中的实现,在更复杂的项目中必定有更优秀的框架来实现。

        第一步先创建两个秘钥库,服务的库和客户端库,我这里都用keystore文件保存。

        jdk自带的keytool命令生成各自的秘钥库

keytoo -****** -alias server -keypass 123456 -keysize 1024 -keyalg RSA -validity 720 -keystore "e:/keystore/server.keystore"  -storeypass 123456 -dname "CN=localhost,OU=sf,O=cib,L=sz,ST=gd,C=cn"

keytoo -****** -alias client -keypass 123456 -keysize 1024 -keyalg RSA -validity 720 -keystore "e:/keystore/client.keystore"  -storeypass 123456 -dname "CN=localhost,OU=sf,O=cib,L=sz,ST=gd,C=cn"

      导出各自秘钥并导入到对方的秘钥库中

keytool -export -alias server -keystore "e:/keystore/server.keystore" -file "e:/keystore/cer/server.cer"  -storeypass 123456

keytool -export -alias client-keystore "e:/keystore/client.keystore" -file "e:/keystore/cer/client.cer"  -storeypass 123456

keytool -import -alias client -file "e:/keystore/cer/client.cer"  -keystore "e:/keystore/server.keystore" -storeypass 123456

keytool -import -alias server -file "e:/keystore/cer/server .cer"  -keystore "e:/keystore/client.keystore" -storeypass 123456

      查看秘钥库内容

keytool -list -v -keystore -keystore "e:/keystore/server.keystore"  -storepass 123456

配置tomcat server.xml服务器SSL请求 clientAuth="true" 为双向认证

   <Connector SSLEnabled="true" 
    keystoreFile="E:\keystore\httpsweb.keystore" 
    keystorePass="changeit" 
    truststoreFile="E:\keystore\httpsweb.keystore" 
    truststorePass="changeit" scheme="https" secure="true"
    clientAuth="true" sslProtocol="SSL"
    maxThreads="150" port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"/>

配置web.xml

  <servlet>
    <servlet-name>httpsweb</servlet-name>
    <servlet-class>com.wm.core.HttpswebServlet</servlet-class>
  </servlet>
  <servlet-mapping>
        <servlet-name>httpsweb</servlet-name>
        <url-pattern>/ssl</url-pattern>
  </servlet-mapping>  
  <security-constraint>
      <web-resource-collection>
          <web-resource-name>SSL</web-resource-name>
          <url-pattern>/*</url-pattern>
      </web-resource-collection>
      <user-data-constraint>
         <transport-guarantee>CONFIDENTIAL</transport-guarantee>
            <!-- CONFIDENTIAL: 要保证服务器和客户端之间传输的数据不能够被修改,且不能被第三方查看到 -->
            <!-- INTEGRAL: 要保证服务器和client之间传输的数据不能够被修改 -->
            <!-- NONE: 指示容器必须能够在任一的连接上提供数据。(即用HTTP或HTTPS,由客户端来决定) -->
      </user-data-constraint>
  </security-constraint>

服务端代码

 

java web https 单向认证 双向认证

客户端代码  http://hc.apache.org/downloads.cgi httpClient包下载地址

    private static final String KEY_STORE_TYPE_JKS = "jks";
    private static final String SCHEME_HTTPS = "https";
    private static final int HTTPS_PORT = 8443;
    private static final String HTTPS_URL = "https://localhost:8443/httpsWeb/ssl";
    private static final String KEYSTORE_PATH = "E:\\keystore\\client.keystore";
    private static final String KEY_STORE_PASSWORD = "123456";
    private static final String CHARSET = "utf-8";

    public static String doPost(String url, Map<String, String> map) throws Exception {
        HttpClient httpClient = new DefaultHttpClient();
        String result = "";
        try {
            InputStream ksin = null;
            InputStream tsin = null;
            KeyStore keyStore = KeyStore.getInstance(KEY_STORE_TYPE_JKS);
            KeyStore trustKeyStore = KeyStore.getInstance(KEY_STORE_TYPE_JKS);
            try {
                ksin = new FileInputStream(KEYSTORE_PATH);
                tsin = new FileInputStream(KEYSTORE_PATH);
                keyStore.load(ksin, KEY_STORE_PASSWORD.toCharArray());
                trustKeyStore.load(tsin, KEY_STORE_PASSWORD.toCharArray());
            } catch (Exception e) {
                throw e;
            } finally {
                if (ksin != null) {
                    ksin.close();
                }
                if (tsin != null) {
                    tsin.close();
                }
            }
            SSLSocketFactory socketFactory = new SSLSocketFactory(keyStore, KEY_STORE_PASSWORD, trustKeyStore);
            Scheme sch = new Scheme(SCHEME_HTTPS, HTTPS_PORT, socketFactory);
            httpClient.getConnectionManager().getSchemeRegistry().register(sch);
            HttpPost httpPost = new HttpPost(url);
            // 设置参数
            if (map != null) {
                List<NameValuePair> list = new ArrayList<NameValuePair>();
                Iterator<Entry<String, String>> iterator = map.entrySet().iterator();
                while (iterator.hasNext()) {
                    Map.Entry<String, String> elem = (Map.Entry<String, String>) iterator.next();
                    list.add(new BasicNameValuePair(elem.getKey(), elem.getValue()));
                }
                httpPost.setEntity(new UrlEncodedFormEntity(list, CHARSET));
            }
            HttpResponse response = httpClient.execute(httpPost);
            if (response != null) {
                HttpEntity resEntity = response.getEntity();
                if (resEntity != null) {
                    result = EntityUtils.toString(resEntity, CHARSET);
                }
            }
        } finally {
            httpClient.getConnectionManager().shutdown();
        }
        return result;
    }

    public static void main(String[] args) throws Exception {
        Map<String,String> map = new HashMap<String,String>();
        map.put("xml", "https request data");
        String result = httpClientUtil.doPost(HTTPS_URL, map);
        System.out.println(result);
    }

另一种客户端实现   纯jdk实现 但是取值的方式这里有限制

private static final String KEYSTORE_PATH = "E:\\keystore\\client.keystore";
    
    private static final String KEYSTORE_PASS = "123456";
    
    private static final String LOCAL_URL = "https://localhost:8443/httpsWeb/ssl";
    
    private static SSLContext sslContext;
    
    public static void initSSL() throws Exception{
        
        synchronized(SkipAuthReq.class) {
            if(sslContext != null) {
                return;
            }
            KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
            FileInputStream ks = new FileInputStream(new File(KEYSTORE_PATH));
            KeyStore mk = KeyStore.getInstance("jks");
            mk.load(ks, KEYSTORE_PASS.toCharArray());
            kmf.init(mk, KEYSTORE_PASS.toCharArray());
            KeyManager[] keymanager =  kmf.getKeyManagers();
            
            TrustManagerFactory trustmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            FileInputStream trustks = new FileInputStream(new File(KEYSTORE_PATH));
            KeyStore trustmk = KeyStore.getInstance("jks");
            trustmk.load(trustks, KEYSTORE_PASS.toCharArray());
            trustmf.init(trustmk);
            sslContext = SSLContext.getInstance("TLS");
            //sslContext.init(keymanager, trustmf.getTrustManagers(), null);  //验证服务器验证
            sslContext.init(keymanager, new TrustManager[] {new defaultTrustManage()}, null);  //跳过验证
            HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
                
                @Override
                public boolean verify(String arg0, SSLSession arg1) {
                    return true;
                }
            });
            
        }
    }
    
    private static class defaultTrustManage implements X509TrustManager{
        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }

        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }
        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }    
    }
    public static String doPost(String param) throws Exception {
        String rsp = "";
        BufferedReader read = null;
        OutputStream out = null;
        byte[] content = param.getBytes();
        try {
            URL url = new URL(LOCAL_URL);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            if(conn instanceof HttpsURLConnection) {
                initSSL();
                ((HttpsURLConnection) conn).setSSLSocketFactory(sslContext.getSocketFactory());
            }
            conn.setRequestProperty("accept", "*/*");  //请求报文可通过一个“Accept”报文头属性告诉服务端 客户端接受什么类型的响应
            conn.setRequestProperty("connection", "Keep-Alive");//表示是否需要持久连接。(HTTP 1.1默认进行持久连接)Keep-Alive为持久连接
            conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 7.0; WOW64)");//(用户代理),简称 UA,它是一个特殊字符串头,使得服务器能够识别客户端使用的操作系统及版本、CPU 类型、浏览器及版本、浏览器渲染引擎、浏览器语言、浏览器插件等。
            conn.setRequestProperty("Content-type", "application/json;charset=utf-8"); //返回的响应MIME类型与编码–告诉浏览器它发送的数据属于什么文件类型
            conn.setRequestMethod("POST");
            conn.setDoOutput(true);
            conn.setDoInput(true);
            out = conn.getOutputStream();
            out.write(content);
            out.flush();
            if(200 == conn.getResponseCode()) {
                read = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
            }else {
                read = new BufferedReader(new InputStreamReader(conn.getErrorStream()));
            }
            String line = "";
            while ((line = read.readLine()) != null) {
                rsp += line;
            }
        }finally{
            if(read != null) {
                read.close();
            }
            if(out != null) {
                out.close();
            }
        }
        return rsp;
    }

这里给了几种实现,可以根据具体需求跟场景搭配组合使用。