社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
keytool
-genkey
-alias ycb_server 别名
-keypass 123456 密码
-keyalg RSA 加密方式
-keysize 1024 加密密钥长度(可以不加这一行,默认2048)
-validity 3650 证书有效期(单位:天)
-keystore E:/yuncaibianhttps/ycb_server.jks 证书存放的具体路径
-storepass 123456 查看jks信息的密码
keytool -printcert -rfc -file E:/yuncaibianhttps/ycb_server.cer
-----BEGIN CERTIFICATE-----
MIICUjCCAbugAwIBAgIEBt35sTANBgkqhkiG9w0BAQsFADBcMQswCQYDVQQGEwJjbjEQMA4GA1UE
CBMHYmVpamluZzEQMA4GA1UEBxMHYmVpamluZzEMMAoGA1UEChMDY25yMQwwCgYDVQQLEwNjbnIx
DTALBgNVBAMTBGdvbmcwHhcNMTgwMzA2MDcwMTQzWhcNMjgwMzAzMDcwMTQzWjBcMQswCQYDVQQG
EwJjbjEQMA4GA1UECBMHYmVpamluZzEQMA4GA1UEBxMHYmVpamluZzEMMAoGA1UEChMDY25yMQww
CgYDVQQLEwNjbnIxDTALBgNVBAMTBGdvbmcwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAI31
yMvNkmdFLyDr4uI35lMZwZf9lqt/q0MHpIaMP7j4evtVMqs9SnAJwEprOK35+z5xIgqpD+ioy5Xf
e1g2crM0r22qU229WOT4OfSk12bcobWw9Dr7Hqy5hjdXDtJ7hwg+c4mYE4WWZOH6REkR58c0LCoe
4Y1g0iZOpXwiTjF7AgMBAAGjITAfMB0GA1UdDgQWBBTDpVe72BYQOuvexvW+WpDt5XrpPDANBgkq
hkiG9w0BAQsFAAOBgQB0KhMRb7LhFB/8jWRa9owvSF9rEPmx8BzcsrMDQQEU+XV1dBhrr+YgADcl
wgEHZjCHair5rTId888bdSN+OXYIMat5jRyH2MW+5ybRQYCCijQ6jjyK1TV+/hDNZSYtEk9RW9vC
I7WMhdclqcbnzT6COg1cJHgrMhLlMz8bsoDZWQ==
-----END CERTIFICATE-----
private String PUB_KEY = "-----BEGIN CERTIFICATE-----n" +
"MIICUjCCAbugAwIBAgIEBt35sTANBgkqhkiG9w0BAQsFADBcMQswCQYDVQQGEwJjbjEQMA4GA1UEn" +
"CBMHYmVpamluZzEQMA4GA1UEBxMHYmVpamluZzEMMAoGA1UEChMDY25yMQwwCgYDVQQLEwNjbnIxn" +
"DTALBgNVBAMTBGdvbmcwHhcNMTgwMzA2MDcwMTQzWhcNMjgwMzAzMDcwMTQzWjBcMQswCQYDVQQGn" +
"EwJjbjEQMA4GA1UECBMHYmVpamluZzEQMA4GA1UEBxMHYmVpamluZzEMMAoGA1UEChMDY25yMQwwn" +
"CgYDVQQLEwNjbnIxDTALBgNVBAMTBGdvbmcwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAI31n" +
"yMvNkmdFLyDr4uI35lMZwZf9lqt/q0MHpIaMP7j4evtVMqs9SnAJwEprOK35+z5xIgqpD+ioy5Xfn" +
"e1g2crM0r22qU229WOT4OfSk12bcobWw9Dr7Hqy5hjdXDtJ7hwg+c4mYE4WWZOH6REkR58c0LCoen" +
"4Y1g0iZOpXwiTjF7AgMBAAGjITAfMB0GA1UdDgQWBBTDpVe72BYQOuvexvW+WpDt5XrpPDANBgkqn" +
"hkiG9w0BAQsFAAOBgQB0KhMRb7LhFB/8jWRa9owvSF9rEPmx8BzcsrMDQQEU+XV1dBhrr+YgADcln" +
"wgEHZjCHair5rTId888bdSN+OXYIMat5jRyH2MW+5ybRQYCCijQ6jjyK1TV+/hDNZSYtEk9RW9vCn" +
"I7WMhdclqcbnzT6COg1cJHgrMhLlMz8bsoDZWQ==n" +
"-----END CERTIFICATE-----";
private final DefaultHttpClient httpClient;
//构造一个支持https的HttpClient
public RdHttpClient() {
BasicHttpParams httpParams = new BasicHttpParams();
...省略代码:http基本请求配置...
SchemeRegistry schemeRegistry = new SchemeRegistry();
...省略代码:支持http...
//配置自己的SSLSocketFactory
SSLSocketFactory sf = null;
try {
KeyStore trustStore = KeyStore.getInstance(KeyStore
.getDefaultType());
trustStore.load(null, null);
sf = new MySSLSocketFactory(trustStore);//自己的SSLSocketFactory
sf.setHostnameVerifier(MySSLSocketFactory.STRICT_HOSTNAME_VERIFIER);//关键点:严格校验服务器域名
} catch (Exception e) {
e.printStackTrace();
}
//支持https,端口号为443
schemeRegistry.register(new Scheme("https", sf, 443));
ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager(
httpParams, schemeRegistry);
httpClient = new DefaultHttpClient(cm, httpParams);
...省略代码...
}
//重写的SSLSocketFactory
public class MySSLSocketFactory extends SSLSocketFactory {
SSLContext sslContext = SSLContext.getInstance("TLS");
public MySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
super(truststore);
TrustManager tm = new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
//关键,打印服务端返回的证书中公钥字符串PUB_KEY
RSAPublicKey pubkey = (RSAPublicKey) chain[0].getPublicKey();
String encoded = new BigInteger(1, pubkey.getEncoded()).toString(16);
Log.e("PUB_KEY",encoded);
}
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
sslContext.init(null, new TrustManager[] { tm }, null);
}
@Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
}
@Override
public Socket createSocket() throws IOException {
return sslContext.getSocketFactory().createSocket();
}
}
03-07 11:24:28.672 12103-12256/com.cnr.broadcastCollect E/PUB_KEY: 30820122300d06092a864886f70d01010105000382010f003082010a0282010100c9292d7
private final String PUB_KEY = "30820122300d06092a864886f70d01010105000382010f003082010a0282010100c9......"
public MySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
super(truststore);
TrustManager tm = new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
//1.对服务端返回的证书做判空处理
if (chain == null) {
throw new IllegalArgumentException("checkServerTrusted: X509Certificate array is null");
}
if (!(chain.length > 0)) {
throw new IllegalArgumentException("checkServerTrusted: X509Certificate is empty");
}
//2.校验加密算法种类(通常所说的 ECDHE 密钥交换默认都是指 ECDHE_RSA,使用 ECDHE 生成 DH 算法所需的公私钥,然后使用 RSA 算法进行签名,最后再计算得出对称密钥。)
if (!(null != authType && authType.equalsIgnoreCase("ECDHE_RSA"))) {
throw new CertificateException("checkServerTrusted: AuthType is not ECDHE_RSA");
}
for (X509Certificate cert:chain){
try {
//3.检查证书是否在有效期内
cert.checkValidity();
} catch (Exception e) {
e.printStackTrace();
}
}
//4.校验公钥字符串是否相同
RSAPublicKey pubkey = (RSAPublicKey) chain[0].getPublicKey();
String encoded = new BigInteger(1, pubkey.getEncoded()).toString(16);
final boolean expected = PUB_KEY.equalsIgnoreCase(encoded);
if (!expected) {
throw new CertificateException("checkServerTrusted: Expected public key: " + PUB_KEY + ", got public key:" + encoded);
}
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
sslContext.init(null, new TrustManager[] { tm }, null);
}
public static final X509HostnameVerifier ALLOW_ALL_HOSTNAME_VERIFIER
= new AllowAllHostnameVerifier();
public static final X509HostnameVerifier BROWSER_COMPATIBLE_HOSTNAME_VERIFIER
= new BrowserCompatHostnameVerifier();
public static final X509HostnameVerifier STRICT_HOSTNAME_VERIFIER
= new StrictHostnameVerifier();
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
Log.e("checkServerTrusted","checkServerTrusted");
...省略代码...
}
03-07 14:27:57.446 11056-11705/com.cnr.broadcastCollect E/HttpClient?getInstance: HttpClient getInstance
03-07 14:27:57.576 11056-11705/com.cnr.broadcastCollect E/checkServerTrusted: checkServerTrusted
03-07 14:27:57.714 11056-11705/com.cnr.broadcastCollect E/tag: js {"result":{"sex":"","channelName":"中国之声","channelId":"","dutyTitle":"","roleName":"节目编辑","userRole":"","departmentName":"早间节目部","departmentType":"","id":"","authority":"11111111111111111111111111111","token":"","roleType":"3","userName":"","departmentId":"","userPhonePath":"","loginName":""},"error":{"message":"","code":"0"}}
03-07 14:28:04.778 11056-11705/com.cnr.broadcastCollect E/HttpClient?getInstance: HttpClient getInstance
03-07 14:28:04.994 11056-11705/com.cnr.broadcastCollect E/tag: js {"totalNum":1499,"result":[{"taskId":"",ck":"","}]
03-07 14:28:08.377 11056-11705/com.cnr.broadcastCollect E/HttpClient?getInstance: HttpClient getInstance
03-07 14:28:09.717 11056-11705/com.cnr.broadcastCollect E/tag: js {"totalNum":453,"result":[{"taskId":"",,"createDate":"2018-03-07 10:10:29"}]
/**
下面注释再说名使用这种HostnameVerifier,就只在第一次链接的时候检查服务端的合法性,即造成上面的现象
* <p/>
* The hostname must match either the first CN, or any of the subject-alts.
* A wildcard can occur in the CN, and in any of the subject-alts. The
* one divergence from IE6 is how we only check the first CN. IE6 allows
* a match against any of the CNs present. We decided to follow in
* Sun Java 1.4's footsteps and only check the first CN. (If you need
* to check all the CN's, feel free to write your own implementation!).
* <p/>
* 下面的注释说明严格控制域名匹配,和BrowserCompatHostnameVerifier形成对比,后者宽泛:允许子域名匹配也可以认为域名相同,确认合法
* A wildcard such as "*.foo.com" matches only subdomains in the same
* level, for example "a.foo.com". It does not match deeper subdomains
* such as "a.b.foo.com".
*/
@Deprecated
public class StrictHostnameVerifier extends AbstractVerifier {
public final void verify(
final String host,
final String[] cns,
final String[] subjectAlts) throws SSLException {
verify(host, cns, subjectAlts, true);
}
@Override
public final String toString() {
return "STRICT";
}
}
public abstract class AbstractVerifier implements X509HostnameVerifier {
public final void verify(final String host, final String[] cns,
final String[] subjectAlts,
final boolean strictWithSubDomains)
throws SSLException {
//下面注释解释了为何只在第一次链接时候校验,并且一些浏览器也是这么做的
// Build the list of names we're going to check. Our DEFAULT and
// STRICT implementations of the HostnameVerifier only use the
// first CN provided. All other CNs are ignored.
// (Firefox, wget, curl, Sun Java 1.4, 5, 6 all work this way).
//取出服务器返回的证书中的域名
LinkedList<String> names = new LinkedList<String>();
if(cns != null && cns.length > 0 && cns[0] != null) {
names.add(cns[0]);
}
if(subjectAlts != null) {
for (String subjectAlt : subjectAlts) {
if (subjectAlt != null) {
names.add(subjectAlt);
}
}
}
//返回域名不能为空,否则抛异常
if(names.isEmpty()) {
String msg = "Certificate for <" + host + "> doesn't contain CN or DNS subjectAlt";
throw new SSLException(msg);
}
// StringBuffer for building the error message.
StringBuffer buf = new StringBuffer();
// We're can be case-insensitive when comparing the host we used to
// establish the socket to the hostname in the certificate.
//本地请求服务域名整理为小写
String hostName = host.trim().toLowerCase(Locale.ENGLISH);
boolean match = false;
//下面是匹配过程
for(Iterator<String> it = names.iterator(); it.hasNext();) {
// Don't trim the CN, though!
String cn = it.next();
cn = cn.toLowerCase(Locale.ENGLISH);
// Store CN in StringBuffer in case we need to report an error.
buf.append(" <");
buf.append(cn);
buf.append('>');
if(it.hasNext()) {
buf.append(" OR");
}
// The CN better have at least two dots if it wants wildcard
// action. It also can't be [*.co.uk] or [*.co.jp] or
// [*.org.uk], etc...
boolean doWildcard = cn.startsWith("*.") &&
cn.indexOf('.', 2) != -1 &&
acceptableCountryWildcard(cn) &&
!isIPv4Address(host);
if(doWildcard) {
match = hostName.endsWith(cn.substring(1));
//strictWithSubDomains决定对子域名的检查严格程度
if(match && strictWithSubDomains) {
// If we're in strict mode, then [*.foo.com] is not
// allowed to match [a.b.foo.com]
match = countDots(hostName) == countDots(cn);
}
} else {
match = hostName.equals(cn);
}
if(match) {
break;
}
}
if(!match) {
throw new SSLException("hostname in certificate didn't match: <" + host + "> !=" + buf);
}
}
}
public class MySSLSocketFactory extends SSLSocketFactory {
SSLContext sslContext = SSLContext.getInstance("TLS");
public MySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
super(truststore);
TrustManager tm = new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
Log.e("checkServerTrusted","checkServerTrusted");
//对服务端返回的证书做判空处理
if (chain == null) {
throw new IllegalArgumentException("checkServerTrusted: X509Certificate array is null");
}
if (!(chain.length > 0)) {
throw new IllegalArgumentException("checkServerTrusted: X509Certificate is empty");
}
//校验加密算法种类(通常所说的 ECDHE 密钥交换默认都是指 ECDHE_RSA,使用 ECDHE 生成 DH 算法所需的公私钥,然后使用 RSA 算法进行签名,最后再计算得出对称密钥。)
if (!(null != authType && authType.equalsIgnoreCase("ECDHE_RSA"))) {
throw new CertificateException("checkServerTrusted: AuthType is not ECDHE_RSA");
}
for (X509Certificate cert:chain){
try {
//检查证书是否在有效期内
cert.checkValidity();
//下面是和上面4.1.3中唯一的差异,增加了这一句,并将下面原有的验证公钥字符串相同的部分去掉。
//校验服务端返回的证书和本地预留的证书的一致性
cert.verify(getCert().getPublicKey());
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
sslContext.init(null, new TrustManager[] { tm }, null);
}
@Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
}
@Override
public Socket createSocket() throws IOException {
return sslContext.getSocketFactory().createSocket();
}
//下面两种获取证书的方法选一种即可
//将公钥文件放在工程的assets文件夹中的时候,获取证书
private X509Certificate getCert(){
X509Certificate serverCert = null;
try {
InputStream certificate = App.getInstance().getResources().getAssets().open("ycb_server.cer");
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
serverCert = (X509Certificate) certificateFactory.generateCertificate(certificate);
} catch (Exception e) {
e.printStackTrace();
}
return serverCert;
}
//将公钥信息以字符串的形式硬编码在类中的时候,获取证书
private X509Certificate getCertFromString(){
X509Certificate serverCert = null;
try {
InputStream certificate = new ByteArrayInputStream(PUB_KEY_CERT.getBytes());
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
serverCert = (X509Certificate) certificateFactory.generateCertificate(certificate);
} catch (Exception e) {
e.printStackTrace();
}
return serverCert;
}
//下面的公钥字符串也是改过的
private final String PUB_KEY_CERT = "-----BEGIN CERTIFICATE-----n" +
"MIICUjCCAbugAwIBAgIEBt35sTANBgkqhkiG9w0BAQsFADBcMQswCQYDVQQGEwJjbjEQMA4GA1UEn" +
"CBMHYmVpamluZzEQMA4GA1UEBxMHYmVpamluZzEMMAoGA1UEChMDY25yMQwwCgYDVQQLEwNjbnIxn" +
"HHHHBgNVBAMTBGdvbmcwHhcNMTgwMzA2MDcwMTQzWhcNMjgwMzAzMDcwMTQzWjBcMQswCQYDVQQGn" +
"HHHHbjEQMA4GA1UECBMHYmVpamluZzEQMA4GA1UEKKKKYmVpamluZzEMMAoGA1UEChMDY25yMQwwn" +
"HHHHVQQLEwNjbnIxDTALBgNVBAMTBGdvbmcwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAI31n" +
"yMvNkmdFLyDr4uI35lMZwZf9lqt/q0MHpIaMP7j4evtVMqs9SnAJwEprOK35+z5xIgqpD+ioy5Xfn" +
"e1g2crM0r22qU229WOT4OfSk12bcobWw9Dr7Hqy5KKKKDtJ7hwg+c4mYE4WWZOH6REkR58c0LCoen" +
"4Y1g0iZOpXwiTjF7AgMBAAGjITAfMB0GA1UdDgQWBBTDpVe72BYQOuvexvW+WpDt5XrpPDANBgkqn" +
"hkiG9w0BAQsFAAOBgQB0KhMRb7LhFB/8jWRa9owvSF9rEPmx8BzcsrMDQQEU+XV1dBhrr+YgADcln" +
"wgEHZjCHair5rTId888bdSN+OXYIMat5jRyH2MW+5ybRQYCCijQ6jjyK1TV+/hDNZSYtEk9RW9vCn" +
"I7WMhdclqcbnzT6COg1cJHgrMhLlMz8bsoDZWQ==n" +
"-----END CERTIFICATE-----";
}
sf.setHostnameVerifier(new AbstractVerifier() {
@Override
public void verify(String host, String[] cns, String[] subjectAlts) throws SSLException {
//校验过程
}
});
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!