笔记(二十一)——Http、Https、数据加解密

一、Http请求(转载部分HTTP请求报文(请求行、请求头、请求体)
特点:

(1)HTTP是无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。

(2)HTTP是媒体独立的:只要客户端和服务器知道如何处理的数据内容,任何类型的数据都可以通过HTTP发送。客户端以及服务器指定使用适合的MIME-type内容类型。

(3)HTTP是无状态:无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。

HTTP请求报文

HTTP请求报文由4部分组成(请求行+请求头+请求体+空行):


image

请求行:

①是请求方法,GET和POST是最常见的HTTP方法,除此以外还包括DELETE、HEAD、OPTIONS、PUT、TRACE。

②为请求对应的URL地址,它和报文头的Host属性组成完整的请求URL。

③是协议名称及版本号。

请求头:

④是HTTP的报文头,报文头包含若干个属性,格式为“属性名:属性值”,服务端据此获取客户端的信息。

与缓存相关的规则信息,均包含在header中

请求体:

⑤是报文体即请求体,它将一个页面表单中的组件值通过param1=value1&param2=value2的键值对形式编码成一个格式化串,它承载多个请求参数的数据。不但报文体可以传递请求参数,请求URL也可以通过类似于“/chapter15/user.html? param1=value1&param2=value2”的方式传递请求参数。

⑥还有一个是跟在请求体后面的空行,其没有实际意义,主要是用来发送回车符和换行符,通知服务器以下不再有请求头。

服务端响应客户端:


image

响应行:

①报文协议及版本;
②状态码及状态描述;

响应头:

③响应报文头,也是由多个属性组成;

响应体:

④响应报文体,即我们真正要的信息数据

二、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;
    }
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容