常见加密算法汇总

消息摘要

  • 消息摘要(Message Digest)又称为数字摘要(Digital Digest)

  • 它是一个唯一对应一个消息或文本的固定长度的值,它由一个单向Hash加密函数对消息进行作用而产生

  • 使用数字摘要生成的值是不可以篡改的,为了保证文件或者值的安全

  • 无论输入的消息有多长,计算出来的消息摘要的长度总是固定的。例如应用MD5算法摘要的消息有128个比特位,用SHA-1算法摘要的消息最终有160比特位的输出

    只要输入的消息不同,对其进行摘要以后产生的摘要消息也必不相同;但相同的输入必会产生相同的输出,消息摘要是单向、不可逆的

常见的消息摘要算法

  • MD5算法 : 摘要结果16个字节, 转16进制后32个字节
  • SHA1算法 : 摘要结果20个字节, 转16进制后40个字节
  • SHA256算法 : 摘要结果32个字节, 转16进制后64个字节
  • SHA512算法 : 摘要结果64个字节, 转16进制后128个字节

应用场景

  • 一般用在校验文件合法性,一般官网里面的软件产品会有一个数字摘要,当下载到的软件时候经过相同的摘要算法会返回与官网给出摘要相同,如果不同说明文件被篡改过
  • 秒传功能通过数字摘要算法获取某一个文件的摘要秘钥跟服务器已经存储的文件对比如果已经存在说明用户不需要再上传文件直接添加一个引用指向即可

代码示例

public class MessageDigestDemo {

    public static void main(String[] args) throws Exception {
        String md5 = digest("test", "MD5");
        System.out.println(md5);
        String sha1 = digest("test", "SHA-1");
        System.out.println(sha1);
        String sha256 = digest("test", "SHA-256");
        System.out.println(sha256);
        String sha512 = digest("test", "SHA-512");
        System.out.println(sha512);
    }

    public static String digest(String content, String algorithm) throws Exception {
        // 消息摘要核心类 MessageDigest
        MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
        // 调用digest 方法获取数字摘要 返回一个byte 数组
        byte[] digest = messageDigest.digest(content.getBytes());
        StringBuilder builder = new StringBuilder();
        // 将摘要转成一个16进制的字符串如果不足用0补全
        for (byte b : digest) {
            // 转成 16进制
            String s = Integer.toHexString(b & 0xff);
            if (s.length() == 1) {
                // 如果生成的字符只有一个,前面补0
                s = "0" + s;
            }
            builder.append(s);
        }
        return builder.toString();
    }
}

对称加密

  • 采用单钥密码系统的加密方法,同一个密钥可以同时用作信息的加密和解密,这种加密方法称为对称加密,也称为单密钥加密。
  • 常见加密算法
    • DES : Data Encryption Standard,即数据加密标准,是一种使用密钥加密的块算法,1977年被美国联邦政府的国家标准局确定为联邦资料处理标准(FIPS),并授权在非密级政府通信中使用,随后该算法在国际上广泛流传开来。
    • AES : Advanced Encryption Standard, 高级加密标准 .在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。
  • 特点
    • 加密速度快, 可以加密大文件
    • 密文可逆, 一旦密钥文件泄漏, 就会导致数据暴露
    • 加密后编码表找不到对应字符, 出现乱码
    • 一般结合Base64使用

代码示例:

DES 算法

需要主要的是DES算法中传入key必须是一个8位

public class DESDemo {

    public static void main(String[] args) throws Exception {
        String key = "12345678";
        String secret = enCryptByDES("测试DES", key);
        String deCryptByDES = deCryptByDES(secret,key);
        assert secret.equals(deCryptByDES);
    }

    /**
     * 解码
     * @param secret Base64之后的密文
     * @param key 秘钥key
     * @return
     * @throws Exception
     */
    private static String deCryptByDES(String secret,String key) throws Exception{
        String algorithm = "DES";
        Cipher cipher = Cipher.getInstance(algorithm);
        Key keySpec = new SecretKeySpec(key.getBytes(),algorithm);
        // 第一个参数传入解密模式
        cipher.init(Cipher.DECRYPT_MODE,keySpec);
        // 需要将之前Base64 encode之后的秘钥进行decode转回byte[]
        byte[] deCrypt = cipher.doFinal(Base64.decode(secret.getBytes()));
        String content = new String(deCrypt);
        System.out.println(content);
        return content;
    }

