2、加密算法介绍及RSA实战

加密算法介绍及RSA实战

一、算法的分类

通常分为可逆性加密算法、不可逆性算法。

二、算法介绍

2.1、可逆加密算法

解释:加密后, 密文可以反向解密得到密码原文.

2.1.1、对称加密

文件加密和解密使用相同的密钥,即加密密钥也可以用作解密密钥

解释:

在对称加密算法中,数据发信方将明文和加密密钥一起经过特殊的加密算法处理后,使其变成复杂的加密密文 发送出去;

​ 收信方收到密文后,若想解读出原文,则需要使用加密时用的密钥以及相同加密算法的逆算法对密文进行。

​ 解密,才能使其回复成可读明文。在对称加密算法中,使用的密钥只有一个,收发双方都使用这个密钥,这就需要解

​ 密方事先知道加密密钥。

优点: 对称加密算法的优点是算法公开、计算量小、加密速度快、加密效率高。

缺点:没有非对称加密安全.

用途: 一般用于保存用户手机号、身份证等敏感但能解密的信息。

常见的对称加密算法有: AES、DES、3DES、Blowfish、IDEA、RC4、RC5、RC6、HS256

2.1.2、非对称加密

两个密钥:公开密钥(publickey)和私有密钥,公有密钥加密,私有密钥解密

解释:

​ 同时生成两把密钥:私钥和公钥,私钥隐秘保存,公钥可以下发给信任客户端.

​ 加密与解密:

​ 私钥加密,持有私钥或公钥才可以解密

​ 公钥加密,持有私钥才可解密

​ 签名:

​ 私钥签名, 持有公钥进行验证是否被篡改过.

优点:非对称加密与对称加密相比,其安全性更好;

缺点: 非对称加密的缺点是加密和解密花费时间长、速度慢,只适合对少量数据进行加密。

用途: 一般用于签名和认 证。私钥服务器保存, 用来加密, 公钥客户拿着用于对于令牌或者签名的解密或者校验使用.

常见的非对称加密算法有: RSA、DSA(数字签名用)、ECC(移动设备用)、RS256 (采用SHA-256 的 RSA 签名)

2.2、不可逆加密算法

解释: 一旦加密就不能反向解密得到密码原文.

种类:Hash加密算法, 散列算法, 摘要算法等

**用途:**一般用于效验下载文件正确性,一般在网站上下载文件都能见到;存储用户敏感信息,如密码、 卡号等不可解密的信息。

常见的不可逆加密算法有: MD5、SHA、HMAC

三、扩展之Base64编码

解释:Base64是网络上最常见的用于传输8Bit字节代码的编码方式之一。Base64编码可用于在HTTP环境下传递较长的标识信息。采用Base64编码解码具有不可读性,即所编码的数据不会被人用肉眼所直接看到。

注意:Base64只是一种编码方式,不算加密方法。

在线编码工具: http://www.jsons.cn/img2base64/

四、RSA详解

解释:同时生成两把密钥:私钥和公钥,私钥隐秘保存,公钥可以下发给信任客户端。

​ 私钥加密,持有私钥或公钥才可以解密 ;

​ 公钥加密,持有私钥才可解密 。

优点:安全,难以破解

缺点:算法比较耗时,为了安全,可以接受

历史:三位数学家Rivest、Shamir 和 Adleman 设计了一种算法,可以实现非对称加密。这种算法用他们三

个人的名字缩写:RSA。

五、RSA实战

1、生成私钥、公钥;

根据如下generateKey方法获取公钥、私钥,然后文件名分别命名为id_key_rsa、id_key_rsa.pub,并放入resources下。


import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.util.Base64;

/**
 * @author :
 * @date :Created in 16:30 2022/7/20
 * @description :
 * @version: 1.0
 */
public class RsaUtils {

    private static final int DEFAULT_KEY_SIZE = 2048;
    
    /**
     * 根据密文,生成rsa公钥和私钥,并写入指定文件
     *
     * @param publicKeyFilename  公钥文件路径
     * @param privateKeyFilename 私钥文件路径
     * @param secret             生成密钥的密文
     */
    public static void generateKey(String publicKeyFilename, String privateKeyFilename, String secret, int keySize) throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        SecureRandom secureRandom = new SecureRandom(secret.getBytes());
        keyPairGenerator.initialize(Math.max(keySize, DEFAULT_KEY_SIZE), secureRandom);
        KeyPair keyPair = keyPairGenerator.genKeyPair();
        // 获取公钥并写出
        byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
        publicKeyBytes = Base64.getEncoder().encode(publicKeyBytes);
        writeFile(publicKeyFilename, publicKeyBytes);
        // 获取私钥并写出
        byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
        privateKeyBytes = Base64.getEncoder().encode(privateKeyBytes);
        writeFile(privateKeyFilename, privateKeyBytes);
    }

    private static void writeFile(String destPath, byte[] bytes) throws IOException {
        File dest = new File(destPath);
        if (!dest.exists()) {
            dest.createNewFile();
        }
        Files.write(dest.toPath(), bytes);
    }
}

