数据结构和算法专题---8、加密算法

本章我们会对加密算法做个简单介绍,包括概述、实现方式、典型场景做个说明。

散列

概述

严格来讲这不算是一种加密,而应该叫做信息摘要算法。该算法使用散列函数把消息或数据压缩成摘要,使得数据量变小,将数据的格式固定下来。通过数据打乱混合,重新创建一个叫做 散列值

常见算法

MD5、SHA(128、256)系列

名称 安全性 速度
SHA-1
MD5

应用

常用于密码存储,或文件指纹校验。

网站用户注册后,密码经过MD5加密后的值,存储进DB。再次登录时,将用户输入的密码按同样的方式加密,与数据库中的密文比对。这样即使数据库被破解,或者开发人员可见,基于MD5的不可逆性,仍然不知道密码是什么。

其次是文件校验场景。例如从某站下载的文件(尤其是大文件,比如系统镜像iso),官方网站都会放置一个签名(可能是MD5,或者SHA),当用户拿到文件后,可以本地执行散列算法与官网签名比对是否一致,来判断文件是否被篡改。

实现

先添加commons坐标

yaml 复制代码
<dependency> 
  <groupId>commons‐codec</groupId> 
  <artifactId>commons‐codec</artifactId>  
  <version>1.14</version>
</dependency>
java 复制代码
package com.ls.cloud.sys.alg.pwd;

import org.apache.commons.codec.digest.DigestUtils;

import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class Hash {

    /**
     * jdk的security实现md5
     * 也可以借助commons-codec包
     */
    public static String md5(String src) {
        byte[] pwd = null;
        try {
            pwd = MessageDigest.getInstance("md5").digest(src.getBytes("utf-8"));
        } catch (Exception e) {
            e.printStackTrace();
        }
        String code = new BigInteger(1, pwd).toString(16);
        for (int i = 0; i < 32 - code.length(); i++) {
            code = "0" + code;
        }
        return code;
    }
    public static String commonsMd5(String src){
        return DigestUtils.md5Hex(src);
    }

    /**
     * jdk实现sha算法
     * 也可以借助commons-codec包
     */
    public static String sha(String src) throws Exception {
        MessageDigest sha = MessageDigest.getInstance("sha");
        byte[] shaByte = sha.digest(src.getBytes("utf-8"));
        StringBuffer code = new StringBuffer();
        for (int i = 0; i < shaByte.length; i++) {
            int val = ((int) shaByte[i]) & 0xff;
            if (val < 16) {
                code.append("0");
            }
            code.append(Integer.toHexString(val));
        }
        return code.toString();
    }
    public static String commonsSha(String src) throws Exception {
        return DigestUtils.sha1Hex(src);
    }


    public static void main(String[] args) throws Exception {
        System.out.println(name);
        System.out.println(md5(name));
        System.out.println(commonsMd5(name));
        System.out.println(sha(name));
        System.out.println(commonsSha(name));
    }
}

结果分析

java 复制代码
d98c9e606978909dd8cbeda3409b38ba
d98c9e606978909dd8cbeda3409b38ba
a74474a705b01a8ed1bfae76f4b8c36518341959
a74474a705b01a8ed1bfae76f4b8c36518341959
  • jdk与commons均生成了相同的散列值
  • 多次运行,依然生成固定值
  • commons-codec还有很多可用方法,如:sha256,sha512...

对称

概述

加密与解密用的都是同一个秘钥,性能比非对称加密高很多。

常见算法

常见的对称加密算法有 DES、3DES、AES

DES算法在POS、ATM、磁卡及智能卡(IC卡)、加油站、高速公路收费站等领域被广泛应用,以此来实现关键数据的保密,如信用卡持卡人的PIN的加密传输,IC卡与POS间的双向认证、金融交易数据包的MAC校验等

3DES是DES加密算法的一种模式,是DES的一个更安全的变形。从DES向AES的过渡算法

AES,是下一代的加密算法标准,速度快,安全级别更高。

名称 秘钥名称 运行速度 安全性 资源消耗
DES 56位 较快
3DES 112位或168位
AES 128、192、256位

应用

常用于对效率要求较高的实时数据加密通信。

实现

以AES为例:

java 复制代码
package com.ls.cloud.sys.alg.pwd;

import org.apache.commons.codec.binary.Base64;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

public class AESUtil {
    private static final String IV_STRING = "sdf4ddfsFD86Vdf2";
    private static final String encoding = "UTF-8";

