一、对称加密与非对称加密

.net加密 java aes解密 java加密与解密_System

1、对称加密

对称加密采用了对称密码编码技术,它的特点是文件加密和解密使用相同的密钥加密,这种方法在密码学中叫做对称加密算法,对称加密算法使用起来简单快捷,密钥较短,且破译困难,除了数据加密标准(DES),另一个对称密钥加密系统是国际数据加密算法(IDEA),它比DES的加密性好,而且对计算机功能要求也没有那么高。

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

2、非对称加密

非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥(privatekey)。
公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。

3、对称加密与非对称加密的区别
  • 1、加密和解密过程不同
    对称加密的加密过程和解密过程使用的同一个密钥,加密过程相当于用原文+密钥可以传输出密文,同时解密过程用密文-密钥可以推导出原文。
    但非对称加密采用了两个密钥,一般使用公钥进行加密,使用私钥进行解密。
  • 2、加密解密速度不同
    对称加密解密的速度比较快,适合数据比较长时的使用。
    非对称加密和解密花费的时间长、速度相对较慢,只适合对少量数据的使用。
  • 3、传输的安全性不同
    对称加密的过程中无法确保密钥被安全传递,密文在传输过程中是可能被第三方截获的,如果密码本也被第三方截获,则传输的密码信息将被第三方破获,安全性相对较低。
    非对称加密算法中私钥是基于不同的算法生成不同的随机数,私钥通过一定的加密算法推导出公钥,但私钥到公钥的推导过程是单向的,也就是说公钥无法反推导出私钥。所以安全性较高。

二、加密算法实例

1、MD5加密单向加密

  • MD5是一种单向加密算法,只能加密不能解密
  • MD5 主要用做数据一致性验证、数字签名和安全访问认证,而不是用作加密。
package com.example.demo.password;

import org.springframework.util.DigestUtils;

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

public class MD5Util {
    /**
     * java自带jar工具 java.security.MessageDigest 实现
     *
     * @param text  要加密的字符串
     * @param rad   进制:比如 16、32
     * @param isUpp 是否转换为大写
     * @return
     */
    public static String strToMD5(String text, int rad, boolean isUpp) {
        MessageDigest messageDigest = null;
        try {
            //通过MessageDigest类来的静态方法getInstance获取MessageDigest对象
            messageDigest = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        // 4、获取明文字符串对应的字节数组
        byte[] input = text.getBytes();
        // 5、执行加密
        messageDigest.update(input);
        //这里也可以直接使用byte[] output = messageDigest.digest(input)方法来进行加密,就省略了上面的update方法了
        byte[] output = messageDigest.digest();
        // 6、创建BigInteger对象
        // signum为1表示正数、-1表示负数、0表示0。不写默认表示正数
        int signum = 1;
        BigInteger bigInteger = new BigInteger(signum, output);
        // 7、按照16进制(或32进制)将bigInteger转为字符串
        return isUpp ? bigInteger.toString(rad).toUpperCase() : bigInteger.toString(rad);
    }

    /**
     * spring自带的工具 org.springframework.util.DigestUtils 实现
     *
     * @param text
     * @return 16进制
     */
    public static String _strToMD5(String text) {
        return DigestUtils.md5DigestAsHex(text.getBytes());
    }

    public static void main(String[] args) {
        String str = "hello";
        System.out.println(strToMD5(str, 16, true));
        System.out.println(_strToMD5(str));
        System.out.println(strToMD5(str, 16, false).equals(_strToMD5(str)));
    }
}

2、Base64加密解密

Java 8 内置了 Base64 编码的编码器和解码器。
Base64工具类提供了一套静态方法获取下面三种BASE64编解码器:

  • 基本:输出被映射到一组字符A-Za-z0-9+/,编码不添加任何行标,输出的解码仅支持A-Za-z0-9+/。
  • URL:输出映射到一组字符A-Za-z0-9+_,输出是URL和文件。
  • MIME:输出隐射到MIME友好格式。输出每行不超过76字符,并且使用’\r’并跟随’\n’作为分割。编码输出最后没有行分割。

static class Base64.Decoder:该类实现一个解码器用于,使用 Base64 编码来解码字节数据。
static class Base64.Encoder:该类实现一个编码器,使用 Base64 编码来编码字节数据。

package com.example.demo.password;

import java.io.UnsupportedEncodingException;
import java.util.Base64;
import java.util.UUID;

public class Base64Util {
    public static void main(String[] args) throws Exception {
        String text = "hello";
        try {
            /**
             * static Base64.Encoder getEncoder()
             * 返回一个 Base64.Encoder ,编码使用基本型 base64 编码方案。
             */
            String base64encodedString = Base64.getEncoder().encodeToString(text.getBytes("utf-8"));
            System.out.println("Base64 编码字符串 (基本) :" + base64encodedString);

            /**
             * static Base64.Decoder getDecoder()
             * 返回一个 Base64.Decoder ,解码使用基本型 base64 编码方案。
             */
            byte[] base64decodedBytes = Base64.getDecoder().decode(base64encodedString);
            System.out.println("原始字符串: " + new String(base64decodedBytes, "utf-8"));

            /**
             * static Base64.Encoder getUrlEncoder()
             * 返回一个 Base64.Encoder ,编码使用 URL 和文件名安全型 base64 编码方案。
             */
            base64encodedString = Base64.getUrlEncoder().encodeToString(text.getBytes("utf-8"));
            System.out.println("Base64 编码字符串 (URL) :" + base64encodedString);

            /**
             * static Base64.Decoder getUrlDecoder()
             * 返回一个 Base64.Decoder ,解码使用 URL 和文件名安全型 base64 编码方案。
             */
            byte[] base64decodedBytes2 = Base64.getUrlDecoder().decode(base64encodedString);
            System.out.println("原始字符串: " + new String(base64decodedBytes2, "utf-8"));

            StringBuilder stringBuilder = new StringBuilder();

            for (int i = 0; i < 10; ++i) {
                stringBuilder.append(UUID.randomUUID().toString());
            }

            byte[] mimeBytes = stringBuilder.toString().getBytes("utf-8");
            String mimeEncodedString = Base64.getMimeEncoder().encodeToString(mimeBytes);
            System.out.println("Base64 编码字符串 (MIME) :" + mimeEncodedString);

        }catch(UnsupportedEncodingException e){
            System.out.println("Error :" + e.getMessage());
        }
    }
}

Base 64主要用途不是加密,而是把一些二进制数转成普通字符,方便在网络上传输。 由于一些二进制字符在传输协议中属于控制字符,不能直接传送,所以需要转换一下才可以。由于某些系统中只能使用ASCII字符,Base64就是用来将非ASCII字符的数据转换成ASCII字符的一种方法,Base64特别适合在http,mime协议下快速传输数据。比如网络中图片的传输。

package com.example.demo.password;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Base64;

public class ImgToBase64String {
    /**
     * 图片转base64
     * @param filePath 图片文件路径
     * @return
     */
    public static String toBase64String(String filePath){
        if (null == filePath || 0 == filePath.length()){
            throw new RuntimeException("图片路径不能为空!!");
        }
        InputStream in = null;
        byte[] data = null;
        try {
            //读取图片字节数组
            in = new FileInputStream(filePath);
            data = new byte[in.available()];
            in.read(data);
            in.close();
        }catch (IOException e){
            e.printStackTrace();
        }
        StringBuilder sb = new StringBuilder();
        sb.append("data:image/jpg;base64,");
        sb.append(Base64.getEncoder().encodeToString(data));
        
        //对字节数组Base64编码,返回Base64编码过的字节数组字符串
        return sb.toString();
    }

