Java常用加解密工具

140 阅读3分钟

Java常用加解密工具

这篇主要给大家分享几个常用的加解密工具,包括对称加密 AES 和对称加密 DES,还有非对称加密 RSA,希望对大家有用。

对称加密 AES

高级加密标准(英语:Advanced Encryption Standard,缩写:AES),在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。 这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。经过五年的甄选流程,高级加密标准由美国国家标准与技术研究院(NIST)于2001年11月26日发布于FIPS PUB 197,并在2002年5月26日成为有效的标准。 2006年,高级加密标准已然成为对称密钥加密中最流行的算法之一。 采用对称分组密码体制,密钥的长度最少支持为128、192、256位;加密分组长度128位,如果数据块及密钥长度不足时,会补齐进行加密。

public class AESUtil {

  private static final Logger logger = LoggerFactory.getLogger(AESUtil.class);


  private static final String ALGORITHM = "AES";

  // 默认密码算法
  private static final String TRANSFORMATION = "AES/CBC/PKCS5Padding";

  /**
   * 生成指定长度的 key
   * @param keySize 用来指定 key 的长度
   * @return
   */
  public static byte[] createKey(int keySize) {
    try {
      KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM);
      keyGenerator.init(keySize);
      return keyGenerator.generateKey().getEncoded();
    } catch (NoSuchAlgorithmException e) {
      logger.error("AES密钥/向量生成异常_" + e.getMessage(), e);
      return null;
    }
  }

  /**
   * AES 加密
   * @param source 需要加密的字符串,要加密的明文
   * @param key 密钥,AES固定格式为128/256 bits.即:16/32bytes,即加密内容应该为 16字节的倍数。DES固定格式为128bits,即8bytes。
   * @param iv 偏移量,AES 为 16bytes. DES 为 8bytes
   * @return 返回加密后的字节数组,可以选择编码。主要编解码方式有Base64, HEX, UUE,7bit等等。此处看服务器需要什么编码方式
   * 所以也可以这样返回:return new BASE64Encoder().encode(encryptedData); 返回Base64转码后的加密数据
   */
  public static String enc(byte[] source, byte[] key, byte[] iv) {
    try {
      Cipher cipher = Cipher.getInstance(TRANSFORMATION);
      cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, ALGORITHM), new IvParameterSpec(iv));
      byte[] encryptedData = cipher.doFinal(source);
      return Base64Util.encodeBytesToString(encryptedData);
    } catch (Exception e) {
      logger.error("AES加密异常_" + e.getMessage(), e);
      return null;
    }
  }

  /**
   * AES解密
   * @param source 已加密的密文
   * @param key 密钥,AES固定格式为128/256 bits.即:16/32bytes,即加密内容应该为 16字节的倍数。DES固定格式为128bits,即8bytes。
   * @param iv iv 偏移量,AES 为 16bytes. DES 为 8bytes
   * @return 返回解密后的明文
   */
  public static String dec(byte[] source, byte[] key, byte[] iv) {
    try {
      Cipher cipher = Cipher.getInstance(TRANSFORMATION);
      cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, ALGORITHM), new IvParameterSpec(iv));
      byte[] result = cipher.doFinal(source);
      return new String(result, StandardCharsets.UTF_8);
    } catch (Exception e) {
      logger.error("AES解密异常_" + e.getMessage(), e);
      return null;
    }
  }
}

使用案例

public class CryptoTest {

  /**
   * 测试 AES 可逆加密
   */
  @Test
  public void testAES(){
    // 测试 AES 加密
    byte[] aesKey = AESUtil.createKey(256);
    byte[] aesIv = AESUtil.createKey(128);
    Map<String, String> user = new HashMap<>();
    user.put("name","张三");
    user.put("age","21");
    user.put("phone","182****6871");

    String encryptData = AESUtil.enc(JSON.toJSON(user).getBytes(), aesKey, aesIv);
    System.out.println("encrypt:" + encryptData);
    // encrypt:n3el9w1sXguCKcZmoSTCLf6VRWDwPQZ58/1FasPyNP2pPl1jfPE135QdzryfduDH+NDaJFqQeD9tLkSVupOMRDosgppJR0Nbypq27EiXiMM=

    testAESTransportWithBase64(encryptData, aesKey, aesIv);

  }