    public static String encryptAES(String content, String key) throws Exception {
        SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(encoding), "AES");
        byte[] initParam = IV_STRING.getBytes(encoding);
        IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam);
        // 指定加密的算法、工作模式和填充方式
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
        byte[] encryptedBytes = cipher.doFinal(content.getBytes(encoding));
        // 同样对加密后数据进行 base64 编码
        String base64 = new Base64().encodeToString(encryptedBytes);
        return URLEncoder.encode(base64, encoding);
    }

    public static String decryptAES(String content, String key)
            throws InvalidKeyException, NoSuchAlgorithmException,
            NoSuchPaddingException, InvalidAlgorithmParameterException,
            IllegalBlockSizeException, BadPaddingException, IOException {
        //URL解码
        content = URLDecoder.decode(content, encoding);
        // base64 解码
        byte[] encryptedBytes = Base64.decodeBase64(content);
        byte[] enCodeFormat = key.getBytes(encoding);
        SecretKeySpec secretKey = new SecretKeySpec(enCodeFormat, "AES");
        byte[] initParam = IV_STRING.getBytes(encoding);
        IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam);
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
        byte[] result = cipher.doFinal(encryptedBytes);
        return new String(result, encoding);
    }

    public static void main(String[] args) throws Exception {
    /*json.put("name", "张三");
    json.put("cityCode", "100001");
    json.put("cityName", "北京市");
    json.put("mobileNo", "15651876590");*/
        String content = "架构师训练营";
        System.out.println("加密前:" + content);
        String key = "djadiKJdj49dFJLd";
        System.out.println("密钥:" + key);
        String encrypt = encryptAES(content, key);
        System.out.println("加密后:" + encrypt);
        String decrypt = decryptAES(encrypt, key);
        System.out.println("解密后:" + decrypt);
    }
}
java 复制代码
package com.ls.cloud.sys.alg.pwd;

import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.security.NoSuchAlgorithmException;

public class AES {
    public static void main(String[] args) throws Exception {
        //生成KEY
        KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
        keyGenerator.init(128);
        //key转换
        Key key = new SecretKeySpec(keyGenerator.generateKey().getEncoded(), "AES");

        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        
        System.out.println("明文:"+src);
        //加密
        cipher.init(Cipher.ENCRYPT_MODE, key);
        byte[] result = cipher.doFinal(src.getBytes());
        System.out.println("加密:" + Base64.encodeBase64String(result));

        //解密
        cipher.init(Cipher.DECRYPT_MODE, key);
        result = cipher.doFinal(result);
        System.out.println("解密:" + new String(result));
    }
}

结果分析

java 复制代码
明文:测试
加密:RXx6Kq+XQJ8rUAaaqAvaFg==
解密:测试
  • 加密成功,且解密后明文一致

非对称

概述

非对称即加密与解密不是同一把钥匙,而是分成公钥和私钥。私钥在个人手里,公钥公开。这一对钥匙一个用于加密,另一个用于解密。使用其中一个加密后,则原始明文只能用对应的另一个密钥解密,即使最初用于加密的密钥也不能用作解密。正是因为这种特性,所以称为非对称加密。

常见算法

RSA、ElGamal、背包算法、Rabin(RSA的特例)、迪菲-赫尔曼密钥交换协议中的公钥加密算法、椭圆曲线加密算法(英语:Elliptic Curve Cryptography, ECC)。使用最广泛的是RSA算法(发明者Rivest、Shmir和Adleman姓氏首字母缩写)

名称 成熟度 安全性 运算速度 资源消耗
RSA
ECC

应用

最常见的,两点:https和数字签名。

严格意义上讲,https并非所有请求都使用非对称。基于性能考虑,https先使用非对称约定一个key,后期使用该key进行对称加密和数据传输

数字签名则是用于验证报文是否为服务器发出的,用于防伪和认证。过程如下:

签发:

  • 服务器外发布公钥,私钥保密
  • 服务器对消息M计算摘要(如MD5等公开算法),得到摘要D
  • 服务器使用私钥对D进行签名,得到签名S
  • 将M和S一起发给
    客户验证:
  • 客户端对M使用同一摘要算法计算摘要,得到摘要D
  • 使用服务器公钥对S进行解密,得到摘要D'
  • 如果D和D'相同,那么证明M确实是服务器发出的

实现

java 复制代码
package com.ls.cloud.sys.alg.pwd;

import org.apache.commons.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;

public class RSAUtil {
    static String privKey ;
    static String publicKey;

    public static void main(String[] args) throws Exception {
        //生成公钥和私钥
        genKeyPair();
        //加密字符串
        String message = "架构师训练营";
        System.out.println("明文:"+message);
        System.out.println("随机公钥为:" + publicKey);
        System.out.println("随机私钥为:" + privKey);

        String messageEn = encrypt(message,publicKey);
        System.out.println("公钥加密:" + messageEn);
        String messageDe = decrypt(messageEn,privKey);
        System.out.println("私钥解密:" + messageDe);

    }

