美文网首页面试题Android应用开发那些事
笔记(二十一)——Http、Https、数据加解密

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

作者: 木溪bo | 来源:发表于2019-08-07 11:43 被阅读0次

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

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

相关文章

网友评论

    本文标题:笔记(二十一)——Http、Https、数据加解密

    本文链接:https://www.haomeiwen.com/subject/sqkohctx.html