一、Http请求(转载部分HTTP请求报文(请求行、请求头、请求体))
特点:
(1)HTTP是无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
(2)HTTP是媒体独立的:只要客户端和服务器知道如何处理的数据内容,任何类型的数据都可以通过HTTP发送。客户端以及服务器指定使用适合的MIME-type内容类型。
(3)HTTP是无状态:无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
HTTP请求报文
HTTP请求报文由4部分组成(请求行+请求头+请求体+空行):
请求行:
①是请求方法,GET和POST是最常见的HTTP方法,除此以外还包括DELETE、HEAD、OPTIONS、PUT、TRACE。
②为请求对应的URL地址,它和报文头的Host属性组成完整的请求URL。
③是协议名称及版本号。
请求头:
④是HTTP的报文头,报文头包含若干个属性,格式为“属性名:属性值”,服务端据此获取客户端的信息。
与缓存相关的规则信息,均包含在header中
请求体:
⑤是报文体即请求体,它将一个页面表单中的组件值通过param1=value1¶m2=value2的键值对形式编码成一个格式化串,它承载多个请求参数的数据。不但报文体可以传递请求参数,请求URL也可以通过类似于“/chapter15/user.html? param1=value1¶m2=value2”的方式传递请求参数。
⑥还有一个是跟在请求体后面的空行,其没有实际意义,主要是用来发送回车符和换行符,通知服务器以下不再有请求头。
服务端响应客户端:
响应行:
①报文协议及版本;
②状态码及状态描述;
响应头:
③响应报文头,也是由多个属性组成;
响应体:
④响应报文体,即我们真正要的信息数据
二、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
数据加密方式:
对称加密采用对称密码编码技术,也就是编码和解码采用相同描述字符,即加密和解密使用相同的密钥,实现这种加密技术的算法称对称加密算法。对称加密就是将信息和私钥通过某种算法混合在一起。对称加密使用简单,密钥较短,加密和解密过程较快,耗时短,常见的对称加密算法有DES,3DES,lDEA,AES,RC4等。
非对称加密与对称加密不同,其加密算法需要两个密钥:公开密钥(publickey)和私有密钥(private),两者是一对的。如果用公钥加密,只能用私钥才能解密。非对称加密保密性好,但加密和解密花费的时间较长,不适合对大文件加密而只适合对少量的数据加密。常见的非对称加密算法有RSA,ECC,DSA(数字签名)等。
Hash算法是一种单向算法,通过Hash算法可以对目标数据生成一段特定长度、唯一的hash值,但是不能通过这个hash值重新计算出原始的数据,因此也称之为摘要算法,经常被用在不需要数据还原的密码加密以及数据完整性校验上,常用的算法有MD2,MD4,MD5,SHA等。
参考:
https是如何工作的?
Retrofit中如何正确的使用https?——如何校验证书保证安全
详细解析 HTTP 与 HTTPS 的区别
非对称加密是目前在通信方面最安全的做法,SSL/TLS进行HTTP的加密传输流程:
1.[Client]向服务端初次发起请求
2.[Server]生成一对密钥:公钥和私钥,我们称之为“KeyPub-A”,“KeyPri-A”
3.[Server]服务端将公钥KeyPub-A发送到客户端 。
4.[Client]用服务端的公钥KeyPub-A生成一个之后要用到的对称密钥KeyPub-B。
5.[Client]使用公钥KeyPub-A对KeyPub-B进行加密,KeyPub-B是安全的,因为只有服务端有私钥KeyPri-A。
6.[Client]发送两个信息到服务端,用KeyPub-A加密后的KeyPub-B,用KeyPub-B加密的文本信息。
7.[Server]服务端使用私钥KeyPri-A对加密过的KeyPub-B进行解密,得到真正的KeyPub-B。
8.[Server]使用对称秘钥KeyPub-B解密收到的文本信息得到消息正文。之后双方沟通可以使用对称的公钥进行对称加密,而现在的对称加密便是安全的通信了。
总结:Https传输流程是先进行非对称加密,等服务端和客户端建立SSL连接后再进行负荷较轻的对称加密。
整理了一个SSL工具类,CA证书其实就是一对公钥和私钥:
/**
* https证书SSL配置
*/
public class MySSLSocketClient {
/**
* 忽略https证书验证
* <p>
* 使用:okhttpBuilder.sslSocketFactory(MySSLSocketClient.getSSLSocketFactory())
* (这种做法是不安全的,没有了验证容易被网络攻击)
*
* @return
* @throws Exception
*/
public static SSLSocketFactory getSSLSocketFactory() {
try {
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, getTrustManager(), new SecureRandom());
return sslContext.getSocketFactory();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
//获取TrustManager
private static TrustManager[] getTrustManager() {
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) {
//直接空白,不校验
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) {
//直接空白,不校验
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[]{};
}
}
};
return trustAllCerts;
}
/**
* 获取HostnameVerifier
* <p>
* 使用:okhttpBuilder.hostnameVerifier(MySSLSocketClient.getHostnameVerifier())放弃hostname校验,是不安全的
*
* @return
*/
public static HostnameVerifier getHostnameVerifier() {
HostnameVerifier hostnameVerifier = new HostnameVerifier() {
@Override
public boolean verify(String s, SSLSession sslSession) {
return true;
}
};
return hostnameVerifier;
}
/**
* https的证书公匙
*/
private static String sslPublicKey = "";
/**
* 添加ssl验证
* <p>安全套接层工厂(验证服务端购买的证书、非安卓系统内置的可信任证书)
* <p>使用购买的证书
* <p>链接::https://www.jianshu.com/p/7a40e874f6c2?utm_source=oschina-app
* <p>使用:okhttpBuilder.sslSocketFactory(getSSLSocketFactory())
* .hostnameVerifier(org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
*/
public static SSLSocketFactory getDefSSLSocketFactory() {
try {
//初始化SSLContext
SSLContext sslContext = SSLContext.getInstance("TLS");
final TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
//验证服务端证书的公钥
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
if (chain == null) {
throw new IllegalArgumentException("checkServerTrusted:x509Certificate array isnull");
}
if (!(chain.length > 0)) {
throw new IllegalArgumentException("checkServerTrusted: X509Certificate is empty");
}
if (!(!TextUtils.isEmpty(authType) && authType.toUpperCase().contains("RSA"))) {
throw new CertificateException("checkServerTrusted: AuthType is not RSA");
}
// Perform customary SSL/TLS checks
try {
TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
tmf.init((KeyStore) null);
for (TrustManager trustManager : tmf.getTrustManagers()) {
((X509TrustManager) trustManager).checkServerTrusted(chain, authType);
}
} catch (Exception e) {
throw new CertificateException(e);
}
// Hack ahead: BigInteger and toString(). We know a DER encoded Public Key begins
// with 0×30 (ASN.1 SEQUENCE and CONSTRUCTED), so there is no leading 0×00 to drop.
RSAPublicKey pubkey = (RSAPublicKey) chain[0].getPublicKey();
//* signum:1表示是正数;radix:16表示字节数组转16进制
String encoded = new BigInteger(1, pubkey.getEncoded()).toString(16);
final boolean expected = sslPublicKey.equalsIgnoreCase(encoded); //验证服务端证书的公钥
if (!expected) {
throw new CertificateException("checkServerTrusted: got error public key:" + encoded);
}
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[0];
}
}};
//将随机数等加密之后传给服务端。
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
return sslContext.getSocketFactory();
} //省略各种异常处理,请自行添加
catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
return null;
}
/**
* 添加ssl验证
* <p>安全套接层工厂(信任服务端证书的根证书)
* <p>载入证书
* <p>链接:https://www.jianshu.com/p/f2097616e65e、
* <p>https://blog.csdn.net/dd864140130/article/details/52625666
* <p>使用:okhttpBuilder.socketFactory(getSSLSocketFactory(context, certficates));
*
* @param context
* @param certificates 放在res/raw目录下的.bks格式证书
* @return
*/
protected static SSLSocketFactory getSSLSocketFactory(Context context, int[] certificates) {
if (context == null) {
throw new NullPointerException("context == null");
}
//CertificateFactory用来证书生成
CertificateFactory certificateFactory;
try {
certificateFactory = CertificateFactory.getInstance("X.509");
//Create a KeyStore containing our trusted CAs
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
for (int i = 0; i < certificates.length; i++) {
//读取本地证书
InputStream is = context.getResources().openRawResource(certificates[i]);
keyStore.setCertificateEntry(String.valueOf(i), certificateFactory.generateCertificate(is));
if (is != null) {
is.close();
}
}
//Create a TrustManager that trusts the CAs in our keyStore
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
//Create an SSLContext that uses our TrustManager
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());
return sslContext.getSocketFactory();
} catch (Exception e) {
}
return null;
}
}
三、3DES加解密
/**
*
* <p>* Describe:3DES加解密
*
* <p>* eg: String msg = "3DES加密解密案例";
* <p>
* System.out.println("【加密前】:" + msg);
* <p>
* //-----加密------
* <p>
* byte[] secretArr = SecretUtils.encryptMode(msg.getBytes());
* <p>
* System.out.println("【加密后】:" + new String(secretArr));
* <p>
* //-----解密------
* <p>
* byte[] myMsgArr = SecretUtils.decryptMode(secretArr);
* <p>
* System.out.println("【解密后】:" + new String(myMsgArr));
*/
public class SecretUtils {
//定义加密算法,有DES、DESede(即3DES)、Blowfish
private static final String Algorithm = "DESede";
private static final String PASSWORD_CRYPT_KEY = "V7OIDy10yhwnfLI10yutvtgjUYPewVZQ";
//编码
private static final String CHARSET = "UTF-8";
/**
* 加密方法
*
* @param src 源数据的字节数组
* @return
*/
public static byte[] encryptMode(byte[] src) {
try {
//生成密钥
SecretKey deskey = new SecretKeySpec(build3DesKey(PASSWORD_CRYPT_KEY), Algorithm);
//实例化负责加密/解密的Cipher工具类
Cipher c1 = Cipher.getInstance(Algorithm);
//初始化为加密模式
c1.init(Cipher.ENCRYPT_MODE, deskey);
return c1.doFinal(src);
} catch (java.security.NoSuchAlgorithmException e1) {
e1.printStackTrace();
} catch (javax.crypto.NoSuchPaddingException e2) {
e2.printStackTrace();
} catch (java.lang.Exception e3) {
e3.printStackTrace();
}
return null;
}
/**
* 解密函数
*
* @param src 密文的字节数组
* @return
*/
public static byte[] decryptMode(byte[] src) {
try {
SecretKey deskey = new SecretKeySpec(build3DesKey(PASSWORD_CRYPT_KEY), Algorithm);
Cipher c1 = Cipher.getInstance(Algorithm);
//初始化为解密模式
c1.init(Cipher.DECRYPT_MODE, deskey);
return c1.doFinal(src);
} catch (java.security.NoSuchAlgorithmException e1) {
e1.printStackTrace();
} catch (javax.crypto.NoSuchPaddingException e2) {
e2.printStackTrace();
} catch (java.lang.Exception e3) {
e3.printStackTrace();
}
return null;
}
/*
* 根据字符串生成密钥字节数组
* @param keyStr 密钥字符串
* @return
* @throws UnsupportedEncodingException
*/
public static byte[] build3DesKey(String keyStr) throws UnsupportedEncodingException {
//声明一个24位的字节数组,默认里面都是0
byte[] key = new byte[24];
//将字符串转成字节数组
byte[] temp = keyStr.getBytes(CHARSET);
/*
* 执行数组拷贝
* System.arraycopy(源数组,从源数组哪里开始拷贝,目标数组,拷贝多少位)
*/
if (key.length > temp.length) {
//如果temp不够24位,则拷贝temp数组整个长度的内容到key数组中
System.arraycopy(temp, 0, key, 0, temp.length);
} else {
//如果temp大于24位,则拷贝temp数组24个长度的内容到key数组中
System.arraycopy(temp, 0, key, 0, key.length);
}
return key;
}
}