---来自慕课网学习笔记
Https会校验相关证书
在网站中可通过F12-->Security查相关证书
- CA证书: github
github 证书是CA 签署的或者CA某个中间机构签署(CA证书价格昂贵)
此处为证书链
操作系统 会默认存着的根证书的信息-->通过信息可以校验出来
我们当前网站 证书是否是ca颁发的 是否安全
- 自签名证书
12306:
12306不是ca签署的 是自签名的 不被信任签发
自签名会给客户端提供一个证书下载地方(下载安装根证书)
证书导入电脑作为被信任列表
Https案例:
android项目结构
package com.example.https;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import com.example.https;
public class HttpActivity extends AppCompatActivity {
private TextView tv_result;
private Button btn_send;
private EditText et_input;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView(){
tv_result = findViewById(R.id.tv_result);
btn_send = findViewById(R.id.btn_send);
et_input = findViewById(R.id.et_input);
btn_send.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String s = et_input.getText().toString();
if(TextUtils.isEmpty(s)){
return;
}
HttpsUtils.doGet(MainActivity.this,s, new HttpUtils.HttpListener() {
@Override
public void onSuccess(String content) {
tv_result.setText(content);
}
@Override
public void onFail(Exception ex) {
ex.printStackTrace();
}
});
}
});
}
}
package com.example.https;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
public class HttpsUtils {
public interface HttpListener{
void onSuccess(String content);
void onFail(Exception ex);
}
private static Handler uiHandler = new Handler(Looper.getMainLooper());
public static void doGet(final Context context, final String urlStr, final HttpUtils.HttpListener listener){
new Thread(){
@Override
public void run() {
InputStream is = null;
try {
URL url = new URL(urlStr);
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
//初始化sslContext
SSLContext sslContext = SSLContext.getInstance("TLS");
X509Certificate serverCert = getCert(context);
TrustManager[] trustManagers = {new MyX509TrustManager(serverCert)};//被信任的对象
sslContext.init(null,trustManagers,new SecureRandom());
connection.setSSLSocketFactory(sslContext.getSocketFactory());
connection.setHostnameVerifier(new HostnameVerifier() {//对域名校验
@Override
public boolean verify(String hostname, SSLSession session) {
HostnameVerifier defaultHostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier();//拿到验证域名对象
return defaultHostnameVerifier.verify("kyfw.12306.cn",session);//return true不安全
}
});
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setRequestMethod("GET");
connection.setReadTimeout(5000);
connection.setConnectTimeout(5000);
connection.connect();
//经验 乱码问题 页面默认UTF-8格式,读取也是UTF-8格式, 但utf-8默认变长 有可能某个汉字从缓冲区2047开始
//解决方案 1. byte先读到byteArray里面去 一次性处理再转String
//2.使用char来读取
is = connection.getInputStream();
// byte[] buf = new byte[2048];
InputStreamReader isr = new InputStreamReader(is);
char[] buf = new char[2048];
int len = -1;
final StringBuilder content = new StringBuilder();
while ((len = isr.read(buf))!=-1){
content.append(new String(buf,0,len));
}
uiHandler.post(new Runnable() {
@Override
public void run() {
listener.onSuccess(content.toString());
}
});
} catch (Exception e) {
listener.onFail(e);
}finally {
if(is!=null){
try {
is.close();
} catch (IOException e) {
// ignore
}
}
}
}
}.start();
}
public static X509Certificate getCert(Context context){
try {
InputStream is = context.getAssets().open("srca.cer");
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
return (X509Certificate) certificateFactory.generateCertificate(is);
} catch (IOException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
}
return null;
}
}
package com.example.https;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SignatureException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.X509TrustManager;
public class MyX509TrustManager implements X509TrustManager{
private X509Certificate mServerCert;
public MyX509TrustManager(X509Certificate mServerCert) {
this.mServerCert = mServerCert;
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
//做单向校验可以只check服务器可信程度
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
//如果此处无任何代码 默认信任所有 server证书 但发挥不出安全性
//正确处理方式如下 证书校验
for(X509Certificate certificate:chain){
certificate.checkValidity();//检查证书合法性 过期
try {
certificate.verify(mServerCert.getPublicKey());//传入server端证书公钥public key ,此处为与我们预设证书进行校验
} catch (Exception e) {//如果证书正常不会抛Exception
throw new CertificateException(e);
}
}
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
访问12306显示效果:
证书验证另一种写法:
两种写法对比:
第一种证书由自己验证
第二种 通过证书生成 相关信息 keystore,由系统完成校验