    /**
     *
     * @param content 明文
     * @param key DES秘钥 必须是8位!!
     * @return
     */
    private static String enCryptByDES(String content,String key) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
        // 算法
        String algorithm = "DES";
        // 调用cipher中的静态算法getInstance 传入具体的一个算法 如DES AES MD5 RSA..等
        Cipher cipher = Cipher.getInstance(algorithm);
        // 秘钥特征 加密规则
        // 第一个参数key的字节
        // 第二个参数表示加密算法
        Key keySpec = new SecretKeySpec(key.getBytes(), algorithm);
        // 调用cipher init方法初始化cipher对象
        // 参数1 传入模式 如Cipher.ENCRYPT_MODE 加密模式 Cipher.DECRYPT_MODE 其他模式参考Cipher类中定义的静态常量
        cipher.init(Cipher.ENCRYPT_MODE, keySpec);
        // 调用cipher的的doFinal方法传入明文 对明文数据进行加密
        byte[] secretePassword = cipher.doFinal(content.getBytes());
        // 因为加密出来的字节数据直接打印会乱码 所以需要进行Base64编码输出成能识别的字符串
        String encode = Base64.encode(secretePassword);
        System.out.println(encode);
        return encode;
    }
}

AES

AES是DES的加强 要求key的位数必须是16位

// 代码跟DES几乎一样
public class AESDemo {

    public static void main(String[] args) throws Exception {
        String content = "测试AES";
        String key = "1234567812345678";

        String secret = enCryptByAES(content, key);
        String result = deCryptByAES(secret, key);
        System.out.println(result.equals(content));
    }

    private static String enCryptByAES(String content, String key) throws Exception {
        String enCryptMode = "AES";
        String algorithm = "AES";
        Cipher cipher = Cipher.getInstance(enCryptMode);
        Key keySpec = new SecretKeySpec(key.getBytes(), algorithm);
        cipher.init(Cipher.ENCRYPT_MODE, keySpec);
        byte[] secret = cipher.doFinal(content.getBytes());
        String encode = Base64.encode(secret);
        System.out.println(encode);
        return encode;
    }

    private static String deCryptByAES(String secret, String key) throws Exception {
        String enCryptMode = "AES";
        String algorithm = "AES";
        Cipher cipher = Cipher.getInstance(enCryptMode);
        Key keySpec = new SecretKeySpec(key.getBytes(), algorithm);
        cipher.init(Cipher.DECRYPT_MODE, keySpec);
        byte[] content = cipher.doFinal(Base64.decode(secret));
        String result = new String(content);
        System.out.println(result);
        return result;
    }
}

加密模式

  • EBC Electronic codebook, 电子密码本. 需要加密的消息按照块密码的块大小被分为数个块,并对每个块进行独立加密
    • 优点 : 可以并行处理数据
    • 缺点 : 同样的原文生成同样的密文, 不能很好的保护数据
    • 同时加密,原文是一样的,加密出来的密文也是一样
  • CBC Cipher-block chaining, 密码块链接. 每个明文块先与前一个密文块进行异或后,再进行加密。在这种方法中,每个密文块都依赖于它前面的所有明文块 CBC中存在一个iv向量,每次加密会根据上一次的iv向量不同生成不同的密文
    • 优点 : 同样的原文生成的密文不一样
    • 缺点 : 串行处理数据.

填充模式 padding

NoPadding

  • 不填充.
  • 在DES加密算法下, 要求原文长度必须是8byte的整数倍
  • 在AES加密算法下, 要求原文长度必须是16byte的整数倍

PKCS5Padding

数据块的大小为8位, 不够就补足

  • 默认情况下, 加密模式和填充模式为 : ECB/PKCS5Padding
  • 如果使用CBC模式, 在初始化Cipher对象时, 需要增加参数, 初始化向量IV : IvParameterSpec iv = new IvParameterSpec(key.getBytes());