  /**
   * 测试 AES 密钥及 IV 使用 base64 编码传输,并解密
   * @param encryptData
   * @param aesKey
   * @param aesIv
   */
  public static void testAESTransportWithBase64(String encryptData, byte[] aesKey, byte[] aesIv){
    // 测试 AES 传输
    // 我们的密文已经经过了 Base64 编码,可以传输了,但是 aesKey 和 aesIv 还待处理,提供两种处理方式

    // (1)base64 编码 aesKey 和 aesIv
    String aesKeyStr = Base64Util.encodeBytesToString(aesKey);
    String aesIvStr = Base64Util.encodeBytesToString(aesIv);

    // 测试 AES 解密
    // 因为密文经过了 Base64 编码,所以需要 Base64 解码
    byte[] encryptedData = Base64Util.decodeStringToBytes(encryptData);
    byte[] sessionKey = Base64Util.decodeStringToBytes(aesKeyStr);
    byte[] iv = Base64Util.decodeStringToBytes(aesIvStr);

    String decryptData = AESUtil.dec(encryptedData, sessionKey, iv);
    JSONObject decryptInfo = JSON.parse(decryptData, JSONObject.class);
    System.out.println(decryptInfo.toString());
    // {"phone":"182****6871","name":"张三","age":"21"}
  }
}

对称加密 DES

public class DESUtils {

    private static final String INSTANCE_STR = "DES";

    public static String encrypt(byte[] datasource, String password) {
        try {
            SecureRandom random = new SecureRandom();
            DESKeySpec desKey = new DESKeySpec(password.getBytes());
            // 创建一个密匙工厂,然后用它把DESKeySpec转换成
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(INSTANCE_STR);
            SecretKey secureKey = keyFactory.generateSecret(desKey);
            // Cipher对象实际完成加密操作
            Cipher cipher = Cipher.getInstance(INSTANCE_STR);
            // 用密匙初始化Cipher对象
            cipher.init(Cipher.ENCRYPT_MODE, secureKey, random);
            // 现在,获取数据并加密
            // 正式执行加密操作
            byte[] bytes = cipher.doFinal(datasource);
            return Base64.getEncoder().encodeToString(bytes);
        } catch (Throwable e) {
            throw new RuntimeException("encrypt error", e);
        }
    }

    public static String decrypt(String source, String password) {
        try {
            byte[] src = Base64.getDecoder().decode(source);

            // DES算法要求有一个可信任的随机数源
            SecureRandom random = new SecureRandom();
            // 创建一个DESKeySpec对象
            DESKeySpec desKey = new DESKeySpec(password.getBytes());
            // 创建一个密匙工厂
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(INSTANCE_STR);
            // 将DESKeySpec对象转换成SecretKey对象
            SecretKey secureKey = keyFactory.generateSecret(desKey);
            // Cipher对象实际完成解密操作
            Cipher cipher = Cipher.getInstance(INSTANCE_STR);
            // 用密匙初始化Cipher对象
            cipher.init(Cipher.DECRYPT_MODE, secureKey, random);
            // 真正开始解密操作
            byte[] bytes = cipher.doFinal(src);
            return new String(bytes, StandardCharsets.UTF_8);
        } catch (InvalidKeyException | NoSuchAlgorithmException | InvalidKeySpecException | NoSuchPaddingException |
                 IllegalBlockSizeException | BadPaddingException e) {
            throw new RuntimeException("解密失败",e);
        }
    }

    private DESUtils() {
        // no instance
    }
}

测试

public class CryptoTest {

  private static final String SECRET_KEY = "4bP5T8UG7hxNMnkJa%Kr4%bSz7!yFA&k";

  @Test
  public void testDES(){
    Map<String, String> user = new HashMap<>();
    user.put("name","李四");
    user.put("age","21");
    user.put("phone","182****6871");

    String encryptData = DESUtils.encrypt(JSON.toJSON(user).getBytes(), SECRET_KEY);
    System.out.println("encrypt:" + encryptData);
    //encrypt:fRqG1ADBOv0XZmfgp21xfP809NklJi/73EMMj5YB5holalroe0hepaUnwcRM3cjSSyuQ6nABvAIOpmttz2Ie/ADlTwKqDqa8

    String decrypt = DESUtils.decrypt(encryptData, SECRET_KEY);
    System.out.println("decrypt:" + decrypt);
    // decrypt:{
    //  "phone" : "182****6871",
    //  "name" : "李四",
    //  "age" : "21"
    //}
  }
}

