系列学习互联网安全架构第 5 篇 —— 信息加密技术(对称加密、非对称加密)

 

什么是对加密?

对称密码技术:发件人和收件人使用其共同拥有相同的单个密钥,这种密钥既用于加密,也用于解密,叫做机密密钥(也称为对称密钥或会话密钥)。

能够提供信息机密性(没有密钥信息不能被解密)、完整性(被改变的信息不能被解密)的服务。

对称式密码学又称:单钥密码学、秘密密钥密码学、会话密钥密码学、私钥密码学、共享秘钥密码学。

常见的对称式加密技术

DES(数据加密标准):分组式加密,算法源于Lucifer,作为NIST对称式加密标准;64位(有效位56位、校验8位),分组算法

3DES:128位,分组算法IDEA(国际数据加密算法):128位,比DES快,分组算法

AES(高级加密标准):DES升级版,算法出自 Rinjindael

对称加密优缺点

优点:用户只需记忆一个密钥,就可用于加密、解密;与非对称加密方法相比,加密解密的计算量小,速度快,简单易用,适合于对海量数据进行加密处理。

缺点:如果密钥交换不安全,密钥的安全性就会丧失。特别是在电子商务环境下,当客户是未知的、不可信的实体时,如何使客户安全地获得密钥就成为一大难题。
如果用户较多情况下的密钥管理问题。如果密钥多个用户被共享,不能提供抗抵赖性。

使用场景

对称加密一般适用于后台与后台之间的通讯,不暴露外网地址,无法让第三方抓包的场景。在移动端,不能使用对称加密,防止黑客把APK(移动端APP)反编译得到秘钥。

DES 加解密工具类:

package com.study.util;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import java.security.SecureRandom;

/**
 * @author biandan
 * @description
 * @signature 让天下没有难写的代码
 * @create 2021-06-03 下午 5:52
 */
public class DESUtil {

    public static void main(String[] args) {
        // 待加密内容
        String str = "biandan";
        // 密码,长度要是8的倍数
        String password = "87654321";

        byte[] result = DESUtil.encrypt(str.getBytes(), password);
        System.out.println("加密后:" + new String(result));
        // 直接将如上内容解密
        try {
            byte[] decryResult = DESUtil.decrypt(result, password);
            System.out.println("解密后:" + new String(decryResult));
        } catch (Exception e1) {
            e1.printStackTrace();
        }
    }