id_key_rsa
id_key_rsa.pub

2、使用配置类读取;

使用配置类加载resources下的私钥、公钥。

import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.security.PrivateKey;
import java.security.PublicKey;
/**
 * @author :
 * @date :Created in 19:38 2022/7/18
 * @description :
 * @version: 1.0
 */
@Component
public class RsaKeyProperties {

    private String pubKeyFile= "id_key_rsa.pub";
    private String priKeyFile= "id_key_rsa";

    private PublicKey publicKey;
    private PrivateKey privateKey;

    @PostConstruct
    public void createRsaKey() throws Exception {
        publicKey = RsaUtils.getPublicKey(new ClassPathResource(pubKeyFile).getInputStream().readAllBytes());
        privateKey = RsaUtils.getPrivateKey(new ClassPathResource(priKeyFile).getInputStream().readAllBytes());
    }


    public String getPubKeyFile() {
        return pubKeyFile;
    }
    public void setPubKeyFile(String pubKeyFile) {
        this.pubKeyFile = pubKeyFile;
    }

    public String getPriKeyFile() {
        return priKeyFile;
    }

    public void setPriKeyFile(String priKeyFile) {
        this.priKeyFile = priKeyFile;
    }

    public PublicKey getPublicKey() {
        return publicKey;
    }

    public void setPublicKey(PublicKey publicKey) {
        this.publicKey = publicKey;
    }

    public PrivateKey getPrivateKey() {
        return privateKey;
    }

    public void setPrivateKey(PrivateKey privateKey) {
        this.privateKey = privateKey;
    }
}

3、使用工具类加解密;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

/**
 * @author :
 * @date :Created in 19:30 2022/7/19
 * @description :
 * @version: 1.0
 */
public class RsaUtils {

    private static final int DEFAULT_KEY_SIZE = 2048;
    
    /**
     * 从文件中读取公钥
     *
     * @param filename 公钥保存路径,相对于classpath
     * @return 公钥对象
     * @throws Exception
     */
    public static PublicKey getPublicKey(String filename) throws Exception {
        byte[] bytes = readFile(filename);
        return getPublicKey(bytes);
    }


    /**
     * 从文件中读取密钥
     *
     * @param filename 私钥保存路径,相对于classpath
     * @return 私钥对象
     * @throws Exception
     */
    public static PrivateKey getPrivateKey(String filename) throws Exception {
        byte[] bytes = readFile(filename);
        return getPrivateKey(bytes);
    }

    /**
     * 获取公钥
     *
     * @param bytes 公钥的字节形式
     * @return
     * @throws Exception
     */
    public static PublicKey getPublicKey(byte[] bytes) throws Exception {
        bytes = Base64.getDecoder().decode(bytes);
        X509EncodedKeySpec spec = new X509EncodedKeySpec(bytes);
        KeyFactory factory = KeyFactory.getInstance("RSA");
        return factory.generatePublic(spec);
    }

    /**
     * 获取密钥
     *
     * @param bytes 私钥的字节形式
     * @return
     * @throws Exception
     */
    public static PrivateKey getPrivateKey(byte[] bytes) throws NoSuchAlgorithmException, InvalidKeySpecException {
        bytes = Base64.getDecoder().decode(bytes);
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);
        KeyFactory factory = KeyFactory.getInstance("RSA");
        return factory.generatePrivate(spec);
    }

    /**
     * 根据密文,生存rsa公钥和私钥,并写入指定文件
     *
     * @param publicKeyFilename  公钥文件路径
     * @param privateKeyFilename 私钥文件路径
     * @param secret             生成密钥的密文
     */
    public static void generateKey(String publicKeyFilename, String privateKeyFilename, String secret, int keySize) throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        SecureRandom secureRandom = new SecureRandom(secret.getBytes());
        keyPairGenerator.initialize(Math.max(keySize, DEFAULT_KEY_SIZE), secureRandom);
        KeyPair keyPair = keyPairGenerator.genKeyPair();
        // 获取公钥并写出
        byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
        publicKeyBytes = Base64.getEncoder().encode(publicKeyBytes);
        writeFile(publicKeyFilename, publicKeyBytes);
        // 获取私钥并写出
        byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
        privateKeyBytes = Base64.getEncoder().encode(privateKeyBytes);
        writeFile(privateKeyFilename, privateKeyBytes);
    }

    private static byte[] readFile(String fileName) throws Exception {
        return Files.readAllBytes(new File(fileName).toPath());
    }

    private static void writeFile(String destPath, byte[] bytes) throws IOException {
        File dest = new File(destPath);
        if (!dest.exists()) {
            dest.createNewFile();
        }
        Files.write(dest.toPath(), bytes);
    }
}

六、扩展之AES加解密工具类

解释:AES即高级加密标准(AES,Advanced Encryption Standard)为最常见的对称加密算法(微信小程序加密传输就是用这个加密算法的)。对称加密算法也就是加密和解密用相同的密钥。

注: 缺少的导包可以去我的其他文章里面获取

工具类

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.SecretKeySpec;

