Bootstrap

Java实现加密算法

一、常用的加密算法及简略介绍

1、常用加密算法

Base64编码

严格来说只是一种编码方式,并不算是加密算法
Base64 编码是从二进制到字符的过程,用 64 个字符来表示任意的二进制数据,常用于在 HTTP 加密,图片编码传输等

MD5信息摘要算法

Message Digest algorithm 5
一般用于确保信息的传输完整一致性,校验传输的数据是否被修改,一旦原始信息被修改,生成的 MD5 值将会变得很不同

SHA密码散列函数家族

FIPS 所认证的安全散列算法,和 MD5 类似,都是对文本进行散列,产生一定长度的散列值

HMAC散列消息鉴别码

Hash Message Authentication Code
是一种通过特别计算方式之后产生的消息认证码(MAC),使用密码散列函数,同时结合一个加密密钥。它可以用来保证数据的完整性,同时可以用来作某个消息的身份验证。

2、对称加密

对称加密的意思就是信息收发都有相同的一把钥匙,消息的加密解密都用这把钥匙进行。

1、DES
Data Encryption Standard,数据加密标准,速度较快,适用于加密大量数据的场合。
2、3DES
DES的升级版,原理和DES一样,需要处理三个密钥
3、AES
Advanced Encryption Standard,高级加密标准,是下一代的加密算法标准,速度快,安全级别高;

3、非对称加密

非对称加密算法是一种密钥的保密方法。
非对称加密算法需要两个密钥:
公开密钥(publickey)和私有密钥(privatekey)。
公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。

1、RSA
名称来源于发明这个算法的三个人的姓氏组成,算法大致内容就是对极大整数进行因式分解
这种算法非常可靠,密钥越长,它就越难破解。根据已经披露的文献,目前被破解的最长 RSA密钥是768个二进制位。也就是说,长度超过768位的密钥,还无法破解(至少没人公开宣布)。因此可以认为,1024位的RSA密钥基本安全,2048位的密钥极其安全。
2、DSA
Digital Signature Algorithm,数字签名算法,是一种标准的 DSS(数字签名标准);
3、ECC
Elliptic Curves Cryptography,椭圆曲线密码编码学。
一种建立公开密钥加密的算法,基于椭圆曲线数学。
ECC的主要优势是在某些情况下它比其他的方法使用更小的密钥——比如RSA加密算法——提供相当的或更高等级的安全。ECC的另一个优势是可以定义群之间的双线性映射,基于Weil对或是Tate对;双线性映射已经在密码学中发现了大量的应用,例如基于身份的加密。不过一个缺点是加密和解密操作的实现比其他机制花费的时间长。

总结

现在的加密算法大部分情况下是为了验证数据的一致性,例如传递一些参数组的时候,简单的会使用 BASE64 或 MD5 进行加密生成一个签名。复杂点就是 BASE64 编码之后再用 对称密钥再加密一次,达到比较不容易被人篡改的目的
对于一些支付场景,一般使用非对称加密算法 实现,这样的场景需要的安全性更高。

二、简单介绍算法示例

1、Base64

    public static void main(String[] args) throws Exception {
        String data = "即将加密的数据";
		//base64加密(方式1)
        String encData1 = Base64.encodeBase64String(data.getBytes());
        //base64加密(方式2)
        String encData2 = (new BASE64Encoder()).encodeBuffer(data.getBytes());
        System.out.println("encData1 = "+ encData1);
        System.out.println("encData2 = "+ encData2);
        
        //base64解密(方式1)
        byte[] decData1 = Base64.decodeBase64(encData1);
        byte[] decData2 = Base64.decodeBase64(encData2);
        //base64解密(方式2)
        byte[] decData3 = (new BASE64Decoder()).decodeBuffer(encData1);
        byte[] decData4 = (new BASE64Decoder()).decodeBuffer(encData2);
        System.out.println("decData1 = "+ new String(decData1) );
        System.out.println("decData2 = "+ new String(decData2) );
        System.out.println("decData3 = "+ new String(decData3) );
        System.out.println("decData4 = "+ new String(decData4) );
    }