非对称加密 RSA

RSA算法广泛应用于加密与认证两个领域

1.加密(保证数据安全性) 使用公钥加密,需使用私钥解密。 这种广泛应用在保证数据的安全性的方面,用户将自己的公钥广播出去,所有人给该用户发数据时使用该公钥加密,但是只有该用户可以使用自己的私钥解密,保证了数据的安全性。

2.认证(用于身份判断) 使用私钥签名,需使用公钥验证签名。 用户同样将自己的公钥广播出去,给别人发送数据时,使用私钥加密,在这里,我们更乐意称它为签名,然后别人用公钥验证签名,如果解密成功,则可以判断对方的身份。

/**
 * 密钥对
 * private=私钥, public=公钥
 */
public class RSAKey {

    private String publicKey;

    private String privateKey;

    public RSAKey(String publicKey, String privateKey) {
        this.publicKey = publicKey;
        this.privateKey = privateKey;
    }

    public String getPublicKey() {
        return publicKey;
    }

    public String getPrivateKey() {
        return privateKey;
    }
}

以下是工具方法

import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

@Slf4j
public class RSAUtil {

  private static final String ALGORITHM = "RSA/ECB/PKCS1Padding";

  private static final String KEY_ALGORITHM = "RSA";

  /**
   * 生成密钥对
   * @param keySize the keySize. This is an algorithm-specific metric, such as modulus length, specified innumber of bits
   * @return 返回公钥和私钥对
   */
  public static RSAKey generateKeyPair(int keySize) {
    try {
      SecureRandom sr = new SecureRandom();
      KeyPairGenerator kpg = KeyPairGenerator.getInstance(KEY_ALGORITHM);
      kpg.initialize(keySize, sr);
      KeyPair kp = kpg.generateKeyPair();
      Key publicKey = kp.getPublic();
      byte[] publicKeyBytes = publicKey.getEncoded();
      String pub = Base64.getEncoder().encodeToString(publicKeyBytes);
      Key privateKey = kp.getPrivate();
      byte[] privateKeyBytes = privateKey.getEncoded();
      String pri = Base64.getEncoder().encodeToString(privateKeyBytes);
      return new RSAKey(pub, pri);
    } catch (Exception e) {
      e.printStackTrace();
      return null;
    }
  }

  /**
   * RSA公钥加密
   * @param source 内容
   * @param key 公钥(PublicKey) 或者 私钥(PrivateKey)
   * @return 加密后内容, 可以选择编码, 这里返回Base64编码后的加密数据
   */
  public static String enc(byte[] source, Key key) {
    try {
      Cipher cipher = Cipher.getInstance(ALGORITHM);
      cipher.init(Cipher.ENCRYPT_MODE, key);
      byte[] encryptedData = cipher.doFinal(source);
      return Base64Util.encodeBytesToString(encryptedData);
    } catch (Exception e) {
      e.printStackTrace();
      return null;
    }
  }

  /**
   * RSA私钥解密
   * @param source 已加密内容
   * @param key 私钥(PrivateKey) 或者 公钥(PublicKey)
   * @return 解密后内容
   */
  public static String dec(byte[] source, Key key) {
    try {
      Cipher cipher = Cipher.getInstance(ALGORITHM);
      cipher.init(Cipher.DECRYPT_MODE, key);
      byte[] result = cipher.doFinal(source);
      return new String(result, StandardCharsets.UTF_8);
    } catch (Exception e) {
      e.printStackTrace();
      return null;
    }
  }

  /**
   * 根据公钥字符串获取 公钥对象
   * @param key 公钥字符串
   * @return 公钥对象
   */
  public static PublicKey getPublicKey(String key) {
    try {
      X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(key));
      KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
      return keyFactory.generatePublic(keySpec);
    } catch (Exception e) {
      e.printStackTrace();
      return null;
    }
  }

  /**
   * 根据私钥字符串获取 私钥对象
   * @param key 私钥字符串
   * @return 私钥对象
   */
  public static PrivateKey getPrivateKey(String key) {
    try {
      PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(key));
      KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
      return keyFactory.generatePrivate(keySpec);
    } catch (Exception e) {
      e.printStackTrace();
      return null;
    }
  }
}