    /**
     * 加密
     * @param datasource 数据源
     * @param password 密码
     * @return
     */
    public static byte[] encrypt(byte[] datasource, String password) {
        try {
            SecureRandom random = new SecureRandom();
            DESKeySpec desKey = new DESKeySpec(password.getBytes());
            // 创建一个密匙工厂,然后用它把DESKeySpec转换成
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
            SecretKey securekey = keyFactory.generateSecret(desKey);
            // Cipher对象实际完成加密操作
            Cipher cipher = Cipher.getInstance("DES");
            // 用密匙初始化Cipher对象,ENCRYPT_MODE用于将 Cipher 初始化为加密模式的常量
            cipher.init(Cipher.ENCRYPT_MODE, securekey, random);
            // 现在,获取数据并加密
            // 正式执行加密操作
            return cipher.doFinal(datasource); // 按单部分操作加密或解密数据,或者结束一个多部分操作
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 解密
     * @param src
     * @param password
     * @return
     * @throws Exception
     */
    public static byte[] decrypt(byte[] src, String password) throws Exception {
        // DES算法要求有一个可信任的随机数源
        SecureRandom random = new SecureRandom();
        // 创建一个DESKeySpec对象
        DESKeySpec desKey = new DESKeySpec(password.getBytes());
        // 创建一个密匙工厂
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");// 返回实现指定转换的
        // Cipher 对象
        // 将DESKeySpec对象转换成SecretKey对象
        SecretKey securekey = keyFactory.generateSecret(desKey);
        // Cipher对象实际完成解密操作
        Cipher cipher = Cipher.getInstance("DES");
        // 用密匙初始化Cipher对象
        cipher.init(Cipher.DECRYPT_MODE, securekey, random);
        // 真正开始解密操作
        return cipher.doFinal(src);
    }

}

测试:

 

非对称加密(目前互联网主流的加密手段)

使用一对(2个)密钥:一个用于加密信息,另一个则用于解密信息。
两个密钥之间存在着相互依存关系:即用其中任一个密钥加密的信息只能用另一个密钥进行解密。(公钥加密,私钥解密。或者,私钥加密,公钥解密。一般是公钥加密私钥解密)
密钥依据性质划分,将其中的一个向外界公开,称为公钥;另一个则自己保留,称为私钥。公钥(Public key)常用于数据加密(用对方公钥加密)或签名验证(用对方公钥解密),私钥(Private key)常用于数据解密(发送方用接收方公钥加密)或数字签名(用自己私钥加密)。
特性:机密性、完整性、抗抵赖性

加解密过程

场景1:甲给乙传递数据。

乙:生成两把密钥(公钥和私钥),乙把公钥给甲,私钥自己保留。

甲:获取乙的公钥,然后用它对信息加密,把数据传递给乙。

乙:得到甲发送的加密后信息,用自己的私钥解密,得到明文数据。

场景2:乙给甲传递数据

乙:生成两把密钥(公钥和私钥),乙把公钥给甲,自己保留私钥。

乙:用自己的私钥加密数据,传递给甲。

甲:获取乙方私钥加密数据,用公钥解密。

优点:难破解

缺点: 加密速度慢

 

非对称加密常用算法

RSA、Elgamal、背包算法、Rabin、D-H、ECC(椭圆曲线加密算法)

 

非对称加密示例:

package com.study.util;

import org.apache.tomcat.util.codec.binary.Base64;

import javax.crypto.Cipher;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;

/**
 * @author biandan
 * @description RSA 非对称加密工具类
 * @signature 让天下没有难写的代码
 * @create 2021-06-03 下午 10:12
 */
public class RSAUtil {

    public static String publicKey; // 公钥

    public static String privateKey; // 私钥

    public static String RSA = "RSA";//加密算法

    //生成公钥和私钥,它们是成对出现的
    public static void generateKey() {
        // 1.初始化秘钥
        KeyPairGenerator keyPairGenerator;
        try {
            keyPairGenerator = KeyPairGenerator.getInstance(RSA);
            SecureRandom sr = new SecureRandom(); // 随机数生成器
            keyPairGenerator.initialize(512, sr); // 设置最少512位长的秘钥
            KeyPair keyPair = keyPairGenerator.generateKeyPair(); // 开始创建
            RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
            RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
            // 进行转码
            publicKey = Base64.encodeBase64String(rsaPublicKey.getEncoded());
            // 进行转码
            privateKey = Base64.encodeBase64String(rsaPrivateKey.getEncoded());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 私钥匙加密或解密
     *
     * @param content
     * @param privateKeyStr
     * @param mode 1=加密,2=解密
     * @return
     */
    public static String encryptByPrivateKey(String content, String privateKeyStr, int mode) {
        // 私钥要用PKCS8进行处理
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyStr));
        String result = null;
        try {
            KeyFactory keyFactory = KeyFactory.getInstance(RSA);
            // 还原Key对象
            PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
            Cipher cipher = Cipher.getInstance(RSA);
            cipher.init(mode, privateKey);
            byte[] bytes;
            int maxLength;//最大加密、解密字节数,超出最大字节数需要分组加密、解密
            if(mode == Cipher.DECRYPT_MODE){//解密
                bytes = Base64.decodeBase64(content);
                maxLength = 64;
            }else{
                bytes = content.getBytes("UTF-8");
                maxLength = 32;
            }
            int dataLength = bytes.length;
            System.out.println("私钥获取的数据长度="+dataLength);
            int offSet = 0;//偏移值
            byte[] resultBytes = {};
            byte[] tempByte;
            while (dataLength - offSet > 0) {
                if (dataLength - offSet > maxLength) {
                    tempByte = cipher.doFinal(bytes, offSet, maxLength);
                    offSet += maxLength;
                } else {
                    tempByte = cipher.doFinal(bytes, offSet, dataLength - offSet);
                    offSet = dataLength;
                }
                resultBytes = Arrays.copyOf(resultBytes, resultBytes.length + tempByte.length);
                System.arraycopy(tempByte, 0, resultBytes, resultBytes.length - tempByte.length, tempByte.length);
            }
            if (mode == Cipher.ENCRYPT_MODE) {// 加密
                result = Base64.encodeBase64String(resultBytes);
            }else{
                result = new String(resultBytes, "UTF-8");//解密
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }


    /**
     * 公钥匙加密或解密
     *
     * @param content
     * @param publicKeyStr
     * @param mode 1=加密,2=解密
     * @return
     */
    public static String encryptByPublicKey(String content, String publicKeyStr, int mode) {
        // 公钥要用X509进行处理
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyStr));
        String result = null;
        try {
            KeyFactory keyFactory = KeyFactory.getInstance(RSA);
            // 还原Key对象
            PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
            Cipher cipher = Cipher.getInstance(RSA);
            cipher.init(mode, publicKey);
            byte[] bytes;
            int maxLength;//最大加密、解密字节数,超出最大字节数需要分组加密、解密
            if(mode == Cipher.DECRYPT_MODE){//解密
                bytes = Base64.decodeBase64(content);
                maxLength = 64;
            }else{
                bytes = content.getBytes("UTF-8");
                maxLength = 32;
            }
            int dataLength = bytes.length;
            System.out.println("公钥获取的数据长度="+dataLength);
            int offSet = 0;//偏移值
            byte[] resultBytes = {};
            byte[] tempByte;
            while (dataLength - offSet > 0) {
                if (dataLength - offSet > maxLength) {
                    tempByte = cipher.doFinal(bytes, offSet, maxLength);
                    offSet += maxLength;
                } else {
                    tempByte = cipher.doFinal(bytes, offSet, dataLength - offSet);
                    offSet = dataLength;
                }
                resultBytes = Arrays.copyOf(resultBytes, resultBytes.length + tempByte.length);
                System.arraycopy(tempByte, 0, resultBytes, resultBytes.length - tempByte.length, tempByte.length);
            }
            if (mode == Cipher.ENCRYPT_MODE) {// 加密
                result = Base64.encodeBase64String(resultBytes);
            }else{
                result = new String(resultBytes, "UTF-8");//解密
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    //测试
    public static void main(String[] args) throws Exception{
        /**
         * TODO 注意: 私钥加密必须公钥解密。公钥加密必须私钥解密。
         */
        System.out.println("-------------生成一对秘钥,公钥给接收方,私钥给发送方自己保存 -------------");
        RSAUtil.generateKey();
        System.out.println("公钥:" + RSAUtil.publicKey);
        System.out.println("私钥:" + RSAUtil.privateKey);

        System.out.println("------------- 例子1:私钥加密,公钥解密 -------------");
        String secret = "战书:明晚十点城外凉亭 Java 要和 PHP 一决高下!";
        // 私钥加密
        String encryptText = RSAUtil.encryptByPrivateKey(secret, RSAUtil.privateKey, Cipher.ENCRYPT_MODE);
        System.out.println("私钥加密的密文:" + encryptText);
        // 公钥解密
        String result = RSAUtil.encryptByPublicKey(encryptText, RSAUtil.publicKey, Cipher.DECRYPT_MODE);
        System.out.println("公钥解密的明文:" + result);


        System.out.println();
        System.out.println("------------- 例子2:公钥加密,私钥解密 -------------");
        // 公钥加密
        String movieMsg = "后台开发:周日下午一起去看电影《战狼Ⅱ》,有空不? 前端妹纸:可以呀!";

        String cipherText = RSAUtil.encryptByPublicKey(movieMsg, RSAUtil.publicKey, Cipher.ENCRYPT_MODE);
        System.out.println("公钥加密的密文:" + cipherText);
        // 私钥解密
        String text = RSAUtil.encryptByPrivateKey(cipherText, RSAUtil.privateKey, Cipher.DECRYPT_MODE);
        System.out.print("私钥解密的明文:" + text);

    }
}

注意:加解密需要判断数据长度,否则报错:javax.crypto.IllegalBlockSizeException: Data must not be longer than 53 bytes

运行结果:

 

OK,信息加密技术讲解到这。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值