/**
 * @Author: 
 * @Desc: AES加密工具
 * @Date: 2022/7/18 19:44
 * @Modify By:
 */
public class AesEncryptUtils {

    /**
     * 算法
     */
    private static final String ALGORITHMSTR = "AES/ECB/PKCS5Padding";

    /**
     * 秘钥
     **/
    private static final String ENCRYPT_KEY = "nanMu";

    /**
     * base 64 encode
     * @param bytes 待编码的byte[]
     * @return 编码后的base 64 code
     */
    private static String base64Encode(byte[] bytes){
        return Base64.encodeBase64String(bytes);
    }

    /**
     * base 64 decode
     * @param base64Code 待解码的base 64 code
     * @return 解码后的byte[]
     * @throws Exception 抛出异常
     */
    private static byte[] base64Decode(String base64Code) throws Exception{
        return StringUtils.isEmpty(base64Code) ? null : java.util.Base64.getDecoder().decode(base64Code);
    }

    /**
     * AES加密
     * @param content 待加密的内容
     * @return 加密后的byte[]
     */
    private static byte[] aesEncryptToBytes(String content) {
        String encryptKey = ENCRYPT_KEY;
        try{
            KeyGenerator kgen = KeyGenerator.getInstance("AES");
            kgen.init(128);
            Cipher cipher = Cipher.getInstance(ALGORITHMSTR);
            cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(encryptKey.getBytes(), "AES"));

            return cipher.doFinal(content.getBytes("utf-8"));
        }catch (Exception e){
            throw new BusinessException(ExceptionCodeEnum.SERVICE_ERROR,"AES加密失败");
        }
    }

    /**
     * AES加密为base 64 code
     *
     * @param content 待加密的内容
     * @return 加密后的base 64 code
     */
    public static String aesEncrypt(String content) {
        String ciphertext = base64Encode(aesEncryptToBytes(content));
        if(StringUtils.isEmpty(ciphertext)){
            throw new BusinessException(ExceptionCodeEnum.SERVICE_ERROR,"AES加密失败");
        }
        return ciphertext;
    }

    /**
     * AES解密
     *
     * @param encryptBytes 待解密的byte[]
     * @param decryptKey 解密密钥
     * @return 解密后的String
     */
    private static String aesDecryptByBytes(byte[] encryptBytes, String decryptKey) throws Exception {
        KeyGenerator kgen = KeyGenerator.getInstance("AES");
        kgen.init(128);

        Cipher cipher = Cipher.getInstance(ALGORITHMSTR);
        cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(decryptKey.getBytes(), "AES"));
        byte[] decryptBytes = cipher.doFinal(encryptBytes);

        return new String(decryptBytes);
    }

    /**
     * 将base 64 code AES解密
     * @param encryptStr 待解密的base 64 code
     * @param decryptKey 解密密钥
     * @return 解密后的string
     */
    public static String aesDecrypt(String encryptStr, String decryptKey) throws Exception {
        return StringUtils.isEmpty(encryptStr) ? null : aesDecryptByBytes(base64Decode(encryptStr), decryptKey);
    }
}

七、扩展之MD5+加盐加密工具类

解释:md5内容不可逆,相同的内容不论执行多少次,md5加密算法生成的结果都是一致的。

结果:生成的结果是一个32位的16进制字符串。

工具类

import java.security.MessageDigest;

public class MD5Util {
    private static final char[] HEXDIGESTS = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};

    public static String encode(String source) {
        try {
            MessageDigest md5 = MessageDigest.getInstance("md5");

            md5.update(source.getBytes("utf-8"));

            byte[] digests = md5.digest();
            char[] result = new char[digests.length * 2];
            int resultIndex = 0;
            for (int i = 0; i < digests.length; i++) {
                byte bytex = digests[i];

                /***
                 * 25=0 0 0 1 1 0 0 1>>>4=======0 0 0 0 0 0 0 1& 0 0 0 0 1 1 1 1----0 0 0 0  0 0 0 1
                 * 25=0 0 0 1 1 0 0 1&0 0 0 0 1 1 1 1=------0 0 0 0 1 0 0 1
                 *
                 */
                result[resultIndex++] = HEXDIGESTS[bytex >>> 4 & 0xf];
                result[resultIndex++] = HEXDIGESTS[bytex & 0xf];
            }
            return new String(result);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static boolean compare(String source, String encoded) {
        return encode(source).equals(encoded);
    }

    public static String encodeWithSalt(String source, String salt) {
        return encode(source + salt);
    }

    public static boolean compareWithSalt(String source, String salt, String encoded) {
        return compare(encodeWithSalt(source, salt), encoded);
    }

    public static void main(String[] args) {
        //String encode = encode("123456");
        //System.out.println(encode);

        //加盐
        String withSalt = encodeWithSalt("123456", "xiaohu");
        System.out.println(withSalt);
        String withSalt1 = encodeWithSalt("zzz", "123");
        System.out.println(withSalt1);
        String withSalt2 = encodeWithSalt("123", "123");
        System.out.println(withSalt2);

        //慢hash算法

    }
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小白de成长之路

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值