    public static void main(String[] args) {
        System.out.println(toBase64String("D:\\下载\\desktop.png"));
    }
}

3、DES对称加密解密

因jdk1.8自带的base64有所不同,使用jdk其他版本是可适当自行修改。

package com.example.demo.password;

import java.security.Key;
import java.security.SecureRandom;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;

public class DESUtil {
    public static Key setKey(String strKey) {
        Key key = null;
        try {
            KeyGenerator generator = KeyGenerator.getInstance("DES");
            generator.init(new SecureRandom(strKey.getBytes())); // 根据参数生成key
            key = generator.generateKey();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return key;
    }

    /**
     * @param source 编码内容
     * @param key 密钥
     * @param charSet 编码格式
     * @return
     */
    public static String encrypt(String source, String key, String charSet) {
        String encrypt = null;
        try {
            byte[] ret = encrypt(source.getBytes(charSet), key);
            encrypt = new String(Base64.getEncoder().encode(ret));
        } catch (Exception e) {
            e.printStackTrace();
            encrypt = null;
        }
        return encrypt;
    }

    /**
     * @param encryptedData 解码内容
     * @param key 密钥
     * @param charSet 编码格式
     * @return
     */
    public static String decrypt(String encryptedData, String key, String charSet) {
        String descryptedData = null;
        try {
            byte[] ret = descrypt(Base64.getDecoder().decode(encryptedData.getBytes()), key);
            descryptedData = new String(ret, charSet);
        } catch (Exception e) {
            e.printStackTrace();
            descryptedData = null;
        }
        return descryptedData;
    }

    private static byte[] encrypt(byte[] primaryData, String key) {
        Key desKey = setKey(key);
        try {
            Cipher cipher = Cipher.getInstance("DES"); // Cipher对象实际完成加密操作
            cipher.init(Cipher.ENCRYPT_MODE, desKey); // 用密钥初始化Cipher对象(加密)
            return cipher.doFinal(primaryData);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    private static byte[] descrypt(byte[] encryptedData, String key) {
        Key desKey = setKey(key);
        try {
            Cipher cipher = Cipher.getInstance("DES"); // Cipher对象实际完成解密操作
            cipher.init(Cipher.DECRYPT_MODE, desKey); // 用密钥初始化Cipher对象(解密)
            return cipher.doFinal(encryptedData);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static void main(String[] args) {
        String code = "hello world";
        String key = "thisisakey";
        String unicode = "utf-8";
        String encrypt = encrypt(code, key, unicode);
        String decrypt = decrypt(encrypt, key, unicode);
        System.out.println("原内容:" + code);
        System.out.println("加密:" + encrypt);
        System.out.println("解密:" + decrypt);
    }
}

4、AES对称加密解密

AES 与 DES 一样,一共有四种加密模式:电子密码本模式(ECB)、加密分组链接模式(CBC)、加密反馈模式(CFB)和输出反馈模式(OFB)。

package com.example.demo.password;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

public class AESUtil {
    //指定为AES算法,不区分大小写
    private static final String KEY_ALGORITHM = "AES";
    private static final String CHARSET_NAME = "utf-8";

    /*
     * 加密 1.构造密钥生成器 2.根据ecnodeRules规则初始化密钥生成器 3.产生密钥 4.创建和初始化密码器 5.内容加密 6.返回字符串
     */
    public static String AESEncode(String encodeRules, String content) {
        try {
            // 1.构造密钥生成器,指定为AES算法,不区分大小写
            KeyGenerator keygen = KeyGenerator.getInstance(KEY_ALGORITHM);
            // 2.根据ecnodeRules规则初始化密钥生成器
            // 生成一个128位的随机源,根据传入的字节数组
            //keygen.init(128, new SecureRandom(encodeRules.getBytes()));
            SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
            secureRandom.setSeed(encodeRules.getBytes());
            keygen.init(128, secureRandom);
            // 3.产生原始对称密钥
            SecretKey original_key = keygen.generateKey();
            // 4.获得原始对称密钥的字节数组
            byte[] raw = original_key.getEncoded();
            // 5.根据字节数组生成AES密钥
            SecretKey key = new SecretKeySpec(raw, KEY_ALGORITHM);
            // 6.根据指定算法AES自成密码器
            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
            // 7.初始化密码器,第一个参数为加密(Encrypt_mode)或者解密解密(Decrypt_mode)操作,第二个参数为使用的KEY
            cipher.init(Cipher.ENCRYPT_MODE, key);
            // 8.获取加密内容的字节数组(这里要设置为utf-8)不然内容中如果有中文和英文混合中文就会解密为乱码
            byte[] byte_encode = content.getBytes(CHARSET_NAME);
            // 9.根据密码器的初始化方式--加密:将数据加密
            byte[] byte_AES = cipher.doFinal(byte_encode);
            // 10.将加密后的数据转换为字符串
            // 这里用Base64Encoder中会找不到包
            // 解决办法:
            // 在项目的Build path中先移除JRE System Library,再添加库JRE System Library,重新编译后就一切正常了。
            String AES_encode = new String(Base64.getEncoder().encodeToString(byte_AES));
            // 11.将字符串返回
            return AES_encode;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        // 如果有错就返加nulll
        return null;
    }

    /*
     * 解密 解密过程: 1.同加密1-4步 2.将加密后的字符串反纺成byte[]数组 3.将加密内容解密
     */
    public static String AESDncode(String encodeRules, String content) {
        try {
            // 1.构造密钥生成器,指定为AES算法,不区分大小写
            KeyGenerator keygen = KeyGenerator.getInstance(KEY_ALGORITHM);
            // 2.根据ecnodeRules规则初始化密钥生成器
            // 生成一个128位的随机源,根据传入的字节数组
            //keygen.init(128, new SecureRandom(encodeRules.getBytes()));
            SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
            secureRandom.setSeed(encodeRules.getBytes());
            keygen.init(128, secureRandom);
            // 3.产生原始对称密钥
            SecretKey original_key = keygen.generateKey();
            // 4.获得原始对称密钥的字节数组
            byte[] raw = original_key.getEncoded();
            // 5.根据字节数组生成AES密钥
            SecretKey key = new SecretKeySpec(raw, KEY_ALGORITHM);
            // 6.根据指定算法AES自成密码器
            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
            // 7.初始化密码器,第一个参数为加密(Encrypt_mode)或者解密(Decrypt_mode)操作,第二个参数为使用的KEY
            cipher.init(Cipher.DECRYPT_MODE, key);
            // 8.将加密并编码后的内容解码成字节数组
            byte[] byte_content = Base64.getDecoder().decode(content);
            /*
             * 解密
             */
            byte[] byte_decode = cipher.doFinal(byte_content);
            String AES_decode = new String(byte_decode, CHARSET_NAME);
            return AES_decode;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        }

        // 如果有错就返加nulll
        return null;
    }

    public static void main(String[] args) {
        String encodeRules = "thisisrule";
        String text = "hello word";
        String encodeText = AESEncode(encodeRules, text);
        System.out.println("加密后的密文是:" + encodeText);
        System.out.println("解密后的内容是:" + AESDncode(encodeRules, encodeText));
    }
}

5、非对称加密——RSA

RSA算法是一种非对称加密算法,RSA 加密主要有这么几步:生成密钥对、公开公钥、公钥加密私钥解密、私钥加密公钥解密。

package com.example.demo.password;

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

import javax.crypto.Cipher;
import java.security.KeyFactory;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

public class RSAUtil {
    private static final String CHARSET_NAME = "utf-8";
    private static final String TYPE = "RSA";

    /**
     * RSA公钥加密
     *
     * @param str       加密字符串
     * @param publicKey 公钥
     * @return 密文
     * @throws Exception 加密过程中的异常信息
     */
    public static String encrypt(String str, String publicKey) throws Exception {
        //base64编码的公钥
        byte[] decoded = Base64.decodeBase64(publicKey);
        RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance(TYPE).generatePublic(new X509EncodedKeySpec(decoded));
        //RSA加密
        Cipher cipher = Cipher.getInstance(TYPE);
        cipher.init(Cipher.ENCRYPT_MODE, pubKey);
        String outStr = Base64.encodeBase64String(cipher.doFinal(str.getBytes(CHARSET_NAME)));
        return outStr;
    }

    /**
     * RSA私钥解密
     *
     * @param str        加密字符串
     * @param privateKey 私钥
     * @return 铭文
     * @throws Exception 解密过程中的异常信息
     */
    public static String decrypt(String str, String privateKey) throws Exception {
        //64位解码加密后的字符串
        byte[] inputByte = Base64.decodeBase64(str.getBytes(CHARSET_NAME));
        //base64编码的私钥
        byte[] decoded = Base64.decodeBase64(privateKey);
        RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance(TYPE).generatePrivate(new PKCS8EncodedKeySpec(decoded));
        //RSA解密
        Cipher cipher = Cipher.getInstance(TYPE);
        cipher.init(Cipher.DECRYPT_MODE, priKey);
        String outStr = new String(cipher.doFinal(inputByte));
        return outStr;
    }

    public static void main(String[] args) throws Exception {
        String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDKalqxfHXrZ0JZdLd2+BLhNS6bZxVyCOXvzFd0xCyX4oX/IbglKp9BGxQYaNl7stlHkQmMYBTAkIj0mAQzOVkqisYDevxKA5Yeitnim8R+N+a1SYaoQCfHLbCmMg4ZP0xaC30rA3DSMbEWQTD7p/g6v3sZevQUDVUcge9oaif9AQIDAQAB";
        String privateKey = "MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAMpqWrF8detnQll0t3b4EuE1LptnFXII5e/MV3TELJfihf8huCUqn0EbFBho2Xuy2UeRCYxgFMCQiPSYBDM5WSqKxgN6/EoDlh6K2eKbxH435rVJhqhAJ8ctsKYyDhk/TFoLfSsDcNIxsRZBMPun+Dq/exl69BQNVRyB72hqJ/0BAgMBAAECgYEAwOZKOArcddKaMJZCoWYY1/bOy9qZXWuNddHPJsAtnzGJcXK5AvJzgqBDrl99o5z15HYcG2MVY85aNn8IwahNh8CpFF8At7O2hVz1sTER2MPeiV5324BGOCPUkT4lY2iT2Dq6hXraZDI9sovit4FnfFlWH9nMOV5ckBvm5ypcCoECQQD/YLgSqXEERuu5qjU+PBCBKqbtOq5+EBNwLb9isxTPjRPoQ98sEzWsbyeLrc73Cjql5vU1io9rsG3IiYdEvFn9AkEAyuiaK95pbILOqK096d3Gt9oz0fqc5K4c9V44anaFzkEOysZlO3o1iZxqjfHdBlJiE4j0fq52+s+L9riqFbNMVQJATL3V0tXUPoLJZ3u8kD0ggJA+pV9S/FL8ZGN69b/26v/sEYoD0IzdPjoQ2iqa3SXXxe8HlNVUj/nuo6qgWYl4SQJBAJsXsYfoh7JeRXHegV15m8O5sDRGl5efkhjmfL67e0kMpy7M+GG+5p8ZhMScYzHK1JZT73XJCr5o13Ws7qyJkMUCQQD2EaVPOG+5M0VbzGGG820NRk9omSwO3GdSf4HZ6JWmeGk0FJN4NfAHnD8nA5L/HP+muqCdYQtYD068oZrNSabP";
        String text = "hello world";
        String encodeText = encrypt(text, publicKey);
        System.out.println("加密后:" + encodeText);
        System.out.println("解密后:" + decrypt(encodeText, privateKey));
    }
}