加密算法和填充模式组合

AES/CBC/NoPadding (128)
AES/CBC/PKCS5Padding (128)
AES/ECB/NoPadding (128)
AES/ECB/PKCS5Padding (128)
DES/CBC/NoPadding (56)
DES/CBC/PKCS5Padding (56)
DES/ECB/NoPadding (56)
DES/ECB/PKCS5Padding (56)
DESede/CBC/NoPadding (168)
DESede/CBC/PKCS5Padding (168)
DESede/ECB/NoPadding (168)
DESede/ECB/PKCS5Padding (168)
RSA/ECB/PKCS1Padding (1024, 2048)
RSA/ECB/OAEPWithSHA-1AndMGF1Padding (1024, 2048)
RSA/ECB/OAEPWithSHA-256AndMGF1Padding (1024, 2048)

加密模式演示

public class EnCryptModeDemo {

    public static void main(String[] args) throws Exception {
        String content = "测试加密模式";
        String key = "12345678";
        String algorithm = "DES";
//        String mode = "DES/ECB/PKCS5Padding";
        String mode = "DES/CBC/PKCS5Padding";
        String secret = enCrypt(content, key, mode, algorithm);
        String result = deCrypt(secret, key, mode, algorithm);
        System.out.println(result.equals(content));
    }

    private static String deCrypt(String secret, String key, String mode, String algorithm) throws Exception {
        Cipher cipher = Cipher.getInstance(mode);
        Key keySpec = new SecretKeySpec(key.getBytes(),algorithm);
        // 如果模式是CBC需要添加iv向量
        if(mode.contains("CBC")){
            IvParameterSpec iv = new IvParameterSpec(key.getBytes());
            cipher.init(Cipher.DECRYPT_MODE,keySpec,iv);
        }else{
            cipher.init(Cipher.DECRYPT_MODE,keySpec);
        }
        String result = new String(cipher.doFinal(Base64.decode(secret)));
        System.out.println(result);
        return result;
    }

    private static String enCrypt(String content, String key, String mode, String algorithm) throws Exception {
        Cipher cipher = Cipher.getInstance(mode);
        Key keySpec = new SecretKeySpec(key.getBytes(),algorithm);
        // 如果模式是CBC需要添加iv向量
        if(mode.contains("CBC")){
            IvParameterSpec iv = new IvParameterSpec(key.getBytes());
            cipher.init(Cipher.ENCRYPT_MODE,keySpec,iv);
        }else{
            cipher.init(Cipher.ENCRYPT_MODE,keySpec);
        }
        String secret = Base64.encode(cipher.doFinal(content.getBytes()));
        System.out.println(secret);
        return secret;
    }
}

非对称加密

非对称加密是现在加密算法,采用公钥public key 和私钥 private key 两把秘钥。公钥私钥是成对出现,如果公钥对数据加密则需要私钥解密,反之私钥加密必须使用公钥解密。常见的非对称加密算法有RSA ECC

特点

  • 加密和解密使用不同的密钥
  • 如果使用私钥加密, 只能使用公钥解密
  • 如果使用公钥加密, 只能使用私钥解密
  • 处理数据的速度较慢, 因为安全级别高

代码示例

使用RSA生成秘钥对并保存到文件

 public static void main(String[] args) throws Exception {
        String algorithm = "RSA";
        String[] keyPair = genKeyPair(algorithm);
        System.out.println(keyPair[0]);
        System.out.println(keyPair[1]);
     	// 随便网上找个工具即可 我这里使用的hutool 只用appache common也可以
        FileUtil.writeString(keyPair[0],"./pub.key", Charset.forName("UTF-8"));
        FileUtil.writeString(keyPair[1],"./pri.key", Charset.forName("UTF-8"));
    }

    private static String[] genKeyPair(String algorithm) throws Exception{
        // 使用KeyPairGenerator 生成器创建非对称加密 传入算法
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
        // 调用genKeyPair 生成秘钥对
        KeyPair keyPair = keyPairGenerator.genKeyPair();
        // getPrivate 获取私钥 getPublic 获取公钥
        PrivateKey privateKey = keyPair.getPrivate();
        PublicKey publicKey = keyPair.getPublic();
        // getEncoded获取byte数组 使用Base64进行可读性处理
        String pub = Base64.encode(publicKey.getEncoded());
        String pri = Base64.encode(privateKey.getEncoded());
        return new String[]{pub,pri};
    }

