JAVA加解密算法
总体分类
主要分为国密和国际算法
国密算法,即国家商用密码算法,如SM系列的密码,常用的国密密码有:
SM1算法的加密强度与AES相当,但算法未公开,仅以IP核的形式存在于芯片中,调用该算法时,需要通过加密芯片的接口进行调用。
SM4算法,是无线局域网标准的分组数据算法,其加密流程介绍请阅读:SM4分组密码算法
SM7算法未公开,适用于非接IC卡应用包括身份识别类应用(门禁卡、工作证、参赛证),票务类应用(大型赛事门票、展会门票),支付与通卡类应用(积分消费卡、校园一卡通、企业一卡通、公交一卡通)。
国密算法
国密算法主要包括以下几类:
SM2 :基于椭圆曲线密码学(ECC),用于公钥加密和签名。SM2的密钥长度为256位,适用于移动设备、物联网终端等场景12。
SM3 :哈希算法,用于生成数据的哈希值。SM3的输出长度为256位,适用于需要高安全性的场景12。
SM4 :对称加密算法,类似于AES,但密钥长度和轮数不同。SM4的密钥长度为128位,适用于国产化合规场景如政务和金融12。
SM1 :分组密码算法,主要用于无线局域网产品加密3。
祖冲之密码算法(ZUC) :流密码算法,用于移动通信和物联网终端3。
国际算法
国际算法主要包括以下几类:
AES :高级加密标准,分组密码算法,密钥长度可选128、192、256位,适用于高吞吐量场景如互联网和云计算13。
RSA :非对称加密算法,用于公钥加密和签名。RSA的密钥长度可选2048、3072、4096位,适用于传统PKI体系如SSL证书12。
3DES :三重DES算法,通过三次DES运算提高安全性,但效率较低4。
SHA-256 :哈希算法,用于生成数据的哈希值,输出长度为256位12。
分组加密
分组加密(块加密)— Block Cyper:将明文分组成块,一次加密一个明文分组块。
从国密算法中可以知道,分组加密是对128比特的明文输入和128比特的加密密钥进行处理,生成128比特的密文输出,即分组加密算法只能加密固定长度的明文分组块。
但实际情况往往是明文的长度会远超过密码分组的长度,因此需要对分组加密算法进行迭代,才能将明文全部加密,采用的迭代方法就是分组加密算法的加密工作模式。
国密与国际算法区别
1. 对称加密:SM4 vs AES
2. 非对称加密:SM2 vs RSA/ECC
3. 哈希算法:SM3 vs SHA-256
4.应用场景建议
1. 算法种类
单向加密
对称加密
非对称加密
1.1 单向加密
1) Base64
Base64编码一般用于url的处理,或者说任何你不想让普通人一眼就知道是啥的东西都可以用Base64编码处理后再发布在网络上。
package com.amuro.strategy.base64;
import java.util.Base64;
import com.amuro.strategy.IStrategy;
/**
* Base64算法基于64个基本字符,加密后的string中只包含这64个字符
* @author Amuro
*
*/
public class Base64Strategy implements IStrategy
{
public String encode(String src)
{
byte[] encodeBytes = Base64.getEncoder().encode(src.getBytes());
return new String(encodeBytes);
}
public String decode(String src)
{
byte[] decodeBytes = Base64.getDecoder().decode(src.getBytes());
return new String(decodeBytes);
}
}2) MD5
一般用于确保信息的传输完整一致性,校验传输的数据是否被修改,一旦原始信息被修改,生成的 MD5 值将会变得很不同
package com.amuro.strategy.message_digest;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.apache.commons.codec.binary.Hex;
import com.amuro.strategy.IStrategy;
/**
* 消息摘要算法
* @author Amuro
*
*/
public class MD5Strategy implements IStrategy
{
public String encode(String src)
{
try
{
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] encodeBytes = md.digest(src.getBytes());
return Hex.encodeHexString(encodeBytes);
}
catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
}
return null;
}
public String decode(String src)
{
throw new RuntimeException("MD5 no decode");
}
}3) SHA 家族
是一个密码散列函数家族,是 FIPS 所认证的安全散列算法
和 MD5 类似,都是对文本进行散列,产生一定长度的散列值
package com.amuro.strategy.message_digest;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.apache.commons.codec.binary.Hex;
import com.amuro.strategy.IStrategy;
/**
* 安全散列算法
* @author Amuro
*
*/
public class SHAStrategy implements IStrategy
{
public String encode(String src)
{
try
{
MessageDigest md = MessageDigest.getInstance("SHA");
md.update(src.getBytes());
return Hex.encodeHexString(md.digest());
}
catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
}
return null;
}
public String decode(String src)
{
throw new RuntimeException("SHA no decode");
}
}1.2 对称加密
对称加密的意思就是信息收发都有相同的一把钥匙,消息的加密解密都用这进行
1)DES
Data Encryption Standard,数据加密标准,速度较快,适用于加密大量数据的场合。
package com.amuro.strategy.des;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import org.apache.commons.codec.binary.Hex;
import com.amuro.strategy.IStrategy;
/**
*
* @author Amuro
*
*/
public class DESStrategy implements IStrategy
{
private Cipher cipher;
private SecretKey generateKey;
public String encode(String src)
{
try
{ //8字节加密
KeyGenerator keyGenerator = KeyGenerator.getInstance("DES");
keyGenerator.init(56);//size
SecretKey secretKey = keyGenerator.generateKey();
byte[] keyBytes = secretKey.getEncoded();
DESKeySpec desKeySpec = new DESKeySpec(keyBytes);
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("DES");
generateKey = secretKeyFactory.generateSecret(desKeySpec);
cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, generateKey);
byte[] resultBytes = cipher.doFinal(src.getBytes());
return Hex.encodeHexString(resultBytes);
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
public String decode(String src)
{
try
{
cipher.init(Cipher.DECRYPT_MODE, generateKey);
byte[] result = Hex.decodeHex(src.toCharArray());
return new String(cipher.doFinal(result));
}
catch(Exception e)
{
e.printStackTrace();
}
return null;
}
}3DES,也就是“Triple DES”,中文名“三重数据加密算法”,它相当于是对每个数据块应用三次 DES 加密算法。由于计算机运算能力的增强,原版 DES 密码的密钥长度变得容易被暴力破解;3DES 即是设计用来提供一种相对简单的方法,即通过增加 DES 的密钥长度来避免类似的攻击,而不是设计一种全新的块密码算法。
package com.amuro.strategy.des;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import org.apache.commons.codec.binary.Hex;
import com.amuro.strategy.IStrategy;
public class _3DESStrategy implements IStrategy
{
private Cipher cipher;
private SecretKey generateKey;
public String encode(String src)
{
try
{ //16字节加密
KeyGenerator keyGenerator = KeyGenerator.getInstance("DESede");
keyGenerator.init(168);//size
SecretKey secretKey = keyGenerator.generateKey();
byte[] keyBytes = secretKey.getEncoded();
DESedeKeySpec desKeySpec = new DESedeKeySpec(keyBytes);
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("DESede");
generateKey = secretKeyFactory.generateSecret(desKeySpec);
cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, generateKey);
byte[] resultBytes = cipher.doFinal(src.getBytes());
return Hex.encodeHexString(resultBytes);
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
public String decode(String src)
{
try
{
cipher.init(Cipher.DECRYPT_MODE, generateKey);
byte[] result = Hex.decodeHex(src.toCharArray());
return new String(cipher.doFinal(result));
}
catch(Exception e)
{
e.printStackTrace();
}
return null;
}
}2)AES
Advanced Encryption Standard,高级加密标准,是下一代的加密算法标准,速度快,安全级别高;
package com.amuro.strategy.des;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Hex;
import com.amuro.strategy.IStrategy;
public class AESStrategy implements IStrategy
{
private Cipher cipher;
private SecretKey generateKey;
public String encode(String src)
{
try
{
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128);//size
SecretKey secretKey = keyGenerator.generateKey();
byte[] keyBytes = secretKey.getEncoded();
generateKey = new SecretKeySpec(keyBytes, "AES");
cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, generateKey);
byte[] resultBytes = cipher.doFinal(src.getBytes());
return Hex.encodeHexString(resultBytes);
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
public String decode(String src)
{
try
{
cipher.init(Cipher.DECRYPT_MODE, generateKey);
byte[] result = Hex.decodeHex(src.toCharArray());
return new String(cipher.doFinal(result));
}
catch(Exception e)
{
e.printStackTrace();
}
return null;
}
}1.3 非对称加密
非对称加密算法是一种密钥的保密方法。 非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥(privatekey)。 公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。
1)RSA
名称来源于发明这个算法的三个人的姓氏组成,算法大致内容就是对极大整数进行因式分解
这种算法非常可靠,密钥越长,它就越难破解。根据已经披露的文献,目前被破解的最长 RSA密钥是768个二进制位。也就是说,长度超过768位的密钥,还无法破解(至少没人公开宣布)。因此可以认为,1024位的RSA密钥基本安全,2048位的密钥极其安全
package com.amuro.strategy.asymmetric;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.Cipher;
import org.apache.commons.codec.binary.Hex;
import com.amuro.strategy.IStrategy;
public class RSAStrategy implements IStrategy
{
private RSAPublicKey rsaPublicKey;
private RSAPrivateKey rsaPrivateKey;
public String encode(String src)
{
try
{
//初始化密钥
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(512);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
rsaPublicKey = (RSAPublicKey)keyPair.getPublic();
rsaPrivateKey = (RSAPrivateKey)keyPair.getPrivate();
//私钥加密 公钥解密
PKCS8EncodedKeySpec pkcs8EncodedKeySpec
= new PKCS8EncodedKeySpec(rsaPrivateKey.getEncoded());
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] resultBytes = cipher.doFinal(src.getBytes());
//私钥解密 公钥加密
// X509EncodedKeySpec x509EncodedKeySpec =
// new X509EncodedKeySpec(rsaPublicKey.getEncoded());
// KeyFactory keyFactory = KeyFactory.getInstance("RSA");
// PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
// Cipher cipher = Cipher.getInstance("RSA");
// cipher.init(Cipher.ENCRYPT_MODE, publicKey);
// byte[] resultBytes = cipher.doFinal(src.getBytes());
return Hex.encodeHexString(resultBytes);
}
catch(Exception e)
{
e.printStackTrace();
}
return null;
}
public String decode(String src)
{
try
{
//私钥加密 公钥解密
X509EncodedKeySpec x509EncodedKeySpec =
new X509EncodedKeySpec(rsaPublicKey.getEncoded());
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, publicKey);
byte[] resultBytes = cipher.doFinal(Hex.decodeHex(src.toCharArray()));
//私钥解密 公钥加密
// PKCS8EncodedKeySpec pkcs8EncodedKeySpec
// = new PKCS8EncodedKeySpec(rsaPrivateKey.getEncoded());
// KeyFactory keyFactory = KeyFactory.getInstance("RSA");
// PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
// Cipher cipher = Cipher.getInstance("RSA");
// cipher.init(Cipher.DECRYPT_MODE, privateKey);
// byte[] resultBytes = cipher.doFinal(Hex.decodeHex(src.toCharArray()));
return new String(resultBytes);
}
catch(Exception e)
{
e.printStackTrace();
}
return null;
}
}2)DH
DH,全称为“Diffie-Hellman”,他是一种确保共享KEY安全穿越不安全网络的方法,也就是常说的密钥一致协议。由公开密钥密码体制的奠基人Diffie和Hellman所提出的一种思想。简单的说就是允许两名用户在公开媒体上交换信息以生成“一致”的、可以共享的密钥。也就是由甲方产出一对密钥(公钥、私钥),乙方依照甲方公钥产生乙方密钥对(公钥、私钥)。
以此为基线,作为数据传输保密基础,同时双方使用同一种对称加密算法构建本地密钥(SecretKey)对数据加密。这样,在互通了本地密钥(SecretKey)算法后,甲乙双方公开自己的公钥,使用对方的公钥和刚才产生的私钥加密数据,同时可以使用对方的公钥和自己的私钥对数据解密。不单单是甲乙双方两方,可以扩展为多方共享数据通讯,这样就完成了网络交互数据的安全通讯!
package com.amuro.strategy.asymmetric;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.Objects;
import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
import javax.crypto.SecretKey;
import javax.crypto.interfaces.DHPublicKey;
import javax.crypto.spec.DHParameterSpec;
import org.apache.commons.codec.binary.Hex;
import com.amuro.strategy.IStrategy;
public class DHStrategy implements IStrategy
{
private Cipher cipher;
private SecretKey receiverSecretKey;
public String encode(String src)
{
try
{
//初始化发送方密钥
KeyPairGenerator senderKeyPairGenerator = KeyPairGenerator.getInstance("DH");
senderKeyPairGenerator.initialize(512);
KeyPair senderkeyPair = senderKeyPairGenerator.generateKeyPair();
PrivateKey senderPrivateKey = senderkeyPair.getPrivate();
byte[] senderPublicKeyBytes = senderkeyPair.getPublic().getEncoded();//发送方的公钥
//初始化接收方密钥,用发送方的公钥
KeyFactory receiverKeyFactory = KeyFactory.getInstance("DH");
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(senderPublicKeyBytes);
PublicKey receiverPublicKey = receiverKeyFactory.generatePublic(x509EncodedKeySpec);
DHParameterSpec dhParameterSpec =
((DHPublicKey)receiverPublicKey).getParams();
KeyPairGenerator receiverKeyPairGenerator = KeyPairGenerator.getInstance("DH");
receiverKeyPairGenerator.initialize(dhParameterSpec);
KeyPair receiverKeyPair = receiverKeyPairGenerator.generateKeyPair();
PrivateKey receiverPrivateKey = receiverKeyPair.getPrivate();
byte[] receiverPublicKeyBytes = receiverKeyPair.getPublic().getEncoded();
KeyAgreement receiverKeyAgreement = KeyAgreement.getInstance("DH");
receiverKeyAgreement.init(receiverPrivateKey);
receiverKeyAgreement.doPhase(receiverPublicKey, true);
receiverSecretKey = receiverKeyAgreement.generateSecret("DES");
//发送方拿到接收方的public key就可以做加密了
KeyFactory senderKeyFactory = KeyFactory.getInstance("DH");
x509EncodedKeySpec = new X509EncodedKeySpec(receiverPublicKeyBytes);
PublicKey senderPublicKey = senderKeyFactory.generatePublic(x509EncodedKeySpec);
KeyAgreement senderKeyAgreement = KeyAgreement.getInstance("DH");
senderKeyAgreement.init(senderPrivateKey);
senderKeyAgreement.doPhase(senderPublicKey, true);
SecretKey senderSecretKey = senderKeyAgreement.generateSecret("DES");
if(Objects.equals(receiverSecretKey, senderSecretKey))
{
cipher = Cipher.getInstance("DES");
cipher.init(Cipher.ENCRYPT_MODE, senderSecretKey);
byte[] result = cipher.doFinal(src.getBytes());
return Hex.encodeHexString(result);
}
}
catch(Exception e)
{
e.printStackTrace();
}
return null;
}
public String decode(String src)
{
try
{
cipher.init(Cipher.DECRYPT_MODE, receiverSecretKey);
byte[] result = Hex.decodeHex(src.toCharArray());
return new String(cipher.doFinal(result));
}
catch(Exception e)
{
e.printStackTrace();
}
return null;
}
}2)数字证书签名
package com.amuro.strategy.signature;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
public class RSASign
{
public static boolean verifySign(String src)
{
try
{
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(512);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
PublicKey rsaPublicKey = (RSAPublicKey)keyPair.getPublic();
PrivateKey rsaPrivateKey = (RSAPrivateKey)keyPair.getPrivate();
PKCS8EncodedKeySpec pkcs8EncodedKeySpec
= new PKCS8EncodedKeySpec(rsaPrivateKey.getEncoded());
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
Signature signature = Signature.getInstance("MD5withRSA");
signature.initSign(privateKey);
signature.update(src.getBytes());
//生成签名bytes
byte[] signBytes = signature.sign();
X509EncodedKeySpec x509EncodedKeySpec =
new X509EncodedKeySpec(rsaPublicKey.getEncoded());
keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
signature = Signature.getInstance("MD5withRSA");
signature.initVerify(publicKey);
signature.update(src.getBytes());
boolean isVerified = signature.verify(signBytes);
return isVerified;
}
catch(Exception e)
{
e.printStackTrace();
}
return false;
}
}