ikki@github.io:~$

安全算法

安全算法

安全算法概览

安全相关的常见的算法分为2大类:消息摘要算法加密算法

类别 算法 区别
消息摘要算法 MD5, SHA1, SHA256, HmacMD5, HmacSHA1,Hmac512 其中HMAC 是带有秘钥的摘要算法。签名数据,数据本身可读,数据被修改时被发现
加密算法 DES,3DES,AES,RSA 其中 RSA 是非对称加解密算法,即 加密和解密使用的不是同一个秘钥。加密,数据不可读。防止获取

误区

  • 有人经常把 摘要算法 也叫做 加密算法,这个是错误的认知。凡是加密就一定能解密,凡是不能解密的算法都不能叫做加密算法。
  • 有人把Base64 也叫做加密算法,这个也是错误的,Base64 只能叫做编码算法。和 2进制转16进制 的算法本质上没有区别,而且Base64 算法没有秘钥的概念。
  • 非对称秘钥可以互换加解密:公钥不一定是用来加密的, 私钥也不一定是用来解密的。

性能

  • 基于JMH 的加解密性能测试(仅供参考)
Benchmark                       Mode  Cnt      Score     Error  Units
CipherPerformTest.encryptByCBC  avgt   10   5893.295 ± 315.086  ns/op
CipherPerformTest.encryptByCFB  avgt   10   6224.839 ±  28.337  ns/op
CipherPerformTest.encryptByOFB  avgt   10   6275.740 ± 177.735  ns/op

CipherPerformTest.encryptByGCM  avgt   10  12310.963 ± 326.713  ns/op
CipherPerformTest.decryptByGCM  avgt   10  11029.540 ± 236.242  ns/op

CipherPerformTest.encryptRSA  avgt   10  26761.729 ± 639.838  ns/op

  • AES 中 GCM 模式加解密耗时约是其他模式2倍左右

  • 非对称 RSA 加解密耗时约是AES-GCM 2倍多

实现

AES加密

public static byte[] encryptGCM(byte[] secretKey, String algorithm, byte[] text, byte [] iv) {
    SecretKeySpec keySpec = new SecretKeySpec(secretKey, algorithm);
    try {
        Cipher cipher = Cipher.getInstance(GCM_CIPHER_ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, new GCMParameterSpec(128,iv));
        return cipher.doFinal(text);
    } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | BadPaddingException | IllegalBlockSizeException | InvalidAlgorithmParameterException e) {
        log.error(e.getMessage(), e);
    }
    return null;
}

AES 解密

public static byte[] decryptGCM(byte[] secretKey, String algorithm, byte[] text,byte [] iv) {
    SecretKeySpec keySpec = new SecretKeySpec(secretKey, algorithm);
    try {
        Cipher cipher = Cipher.getInstance(GCM_CIPHER_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, keySpec, new GCMParameterSpec(128,iv));
        return cipher.doFinal(text);
    } catch (NoSuchAlgorithmException | IllegalBlockSizeException | BadPaddingException | InvalidKeyException | NoSuchPaddingException | InvalidAlgorithmParameterException e) {
        log.error(e.getMessage(), e);
    }
    return null;
}

RSA 加密

public static byte[] encryptRSA(KeyPair keyPair, String content) {
    Cipher cipher = Cipher.getInstance("RSA");
    cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
    return cipher.doFinal(content.getBytes(StandardCharsets.UTF_8));
}

RSA 解密

public static byte[] decryptRSA(KeyPair keyPair, byte[] cryptedContent) {
    Cipher cipher = Cipher.getInstance("RSA");
    cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate());
    return cipher.doFinal(cryptedContent);
}

恢复秘钥

// 通用读取密钥
PublicKey publicKey =  KeyFactory.getInstance("EC").generatePublic(new X509EncodedKeySpec(Base64.getDecoder().decode(privateKeyStr)));

// 通用读取私钥
PrivateKey privateKey = KeyFactory.getInstance("EC").generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKeyStr)));

// 恢复RSA私钥
RSAPrivateKey privateKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKeyStr)));

// 恢复RSA 公钥
RSAPublicKey publicKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(Base64.getDecoder().decode(privateKeyStr)));

第三方实现

BouncyCastle

  • Bouncycstle 是一个开源的第三方密码包,其中包含了大量的密码算法
  • Bouncycstle 是JCE的实现

添加依赖

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15to18</artifactId>
    <version>1.64</version>
</dependency>
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-ext-jdk15to18</artifactId>
    <version>1.64</version>
</dependency>

添加Provider

  • 想使用BouncyCastle, 仅仅添加依赖是不够的, 因为JVM还是无法找到它
动态添加
Security.addProvider(new BouncyCastleProvider());
  • 该方法可以全局添加,比如:SpringBoot Main方法里
静态添加
  1. 将下载的jar包 添加到 $JAVA_HOME/jre/lib/ext
  2. 修改 $JAVA_HOME/jre/lib/security/java.security, 写入配置 security.provider.2=org.bouncycastle.jce.provider.BouncyCastleProvider