使用RSA加解密示例

public class RSADemo {

    public static void main(String[] args) throws Exception {
        String algorithm = "RSA";
        String pubKeyPath = "./pub.key";
        String priKeyPath = "./pri.key";
//        String[] keyPair = genKeyPair(algorithm);
//        System.out.println(keyPair[0]);
//        System.out.println(keyPair[1]);
//        FileUtil.writeString(keyPair[0], pubKeyPath, Charset.forName("UTF-8"));
//        FileUtil.writeString(keyPair[1], priKeyPath, Charset.forName("UTF-8"));

        String content = "测试RSA";
        String pubKey = FileUtil.readString(pubKeyPath, Charset.forName("UTF-8"));
        String priKey = FileUtil.readString(priKeyPath, Charset.forName("UTF-8"));

        String secretContent = enCryptByRSAPrivateKey(content, priKey, algorithm);
        System.out.println(secretContent);
        String result = deCryptByRSAPublicKey(secretContent, pubKey, algorithm);
        System.out.println(result);
        System.out.println(result.equals(content));
    }

    private static String[] genKeyPair(String algorithm) throws Exception {
        // 使用KeyPairGenerator 生成器创建非对称加密 传入算法
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
        // 调用genKeyPair 生成秘钥对
        KeyPair keyPair = keyPairGenerator.genKeyPair();
        // getPrivate 获取私钥 getPublic 获取公钥
        PrivateKey privateKey = keyPair.getPrivate();
        PublicKey publicKey = keyPair.getPublic();
        // getEncoded获取byte数组 使用Base64进行可读性处理
        String pub = Base64.encode(publicKey.getEncoded());
        String pri = Base64.encode(privateKey.getEncoded());
        return new String[]{pub, pri};
    }

    private static String enCryptByRSAPrivateKey(String content, String key, String algorithm) throws Exception {
        Cipher cipher = Cipher.getInstance(algorithm);
        // 统一密钥规范 进行Base64解码 秘钥使用PKCS8EncodedKeySpec类生成 KeySpec
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.decode(key));
        // 使用KeyFactory生成秘钥对方法
        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
        // 生成私钥
        PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        byte[] bytes = cipher.doFinal(content.getBytes());
        return Base64.encode(bytes);
    }

    private static String deCryptByRSAPublicKey(String secretContent, String key, String algorithm) throws Exception {
        Cipher cipher = Cipher.getInstance(algorithm);
        // 统一密钥规范 进行Base64解码 公钥使用X509EncodedKeySpec生成 keySpec
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.decode(key));
        // 使用KeyFactory生成秘钥对方法
        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
        //生成公钥
        PublicKey publicKey = keyFactory.generatePublic(keySpec);
        // 初始化cipher 传入keySpec
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        byte[] result = cipher.doFinal(Base64.decode(secretContent.getBytes()));
        return new String(result);
    }
}

数字签名

阮一峰老师对数字签名和数字证书理解的分享,我认为非常形象的解释了什么是数字证书,什么是签名,什么是CA,如何生成数字证书和数字签名。

数字签名(又称公钥数字签名)是只有信息的发送者才能产生的别人无法伪造的一段数字串,这段数字串同时也是对信息的发送者发送信息真实性的一个有效证明。它是一种类似写在纸上的普通的物理签名,但是使用了公钥加密领域的技术来实现的,用于鉴别数字信息的方法。一套数字签名通常定义两种互补的运算,一个用于签名,另一个用于验证。数字签名是非对称密钥加密技术与数字摘要技术的应用。

数字签名是什么

代码演示

public class SignatureDemo {

