https证书生成、服务器配置、Android端配置
单项认证--服务器配置
生成服务器证书
命令:C:\Users\bwkt>keytool -genkey -alias server -keyalg RSA -keystore server.jks -validity 3650
-genkey:在用户主目录中创建一个默认文件".keystore"
-alias:别名,独一无二,通常不区分大小写
-keyalg: 指定密钥的算法 (如 RSA DSA(如果不指定默认采用DSA))
-keystore:指定密钥库的名称(产生的各类信息将不在.keystore文件中),可指定路径,例如:D:\keys\server.jks。
-validity:证书的有效期,单位:天。
A、输入keystore密码:此处需要输入大于6个字符的字符串。
B、“您的名字与姓氏是什么?”这是必填项。
C、你的组织单位名称是什么?”、“您的组织名称是什么?”、“您所在城市或区域名称是什么?”、“您所在的省/市/自治区名称是什么?”、“该单位的两字母国家代码是什么?”可以按照需要填写也可以不填写直接回车,在系统询问“正确吗?”时,对照输入信息,如果符合要求则使用键盘输入字母“y”,否则输入“n”重新填写上面的信息。
D、输入<bwkt>的密钥口令,这项较为重要,会在tomcat配置文件中使用,建议输入与keystore的密码一致,设置其它密码也可以,完成上述输入后,直接回车则在你在第二步中定义的位置找到生成的文件。
接下来利用server.jks来签发证书:
C:\Users\bwkt>keytool -export -alias server -file server.cer -keystore server.jks
配置Tomcat
找到tomcat/conf/sever.xml文件,并以文本形式打开。
找到端口为8443的标签,修改为:
<Connector SSLEnabled="true" acceptCount="100" clientAuth="true" disableUploadTimeout="true" enableLookups="true" keystoreFile="C:\Users\bwkt\server.jks" keystorePass="123456" maxSpareThreads="75" maxThreads="200" minSpareThreads="5" port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol" scheme="https" secure="true" sslProtocol="TLS" />
注:
keystoreFile:jks文件存放路径
keystorePass:生成证书时候的密码
测试
启动Tomcat服务器,在浏览器中输入 https://localhost:8443/ ,浏览器提示下图即为成功。
注:上面生成的server.cer是为Android客户端准备的。
单项认证--Android端
准备证书
在Android客户端启动的时候在Application中设置证书信息
public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); try { // 证书放在assets文件夹中 OkHttpClientManager.getInstance().setCertificates(new InputStream[]{getAssets().open("server.cer")}); } catch (IOException e) { e.printStackTrace(); } OkHttpClientManager.getInstance().getOkHttpClient().setConnectTimeout(100000, TimeUnit.MILLISECONDS); } }
也可以通过命令获取到字符串:
C:\Users\bwkt>keytool -printcert -rfc -file server.cer
代码:
public class MyApplication extends Application { private static final String SERVER_CER = "-----BEGIN CERTIFICATE-----\n" + "MIIDSTCCAjGgAwIBAgIEd05HCTANBgkqhkiG9w0BAQsFADBVMQswCQYDVQQGEwJjbjELMAkGA1UE\n" + "CBMCYmoxCzAJBgNVBAcTAmJqMQwwCgYDVQQKEwNzdW4xDTALBgNVBAsTBGJ3a3QxDzANBgNVBAMT\n" + "BnNlcnZlcjAeFw0xNTExMjAwMjAzMTRaFw0yNTExMTcwMjAzMTRaMFUxCzAJBgNVBAYTAmNuMQsw\n" + "CQYDVQQIEwJiajELMAkGA1UEBxMCYmoxDDAKBgNVBAoTA3N1bjENMAsGA1UECxMEYndrdDEPMA0G\n" + "A1UEAxMGc2VydmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAi98I4lBsevRZIPpX\n" + "4y0ocJrGHTNp4CBeNP+EXDs0ofqrdphd4U+rAs1pcYaC0yrXG8MdbS2E1ar4g3YOpuKNMtoac+gL\n" + "C+9+Ri+0Ge6o5Rwcly2JUUEJDyMVMxZTLJdGoaG2lkFRLbkHJDNil2hpnxMkU9f8BzTblTUOJvwX\n" + "MK0hgTDAwOyBcQRDTyKYT41Izn2ofk4UoGQdc2a7BmCZ9aRGkL2ZBtiuqDbebKV7+xZuyGIahCj0\n" + "K3odF61fQAbgDmaHpDiIvkWrRFuhTX6VuIfvrg1ek/fzSYJ2s6w40jDfGpGeaGbMjxcJ4DokwpRI\n" + "R//+7rbuPwH/BZ0KqxbifwIDAQABoyEwHzAdBgNVHQ4EFgQUorCf7tMptaAH6F53FbGO981Zhjow\n" + "DQYJKoZIhvcNAQELBQADggEBAAz+qTR3b7ezVdz3eL+n1qqrc+5GCp7aFhFTrT7GNsKP1A6MmYG9\n" + "j8H2IB58+AEnDGSrOKPFqHUlQSJzW05WDpCJorw8VXC4Mbsb8bP5jXmiJEakyic8dLoiZn3f4uNA\n" + "wWI4g8t/ZGAobG/X/d5Sd3Cyyi60BJoLcFIshjd3Z1YVl5V1c2Q8+k1qzfDno68Msu2IJ5LVbD44\n" + "Wmt03rlDP2bfeAvpX0PMyPS4RbILtvixLOUB5KM9LPOO7kliS6ZajVR/qDKo3H4fX2OCCYke4hsW\n" + "DR+iUT2S8FQEpBLQOyX6ULdELg0eMLhfmcUk2vvOaItF9LzeMim3tPPzs2XyWmA=\n" + "-----END CERTIFICATE-----"; @Override public void onCreate() { super.onCreate(); try { // 证书放在assets文件夹中 // OkHttpClientManager.getInstance().setCertificates(new InputStream[]{getAssets().open("server.cer")}); // 根据证书生成的字符串设置证书信息 OkHttpClientManager.getInstance().setCertificates(new InputStream[]{new Buffer().writeUtf8(SERVER_CER).inputStream()}); } catch (IOException e) { e.printStackTrace(); } OkHttpClientManager.getInstance().getOkHttpClient().setConnectTimeout(100000, TimeUnit.MILLISECONDS); } }
准备证书的核心代码如下:
1.设置OkHttpClient主机认证。
mOkHttpClient.setHostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { return true; } });
2.设置证书信息
/** * 设置证书信息 * @param certificates * @param bksFile * @param password */ public void setCertificates(InputStream[] certificates, InputStream bksFile, String password) { try { TrustManager[] trustManagers = prepareTrustManager(certificates); KeyManager[] keyManagers = prepareKeyManager(bksFile, password); SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(keyManagers, new TrustManager[] { new MyTrustManager(chooseTrustManager(trustManagers)) }, new SecureRandom()); mOkHttpClient.setSslSocketFactory(sslContext.getSocketFactory()); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (KeyManagementException e) { e.printStackTrace(); } catch (KeyStoreException e) { e.printStackTrace(); } } /** * 准备所信任的服务器证书 * @param certificates * @return */ private TrustManager[] prepareTrustManager(InputStream... certificates) { if (certificates == null || certificates.length <= 0){ return null; } try { CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); keyStore.load(null); int index = 0; for (InputStream certificate : certificates) { String certificateAlias = Integer.toString(index++); keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate)); try { if (certificate != null){ certificate.close(); } } catch (IOException e) { } } TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(keyStore); TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); return trustManagers; } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (CertificateException e) { e.printStackTrace(); } catch (KeyStoreException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } return null; } /** * 准备服务器信任的客户端的证书 * @param bksFile * @param password * @return */ private KeyManager[] prepareKeyManager(InputStream bksFile, String password) { try { if (bksFile == null || password == null) { return null; } // Android默认的是BKS格式的证书 KeyStore clientKeyStore = KeyStore.getInstance("BKS"); clientKeyStore.load(bksFile, password.toCharArray()); KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); keyManagerFactory.init(clientKeyStore, password.toCharArray()); return keyManagerFactory.getKeyManagers(); } catch (KeyStoreException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (UnrecoverableKeyException e) { e.printStackTrace(); } catch (CertificateException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } return null; }
测试
String url = "https://192.168.0.163:8443/"; new OkHttpRequest.Builder().url(url).get( new MyResultCallback<String>() { @Override public void onError(Request request, Exception e) { Log.e(TAG, "onError" + e.getMessage()); textView.setText(e.getMessage()); }
注:只是获取的页面Html字符串。
双向认证--服务器配置
生成客户端证书
按照生成证书的方式,再生成一对这样的文件,我们命名为:client.jks,client.cer。
配置服务器
将端口为8443的标签,修改为:
<Connector SSLEnabled="true" acceptCount="100" clientAuth="true" disableUploadTimeout="true" enableLookups="true" keystoreFile="C:\Users\bwkt\server.jks" keystorePass="123456" truststoreFile="C:\Users\bwkt\client.jks" truststorePass="123456" maxSpareThreads="75" maxThreads="200" minSpareThreads="5" port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol" scheme="https" secure="true" sslProtocol="TLS" />
注:
truststoreFile:信任证书的文件路径
truststorePass:信任证书的秘密
测试
启动Tomcat服务器,在浏览器中输入 https://localhost:8443/ ,浏览器提示下图即为成功。
双向认证--Android端
生成Android所需的bks格式的证书
由于Java平台默认识别的是jks格式证书,Android默认识别的是bks格式证书,所有要将jks格式的证书转换成bks格式的证书。
步骤如下:
解压后,里面包含portecle.jar文件,双击portecle.jar即可打开GUI界面。
导入证书
点击
打开之前生成的jks文件。
输入密码:
成功打开。
转换证书
点击Tools-->Change keystore Type-->BKS。
输入密码,成功提示如下:
保存证书
Ctrl+s
准备证书
在Android客户端启动的时候在Application中设置证书信息
public class MyApplication extends Application { private static final String SERVER_CER = "-----BEGIN CERTIFICATE-----\n" + "MIIDSTCCAjGgAwIBAgIEd05HCTANBgkqhkiG9w0BAQsFADBVMQswCQYDVQQGEwJjbjELMAkGA1UE\n" + "CBMCYmoxCzAJBgNVBAcTAmJqMQwwCgYDVQQKEwNzdW4xDTALBgNVBAsTBGJ3a3QxDzANBgNVBAMT\n" + "BnNlcnZlcjAeFw0xNTExMjAwMjAzMTRaFw0yNTExMTcwMjAzMTRaMFUxCzAJBgNVBAYTAmNuMQsw\n" + "CQYDVQQIEwJiajELMAkGA1UEBxMCYmoxDDAKBgNVBAoTA3N1bjENMAsGA1UECxMEYndrdDEPMA0G\n" + "A1UEAxMGc2VydmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAi98I4lBsevRZIPpX\n" + "4y0ocJrGHTNp4CBeNP+EXDs0ofqrdphd4U+rAs1pcYaC0yrXG8MdbS2E1ar4g3YOpuKNMtoac+gL\n" + "C+9+Ri+0Ge6o5Rwcly2JUUEJDyMVMxZTLJdGoaG2lkFRLbkHJDNil2hpnxMkU9f8BzTblTUOJvwX\n" + "MK0hgTDAwOyBcQRDTyKYT41Izn2ofk4UoGQdc2a7BmCZ9aRGkL2ZBtiuqDbebKV7+xZuyGIahCj0\n" + "K3odF61fQAbgDmaHpDiIvkWrRFuhTX6VuIfvrg1ek/fzSYJ2s6w40jDfGpGeaGbMjxcJ4DokwpRI\n" + "R//+7rbuPwH/BZ0KqxbifwIDAQABoyEwHzAdBgNVHQ4EFgQUorCf7tMptaAH6F53FbGO981Zhjow\n" + "DQYJKoZIhvcNAQELBQADggEBAAz+qTR3b7ezVdz3eL+n1qqrc+5GCp7aFhFTrT7GNsKP1A6MmYG9\n" + "j8H2IB58+AEnDGSrOKPFqHUlQSJzW05WDpCJorw8VXC4Mbsb8bP5jXmiJEakyic8dLoiZn3f4uNA\n" + "wWI4g8t/ZGAobG/X/d5Sd3Cyyi60BJoLcFIshjd3Z1YVl5V1c2Q8+k1qzfDno68Msu2IJ5LVbD44\n" + "Wmt03rlDP2bfeAvpX0PMyPS4RbILtvixLOUB5KM9LPOO7kliS6ZajVR/qDKo3H4fX2OCCYke4hsW\n" + "DR+iUT2S8FQEpBLQOyX6ULdELg0eMLhfmcUk2vvOaItF9LzeMim3tPPzs2XyWmA=\n" + "-----END CERTIFICATE-----"; @Override public void onCreate() { super.onCreate(); try { // 证书放在assets文件夹中 // OkHttpClientManager.getInstance().setCertificates(new InputStream[]{getAssets().open("server.cer")}); // 根据证书生成的字符串设置证书信息 // OkHttpClientManager.getInstance().setCertificates(new InputStream[]{new Buffer().writeUtf8(SERVER_CER).inputStream()}); // 双向认证,设置证书信息 OkHttpClientManager.getInstance().setCertificates(new InputStream[]{getAssets().open("server.cer")}, getAssets().open("client.bks"), "123456"); } catch (IOException e) { e.printStackTrace(); } OkHttpClientManager.getInstance().getOkHttpClient().setConnectTimeout(100000, TimeUnit.MILLISECONDS); } }
测试
String url = "https://192.168.0.163:8443/"; new OkHttpRequest.Builder().url(url).get( new MyResultCallback<String>() { @Override public void onError(Request request, Exception e) { Log.e(TAG, "onError" + e.getMessage()); textView.setText(e.getMessage()); } @Override public void onResponse(String response) { textView.setText(response); } });
注:只是获取的页面Html字符串。