2、MD5

    public static void main(String[] args) throws Exception {
        String data = "即将加密的数据";
		// 拿到一个MD5转换器
        MessageDigest md5 = MessageDigest.getInstance("MD5");
        md5.update(data.getBytes());
        BigInteger encData = new BigInteger(md5.digest());
        //加密之后转换为16进制字符串
        System.out.println("encData = "+ encData.toString(16));
    }

3、SHA

    public static void main(String[] args) throws Exception {
        String data = "即将加密的数据";
		// 拿到一个SHA转换器
        MessageDigest sha = MessageDigest.getInstance("SHA");
        sha.update(data.getBytes());
        BigInteger encData = new BigInteger(sha.digest());
        //加密之后转换为32进制字符串
        System.out.println("encData = "+ encData.toString(32));
    }

4、HMAC


	/**
     * MAC算法可选以下多种算法
     * <pre>
     * HmacMD5
     * HmacSHA1
     * HmacSHA256
     * HmacSHA384
     * HmacSHA512
     * </pre>
     */
    public static final String KEY_MAC = "HmacMD5";
    
    public static void main(String[] args) throws Exception {
        String data = "即将加密的数据";
        String key = "给定密钥";
        //根据给定的字节数组构造一个密钥,第二参数指定一个密钥算法的名称
		SecretKey secretKey = new SecretKeySpec(key.getBytes(), KEY_MAC);
        //getInstance得到实例
        Mac mac = Mac.getInstance(secretKey.getAlgorithm());
        //用给定密钥初始化 Mac 对象
        mac.init(secretKey);
        // 加密处理
        BigInteger macMd5 = new BigInteger(mac.doFinal(data.getBytes()))
        System.out.println("HMAC:" + macMd5.toString(16));
    }

5、对称加密

在对称加密算法中,加密与解密的密钥是相同的。
128 192 256 分别表示密钥的长度。

填充方式:
1、NoPadding
  不进行填充,但是这里要求明文必须要是16个字节的整数倍,如果一定要用 NoPadding 的话,那么必须保证原文字节是 8 的倍数,否则的话需要使用其他的填充模式。
2、PKCS5Padding(默认)
  在明文的末尾进行填充,填充的数据是当前和16个字节相差的数量,例如:

  • 未填充明文
    1,2,3,4,5,6,7,8,9,10,11
  • 填充明文(缺少五个满足16个字节)
    1,2,3,4,5,6,7,8,9,10,11,5,5,5,5,5

注:使用PKCS7Padding/PKCS5Padding填充时,最后一个字节肯定为填充数据的长度,所以在解密后可以准确删除填充的数据
3、 ISO10126Padding
   在明文的末尾进行填充,当前和16个字节相差的数量填写在最后,其余字节填充随机数,例如:

  • 未填充明文
    1,2,3,4,5,6,7,8,9,10,11
  • 填充明文(缺少五个满足16个字节)
    1,2,3,4,5,6,7,8,9,10,11,c,b,4,1,5

模式
  模式是需要制定AES对明文进行加密时使用的模式,一共有五种模式,模式的基本原理是相似的,但是细节上会有一些变化。

  • ECB模式(默认):电码本模式
  • CBC模式:密码分组链接模式
  • CFB模式:密码反馈模式
  • OFB模式:输出反馈模式
  • CTR模式:计算器模式

如果想详细了解内部原理可以参考:
https://www.cnblogs.com/better-farther-world2099/p/13293291.html
https://blog.csdn.net/qq_28205153/article/details/55798628