    public static void main(String[] args) throws Exception {
        String content = "hello Jim";
        // 非对称加密算法
        String algorithm = "RSA";
        String pubKeyPath = "./pub.key";
        String priKeyPath = "./pri.key";
        // 获取公钥和私钥
        PublicKey publicKey = getPublicKey(pubKeyPath, algorithm);
        PrivateKey privateKey = getPrivateKey(priKeyPath, algorithm);
        // 数字签名的算法 这里选择sha256作为数字摘要算法结合rsa非对称加密算法最终确定数字签名算法
        String signatureAlgorithm = "sha256withrsa";
        // 获取数字签名
        String signature = getSignature(content, signatureAlgorithm, privateKey);
        System.out.println(signature);
        // 签证数字签名是否如何
        System.out.println(verifySignature(content, signature, signatureAlgorithm, publicKey));

    }

    /**
     * 校验签名是否合法
     * @param content 原文内容
     * @param signatureData 数字签名数据
     * @param signatureAlgorithm 签名算法
     * @param publicKey 公钥
     * @return
     * @throws Exception
     */
    private static Boolean verifySignature(String content, String signatureData, String signatureAlgorithm, PublicKey publicKey) throws Exception {
        Signature signature = Signature.getInstance(signatureAlgorithm);
        // 初始化校验
        signature.initVerify(publicKey);
        // 传入得到的原文
        signature.update(content.getBytes());
        // 如果返回boolean 说明是没有被篡改过的原文,如果返回false 说明被篡改过或者不是正确的发送者
        return signature.verify(Base64.decode(signatureData));
    }

    /**
     * 生成数字签名
     *
     * @param content 需生成签名的原文
     * @param signatureAlgorithm 数字签名算法
     * @param privateKey 私钥
     * @return
     * @throws Exception
     */
    private static String getSignature(String content, String signatureAlgorithm, PrivateKey privateKey) throws Exception {
        // 调用JDK 的Signature类获取签名工具方法类
        Signature signature = Signature.getInstance(signatureAlgorithm);
        // 通过私钥初始化数字签名
        signature.initSign(privateKey);
        // 传入原文
        signature.update(content.getBytes());
        // 签名
        byte[] sign = signature.sign();
        // 将签名数据base64可视化
        return Base64.encode(sign);
    }

    /**
     * 获取公钥
     *
     * @param publicKeyPath 公钥路径
     * @param algorithm 算法
     * @return
     * @throws Exception
     */
    public static PublicKey getPublicKey(String publicKeyPath, String algorithm) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
        String publicKeyStr = FileUtil.readString(publicKeyPath, Charset.defaultCharset());
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.decode(publicKeyStr));
        return keyFactory.generatePublic(keySpec);
    }

    /**
     * 获取私钥
     *
     * @param privateKeyPath 私钥路径
     * @param algorithm 算法
     * @return
     * @throws Exception
     */
    public static PrivateKey getPrivateKey(String privateKeyPath, String algorithm) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
        String privateKeyStr = FileUtil.readString(privateKeyPath, Charset.defaultCharset());
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.decode(privateKeyStr));
        return keyFactory.generatePrivate(keySpec);
    }
}

补充

所谓Base64,即是说在编码过程中使用了64种字符:大写A到Z、小写a到z、数字0到9、“+”和“/”

Base64原理
1.base64将3个字节分成一组,每个字节8位,形成24位之后,然后将3个字节转成4组,每组6位,组成新的数据,对于不足3个字节的后面补0处理。这样的好处在于生成新的6位数据转十进制之后一定会落在0-63之间,这样就对应了base64码表上的64个字符
2.将新生成的6位的数据转成十进制得到Base64码表的索引,通过索引找到转换之后的字符
对于不足6位的在后面补零,得到一个新的二进制
对于不如3个字节的部分以=代替 
如字符a只有一个字节 
1.先获取a的ASCII码表值为97
2.97转二进制‭为01100001 
3.每6位分一组不足补0 011000 ‬010000
4.转十进制 24 16 
5.查询base64码表YQ但是a只有一个字节不出3个字节剩余2个字节用==补全
6.的带YQ==

如Man
转ASCII码 77 97 110
‭转二进制 01001101‬ ‭01100001‬ ‭01101110‬
按6位分割 刚好分成4位 010011 01‬0110 0001‬01 101110
转10进制 19 22 5 46
查询base64码表 T W F u

你可能感兴趣的:(安全)