注意上面方法中的加密解密方法 如果是使用公钥加密则需要私钥进行解密,如果是使用私钥加密则需要公钥进行解密

测试

public class CryptoTest {

    /**
     * 测试 RSA,数据加密(如果是使用公钥加密则需要私钥进行解密)
     */
    @Test
    public void testRSA(){
      try {
        RSAKey rsaKey = RSAUtil.generateKeyPair(1024);
        String publicKey = rsaKey.getPublicKey();
        String privateKey = rsaKey.getPrivateKey();
        System.out.println("公钥:" + publicKey);
        System.out.println("私钥:" + privateKey);
        //公钥:MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxBdePukjrUG7Co8iqaQCLSh57fNQsAqcAVUT1RGJrcJ0KBzyswhCCvcMqXi08ATSCYfL3NAp3LoJoc8OaTeKtpsryIsl5L4AmQoqY5BF6bWHsCtbLiHtvZYGuSVkJrlGekoq3VUepi8WYil+hM4/uhH+TVO+9Y4JItpG8mfynnQIDAQAB
        //私钥:MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALEF14+6SOtQbsKjyKppAItKHnt81CwCpwBVRPVEYmtwnQoHPKzCEIK9wypeLTwBNIJh8vc0Cncugmhzw5pN4q2myvIiyXkvgCZCipjkEXptYewK1suIe29lga5JWQmuUZ6SirdVR6mLxZiKX6Ezj+6Ef5NU771jgki2kbyZ/KedAgMBAAECgYB07a/yPxEVZf6TUI4mic8TMnUqCk03eNUIskonW/FKmIsSaa9ZSaKQSXoVjTmBziX6DWrogJZ9HNqE1hqY3ONhxnLuIevBrVlnyPLv4FWj03AgPi+JvUqG09Q39k0uf2+ZElTfO1t3KriFsz8Il9vCOxGX94dOdXJtyUD7Mw6x4QJBAPZdXPvKuDWKIa8tdz+10Kl5WTtkezllXJg8iu1B35oBu7zSWcPshywb3yOp3/lmiBWl2owu1zpWfwSX43Ap/DkCQQC38jfw1/lPYnCL8ZXKz+QRgkeJ00mmrE+2FoJukG1GB0Af4n9hl60S0NRN37Ow93tQwaiY+Yp8yT7s2sTWbY6FAkEAvS8YtioHpuV51GGfjwb5QwryYM9aaMrTffwP2v2YzVTDa85ELFW3v0Fv6p0KZyQ7IBSKbNeTYzMEuuxANOOo0QJAG+HdVOaH5d6YDL5r5Dgq6/SBf4TFbzxDQOickMHXrnu+3pZolJR3KZj59WvpQvPgf1c7hSSeN1gR77khiRdGOQJAeEMT7Q3EyxudtIwKM41HenrxEr6etwfeMXvk6wMXH6g7McJ67mlqViTg4Ob3dl11tIYOhvF23ZENFKJTmE9HoA==

        Map<String, String> user = new HashMap<>();
        user.put("name","张三");
        user.put("age","21");
        user.put("phone","182****6871");

        // RSA 加密,公钥加密
        String encryptData = RSAUtil.enc(JSON.toJSON(user).getBytes(StandardCharsets.UTF_8), RSAUtil.getPublicKey(publicKey));
        System.out.println("encryptData:" + encryptData);
        //encryptData:FI44nR80K9x5nlAR3q/KodgOpmo36AgBex3s/e2pvhF03oB4n2zBstWmhV8qe7ezI1Avp389KqtZ/L9YefETik/QqW2hDuuT06Kt2YbpDmOC5q6FFCsbIhLd8x2HUq/7Nq6EJcyL2oPofWi1YPKzRKBWXzITm+H+Snxwygd0tQ4=

        // 因为我们的密文经过 Base64 编码,所以需要 Base64 解码, 编码的原因是方便传输
        byte [] b = Base64.getDecoder().decode(encryptData);

        //RSA 解密,私钥解密
        String decryptData = RSAUtil.dec(b, RSAUtil.getPrivateKey(privateKey));
        JSONObject decryptInfo = JSON.parse(decryptData, JSONObject.class);
        System.out.println("decryptData:" + decryptInfo.toString());
        // decryptData:{"phone":"182****6871","name":"张三","age":"21"}

      } catch (Exception e) {
        e.printStackTrace();
      }
    }