    /**
     * 随机生成密钥对
     */
    public static void genKeyPair() throws NoSuchAlgorithmException {
        // KeyPairGenerator类用于生成公钥和私钥对,基于RSA算法生成对象
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
        // 初始化密钥对生成器,密钥大小为96-1024位
        keyPairGen.initialize(1024,new SecureRandom());
        // 生成一个密钥对,保存在keyPair中
        KeyPair keyPair = keyPairGen.generateKeyPair();

        privKey = new String(Base64.encodeBase64((keyPair.getPrivate().getEncoded())));
        publicKey = new String(Base64.encodeBase64(keyPair.getPublic().getEncoded()));

    }
    /**
     * RSA公钥加密
     */
    public static String encrypt( String str, String publicKey ) throws Exception{
        //base64编码的公钥
        byte[] decoded = Base64.decodeBase64(publicKey);
        RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
        //RSA加密
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, pubKey);
        String outStr = Base64.encodeBase64String(cipher.doFinal(str.getBytes("UTF-8")));
        return outStr;
    }

    /**
     * RSA私钥解密
     */
    public static String decrypt(String str, String privateKey) throws Exception{
        //64位解码加密后的字符串
        byte[] inputByte = Base64.decodeBase64(str.getBytes("UTF-8"));
        byte[] decoded = Base64.decodeBase64(privateKey);
        RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, priKey);
        return new String(cipher.doFinal(inputByte));
    }

}

结果分析

java 复制代码
明文:测试
随机公钥为:MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCSywu7KTyiUj8gJQPI5MkCS+3wf8lij/TEh6I/ddTMawkWxLq6HWu/TRxvIQGkRbM/NeIsX+VzPqS/Mf3ir83csln6yQPFON13GQZX7JUhQd9g8NpUI2EC+lKCqgatzStQ1eNyP+Bk52rhjVvn0VU5yLcLBEBPu49WpkzLIbirmQIDAQAB
随机私钥为:MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAJLLC7spPKJSPyAlA8jkyQJL7fB/yWKP9MSHoj911MxrCRbEuroda79NHG8hAaRFsz814ixf5XM+pL8x/eKvzdyyWfrJA8U43XcZBlfslSFB32Dw2lQjYQL6UoKqBq3NK1DV43I/4GTnauGNW+fRVTnItwsEQE+7j1amTMshuKuZAgMBAAECgYBNhgQdBOsrwonp/QJhr0nk95qin6oIboRS4lqybxVCG+kB6EXBEtabgirZGbJXQiQm5tsamk6ALX4uJ0Ww2S27acpon4isU8OybqWP/+PYTSoG/2ySu0i4BX9IS+2QmTJB8i9q+Wpn1tW1BfYjZUWTj19hG/LeU5FIBi6/9EeegQJBANMOSKmOrLNdlLL3FAZk/RqWK3rTirIgWb+B1IwhabpPKYV4boRU9VoXJAafcwMzPLFjp2TBqJuhYETpl7m2hWkCQQCyDXsX6Smecp76Iy/FHLHGJx4A4GuNaF7dIuatG4PqIOkKL4X+UyWEz0j1xWdEP2BLEnHDP+JYYAT0mgAanz6xAkBiF/V1uTZTd18xftzzy+RHgxxaTg/ckmSkObeMGcuGKFzoB/11y/btFGxOF7Xg+uNunx6iGdA/5VVVMiyuyEJhAkAYzJfgdLE/SGesH0qAAccg+kHLjXZtc6QC2OGYKsTszzimGohnK92F6fkXgi8n6kvXKPJ/Z153QKhwCSMKkjOBAkBuFKcsVpcDkCelMWcLz4lvDKgyT/Xb3s1cda9lnvugeI+NGQM4Bqwzv3XxUsV1msEWy7Acpyg7KkwjN5H2w5SX
公钥加密:JOvASbyJmINhFtnKg0nAvfHKY5y8tAedu/IdwqCDSh61PC+Humy9jDitHXyXL7tjATkgkm/wC7MvWJZBnf6QyjvENEQAHREwyLZ00zHcqlhRgxeYC9xdVMK6oBVT2HjLz2445e2ayvb4d2NKFB3uJp6LrnZTw0M5CfR7o4lpwiY=
私钥解密:测试
相关推荐
noravinsc1 分钟前
人大金仓数据库 与django结合
数据库·python·django
豌豆花下猫8 分钟前
Python 潮流周刊#102:微软裁员 Faster CPython 团队(摘要)
后端·python·ai
进击的小白菜31 分钟前
Java回溯算法解决非递减子序列问题(LeetCode 491)的深度解析
java·算法·leetcode
yzx99101341 分钟前
Gensim 是一个专为 Python 设计的开源库
开发语言·python·开源
麻雀无能为力1 小时前
python自学笔记2 数据类型
开发语言·笔记·python
Ndmzi1 小时前
matlab与python问题解析
python·matlab
懒大王爱吃狼1 小时前
怎么使用python进行PostgreSQL 数据库连接?
数据库·python·postgresql
猫猫村晨总1 小时前
网络爬虫学习之httpx的使用
爬虫·python·httpx
web150854159351 小时前
Python线性回归:从理论到实践的完整指南
python·机器学习·线性回归
ayiya_Oese1 小时前
[训练和优化] 3. 模型优化
人工智能·python·深度学习·神经网络·机器学习