1、AES-128-ECB

    public static void main(String[] args) throws Exception {
        /*
         * 此处使用AES-128-ECB加密模式。
         */
        String key = "很随便的密钥";
        // 需要加密的字串
        String data = "即将加密的数据";
        // 加密
        String encData = encrypt(data, key);
        System.out.println("加密后的字串是:" + encData);
        // 解密
        String decData = decrypt(encData, key);
        System.out.println("解密后的字串是:" + decData);
    }
    // 加密
    public static String encrypt(String data, String key) throws Exception {
    	//创建AES的Key生产者
	   	KeyGenerator kgen = KeyGenerator.getInstance("AES");
	   	// 利用"很随便的密钥"作为随机数初始化生成一个128字节16位的密钥
	   	kgen.init(128, new SecureRandom(key.getBytes()));
	   	//SecureRandom是生成安全随机数序列
	   	SecretKey secretKey = kgen.generateKey();
	   	byte[] rKey = secretKey.getEncoded();
	   	// 转换为AES专用密钥
        SecretKeySpec skeySpec = new SecretKeySpec(rKey, "AES");
        // 创建密码器
        //"算法/模式/补码方式"
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        // 初始化为加密模式的密码器
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
        byte[] encrypted = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
        //此处使用BASE64做转码功能,同时能起到2次加密的作用。
        return new BASE64Encoder().encode(encrypted);
    }
    // 解密
    public static String decrypt(String encData, String key) throws Exception {
        //创建AES的Key生产者
	   	KeyGenerator kgen = KeyGenerator.getInstance("AES");
	   	// 利用"很随便的密钥"作为随机数初始化生成一个128字节16位的密钥
	   	kgen.init(128, new SecureRandom(key.getBytes()));
	   	//SecureRandom是生成安全随机数序列
	   	SecretKey secretKey = kgen.generateKey();
	   	byte[] rKey = secretKey.getEncoded();
        SecretKeySpec skeySpec = new SecretKeySpec(rKey, "AES");
        // 创建密码器
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        // 初始化为解密模式的密码器
        cipher.init(Cipher.DECRYPT_MODE, skeySpec);
        //先用base64解密
        byte[] encDataByte = cipher.doFinal(new BASE64Decoder().decodeBuffer(encData));
        String decData = new String(encDataByte,StandardCharsets.UTF_8);
        return decData;
    }

2、AES-128-CBC

使用CBC模式,需要填充一个向量,这里我直接用key来代替。

    public static void main(String[] args) throws Exception {
        /*
         * 此处使用AES-128-CBC加密模式。
         */
        String key = "很随便的密钥";
        // 需要加密的字串
        String data = "即将加密的数据";
        // 加密
        String encData = encrypt(data, key);
        System.out.println("加密后的字串是:" + encData);
        // 解密
        String decData = decrypt(encData, key);
        System.out.println("解密后的字串是:" + decData);
    }
    // 加密
    public static String encrypt(String data, String key) throws Exception {
        //创建AES的Key生产者
        KeyGenerator kgen = KeyGenerator.getInstance("AES");
        // 利用"很随便的密钥"作为随机数初始化生成一个128字节16位的密钥
        kgen.init(128, new SecureRandom(key.getBytes()));
        //SecureRandom是生成安全随机数序列
        SecretKey secretKey = kgen.generateKey();
        byte[] rKey = secretKey.getEncoded();
        // 转换为AES专用密钥
        SecretKeySpec skeySpec = new SecretKeySpec(rKey, "AES");
        // 创建密码器
        //"算法/模式/补码方式"
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        //使用CBC模式,需要一个向量iv,可增加加密算法的强度
        IvParameterSpec ips = new IvParameterSpec(rKey);
        // 初始化为加密模式的密码器
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec,ips);
        byte[] encrypted = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
        //此处使用BASE64做转码功能,同时能起到2次加密的作用。
        return new BASE64Encoder().encode(encrypted);
    }
    // 解密
    public static String decrypt(String encData, String key) throws Exception {
        //创建AES的Key生产者
        KeyGenerator kgen = KeyGenerator.getInstance("AES");
        // 利用"很随便的密钥"作为随机数初始化生成一个128字节16位的密钥
        kgen.init(128, new SecureRandom(key.getBytes()));
        //SecureRandom是生成安全随机数序列
        SecretKey secretKey = kgen.generateKey();
        byte[] rKey = secretKey.getEncoded();
        SecretKeySpec skeySpec = new SecretKeySpec(rKey, "AES");
        // 创建密码器
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");//使用CBC模式,需要一个向量iv,可增加加密算法的强度
        IvParameterSpec ips = new IvParameterSpec(rKey);
        // 初始化为解密模式的密码器
        cipher.init(Cipher.DECRYPT_MODE, skeySpec,ips);
        //先用base64解密
        byte[] encDataByte = cipher.doFinal(new BASE64Decoder().decodeBuffer(encData));
        String decData = new String(encDataByte,StandardCharsets.UTF_8);
        return decData;
    }

6、非对称加密

RSA:

1、私钥加密,公钥解密

	private static final String DATA = "要加密的数据";
    private static String rsaPublicKey = null;
    private static String rsaPrivateKey = null;

    public static void main(String[] args) throws Exception {
        //构建密钥对
        createEnck();
        //私钥加密
        String result = encByPrivateKey(rsaPrivateKey);
        //公钥解密
        decByPublicKey(rsaPublicKey,result);
    }
    //构建密钥对,保存公私钥
    private static void createEnck() throws Exception {
        KeyPairGenerator senderKeyPairGenerator = KeyPairGenerator.getInstance("RSA");
        senderKeyPairGenerator.initialize(512);
        KeyPair keyPair = senderKeyPairGenerator.generateKeyPair();
        //生成公钥
        RSAPublicKey rsaPublicKey1 = (RSAPublicKey) keyPair.getPublic();
        //生成私钥
        RSAPrivateKey rsaPrivateKey1 = (RSAPrivateKey) keyPair.getPrivate();
        //这里也可以不用base64加密,看自己的需求
        rsaPublicKey = Base64.encodeBase64String(rsaPublicKey1.getEncoded());
        rsaPrivateKey = Base64.encodeBase64String(rsaPrivateKey1.getEncoded());

        System.out.println("rsaPublicKey = "+rsaPublicKey);
        System.out.println("rsaPrivateKey = "+rsaPrivateKey);
    }
    /**
     * 私钥加密
     */
    private static String encByPrivateKey(String rsaPrivateKey) throws Exception {
        // 在构建密钥对时,对密钥做了base64编码,这里需要先解码
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec((new BASE64Decoder()).decodeBuffer(rsaPrivateKey));
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
        //创建Cipher对象。
        Cipher cipher = Cipher.getInstance("RSA");
        //初始化为加密模式的密码器
        //用密钥初始化 Cipher
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        //加密
        byte[] bytes = cipher.doFinal(DATA.getBytes());
        String result = Base64.encodeBase64String(bytes);
        System.out.println("私钥加密: " + result);
        return result;
    }

    /**
     * 公钥解密
     */
    private static void decByPublicKey(String rsaPublicKey,String result) throws Exception {
        // 这里也需要对公钥进行解码
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec((new BASE64Decoder()).decodeBuffer(rsaPublicKey));
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        byte[] bytes = cipher.doFinal(Base64.decodeBase64(result));
        System.out.println("公钥解密: " + new String(bytes));
    }

2、公钥加密,私钥解密

	private static final String DATA = "要加密的数据";
    private static String rsaPublicKey = null;
    private static String rsaPrivateKey = null;

    public static void main(String[] args) throws Exception {
        //构建密钥对
        createEnck();
        //加密
        String result = encByPublicKey(rsaPublicKey);
        //解密
        decByPrivateKey(rsaPrivateKey,result);
    }
    //构建密钥对,保存公私钥
    private static void createEnck() throws Exception {
        KeyPairGenerator senderKeyPairGenerator = KeyPairGenerator.getInstance("RSA");
        senderKeyPairGenerator.initialize(512);
        KeyPair keyPair = senderKeyPairGenerator.generateKeyPair();
        //生成公钥
        RSAPublicKey rsaPublicKey1 = (RSAPublicKey) keyPair.getPublic();
        //生成私钥
        RSAPrivateKey rsaPrivateKey1 = (RSAPrivateKey) keyPair.getPrivate();
        rsaPublicKey = Base64.encodeBase64String(rsaPublicKey1.getEncoded());
        rsaPrivateKey = Base64.encodeBase64String(rsaPrivateKey1.getEncoded());

        System.out.println("rsaPublicKey = "+rsaPublicKey);
        System.out.println("rsaPrivateKey = "+rsaPrivateKey);
    }
    /**
     * 公钥加密
     */
    private static String encByPublicKey(String rsaPublicKey) throws Exception {
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec((new BASE64Decoder()).decodeBuffer(rsaPublicKey));
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] bytes = cipher.doFinal(DATA.getBytes());
        String result = Base64.encodeBase64String(bytes);
        System.out.println("公钥加密: " + result);
        return result;
    }

    /**
     * 私钥解密
     */
    private static void decByPrivateKey(String rsaPrivateKey,String result) throws Exception {
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec((new BASE64Decoder()).decodeBuffer(rsaPrivateKey));
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] bytes = cipher.doFinal(Base64.decodeBase64(result));
        System.out.println("私钥解密解密:" + new String(bytes));
    }
;