    /**
     * 测试 RSA,身份认证(如果是使用私钥加密则需要公钥进行解密)
     */
    public void testRSA1(){
      try {
        RSAKey rsaKey = RSAUtil.generateKeyPair(1024);
        String publicKey = rsaKey.getPublicKey();
        String privateKey = rsaKey.getPrivateKey();
        System.out.println("公钥:" + publicKey);
        System.out.println("私钥:" + privateKey);
        //公钥:MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxBdePukjrUG7Co8iqaQCLSh57fNQsAqcAVUT1RGJrcJ0KBzyswhCCvcMqXi08ATSCYfL3NAp3LoJoc8OaTeKtpsryIsl5L4AmQoqY5BF6bWHsCtbLiHtvZYGuSVkJrlGekoq3VUepi8WYil+hM4/uhH+TVO+9Y4JItpG8mfynnQIDAQAB
        //私钥:MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALEF14+6SOtQbsKjyKppAItKHnt81CwCpwBVRPVEYmtwnQoHPKzCEIK9wypeLTwBNIJh8vc0Cncugmhzw5pN4q2myvIiyXkvgCZCipjkEXptYewK1suIe29lga5JWQmuUZ6SirdVR6mLxZiKX6Ezj+6Ef5NU771jgki2kbyZ/KedAgMBAAECgYB07a/yPxEVZf6TUI4mic8TMnUqCk03eNUIskonW/FKmIsSaa9ZSaKQSXoVjTmBziX6DWrogJZ9HNqE1hqY3ONhxnLuIevBrVlnyPLv4FWj03AgPi+JvUqG09Q39k0uf2+ZElTfO1t3KriFsz8Il9vCOxGX94dOdXJtyUD7Mw6x4QJBAPZdXPvKuDWKIa8tdz+10Kl5WTtkezllXJg8iu1B35oBu7zSWcPshywb3yOp3/lmiBWl2owu1zpWfwSX43Ap/DkCQQC38jfw1/lPYnCL8ZXKz+QRgkeJ00mmrE+2FoJukG1GB0Af4n9hl60S0NRN37Ow93tQwaiY+Yp8yT7s2sTWbY6FAkEAvS8YtioHpuV51GGfjwb5QwryYM9aaMrTffwP2v2YzVTDa85ELFW3v0Fv6p0KZyQ7IBSKbNeTYzMEuuxANOOo0QJAG+HdVOaH5d6YDL5r5Dgq6/SBf4TFbzxDQOickMHXrnu+3pZolJR3KZj59WvpQvPgf1c7hSSeN1gR77khiRdGOQJAeEMT7Q3EyxudtIwKM41HenrxEr6etwfeMXvk6wMXH6g7McJ67mlqViTg4Ob3dl11tIYOhvF23ZENFKJTmE9HoA==

        Map<String, String> user = new HashMap<>();
        user.put("name","张三");
        user.put("age","21");
        user.put("phone","182****6871");

        // RSA 加密,私钥加密
        String encryptData = RSAUtil.enc(JSON.toJSON(user).getBytes(StandardCharsets.UTF_8), RSAUtil.getPrivateKey(privateKey));
        System.out.println("encryptData:" + encryptData);
        //encryptData:FI44nR80K9x5nlAR3q/KodgOpmo36AgBex3s/e2pvhF03oB4n2zBstWmhV8qe7ezI1Avp389KqtZ/L9YefETik/QqW2hDuuT06Kt2YbpDmOC5q6FFCsbIhLd8x2HUq/7Nq6EJcyL2oPofWi1YPKzRKBWXzITm+H+Snxwygd0tQ4=

        // 因为我们的密文经过 Base64 编码,所以需要 Base64 解码, 编码的原因是方便传输
        byte [] b = Base64.getDecoder().decode(encryptData);

        //RSA 解密,公钥解密
        String decryptData = RSAUtil.dec(b, RSAUtil.getPublicKey(publicKey));
        JSONObject decryptInfo = JSON.parse(decryptData, JSONObject.class);
        System.out.println("decryptData:" + decryptInfo.toString());
        // decryptData:{"phone":"182****6871","name":"张三","age":"21"}

      } catch (Exception e) {
        e.printStackTrace();
      }
    }
}

链接