kotlin做網(wǎng)站谷歌瀏覽器下載手機(jī)版
簡述
介紹其三種密碼加密方法
1.SM2加密與驗(yàn)簽
2.隨機(jī)密碼鹽加密
3.MD5加密
推薦使用方法1,其次使用方法2,最不推薦的是方法3。方法3極其容易被密碼字典破解,如果項目進(jìn)行安全測試,通常是不允許的加密方式。
SM2加密與驗(yàn)簽
引入bcprov,以使用SM2加密。
<dependency><groupId>org.bouncycastle</groupId><artifactId>bcprov-jdk15to18</artifactId><version>1.69</version></dependency>
工具類與測試方法
加密的主要工具類如下,其中帶有測試的main方法。
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECParameterSpec;import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Map;/*** sm2算法與簽名的使用** @author fir* @date 2024/7/23 14:22*/
@Slf4j
public class Sm2SignatureUtils {static {Security.addProvider(new BouncyCastleProvider());}public static final String PUBLIC_KEY = "publicKey";public static final String PRIVATE_KEY = "privateKey";/*** 生成國密公私鑰對*/public static Map<String, String> generateSmKey() throws Exception {KeyPairGenerator keyPairGenerator;SecureRandom secureRandom = new SecureRandom();ECGenParameterSpec sm2Spec = new ECGenParameterSpec("sm2p256v1");keyPairGenerator = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());keyPairGenerator.initialize(sm2Spec);keyPairGenerator.initialize(sm2Spec, secureRandom);KeyPair keyPair = keyPairGenerator.generateKeyPair();PrivateKey privateKey = keyPair.getPrivate();PublicKey publicKey = keyPair.getPublic();String publicKeyStr = new String(Base64.getEncoder().encode(publicKey.getEncoded()));String privateKeyStr = new String(Base64.getEncoder().encode(privateKey.getEncoded()));return Map.of(PUBLIC_KEY, publicKeyStr, PRIVATE_KEY, privateKeyStr);}/*** 將Base64轉(zhuǎn)碼的公鑰串,轉(zhuǎn)化為公鑰對象*/public static PublicKey createPublicKey(String publicKey) {PublicKey publickey = null;try {X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKey));KeyFactory keyFactory = KeyFactory.getInstance("EC", new BouncyCastleProvider());publickey = keyFactory.generatePublic(publicKeySpec);} catch (Exception e) {log.error("將Base64轉(zhuǎn)碼的公鑰串,轉(zhuǎn)化為公鑰對象異常:{}", e.getMessage(), e);}return publickey;}/*** 將Base64轉(zhuǎn)碼的私鑰串,轉(zhuǎn)化為私鑰對象*/public static PrivateKey createPrivateKey(String privateKey) {PrivateKey publickey = null;try {PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey));KeyFactory keyFactory = KeyFactory.getInstance("EC", new BouncyCastleProvider());publickey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);} catch (Exception e) {log.error("將Base64轉(zhuǎn)碼的私鑰串,轉(zhuǎn)化為私鑰對象異常:{}", e.getMessage(), e);}return publickey;}/*** 根據(jù)publicKey對原始數(shù)據(jù)data,使用SM2加密*/public static String encrypt(byte[] data, String publicKeyBase64) {PublicKey publicKey = createPublicKey(publicKeyBase64);ECPublicKeyParameters localEcPublicKeyParameters = getEcPublicKeyParameters(publicKey);SM2Engine localSm2Engine = new SM2Engine();localSm2Engine.init(true, new ParametersWithRandom(localEcPublicKeyParameters, new SecureRandom()));byte[] arrayOfByte2;try {arrayOfByte2 = localSm2Engine.processBlock(data, 0, data.length);return Base64.getEncoder().encodeToString(arrayOfByte2);} catch (InvalidCipherTextException e) {log.error("SM2加密失敗:{}", e.getMessage(), e);return null;}}private static ECPublicKeyParameters getEcPublicKeyParameters(PublicKey publicKey) {ECPublicKeyParameters localEcPublicKeyParameters = null;if (publicKey instanceof BCECPublicKey localEcPublicKey) {ECParameterSpec localEcParameterSpec = localEcPublicKey.getParameters();ECDomainParameters localEcDomainParameters = new ECDomainParameters(localEcParameterSpec.getCurve(),localEcParameterSpec.getG(), localEcParameterSpec.getN());localEcPublicKeyParameters = new ECPublicKeyParameters(localEcPublicKey.getQ(), localEcDomainParameters);}return localEcPublicKeyParameters;}/*** 根據(jù)privateKey對加密數(shù)據(jù)encode data,使用SM2解密*/public static String decrypt(String encodeBase64, String privateKeyBase64) {SM2Engine localSm2Engine = new SM2Engine();PrivateKey privateKey = createPrivateKey(privateKeyBase64);BCECPrivateKey sm2PriK = (BCECPrivateKey) privateKey;byte[] encodeData = Base64.getDecoder().decode(encodeBase64);ECParameterSpec localEcParameterSpec = sm2PriK.getParameters();ECDomainParameters localEcDomainParameters = new ECDomainParameters(localEcParameterSpec.getCurve(),localEcParameterSpec.getG(), localEcParameterSpec.getN());ECPrivateKeyParameters localEcPrivateKeyParameters = new ECPrivateKeyParameters(sm2PriK.getD(),localEcDomainParameters);localSm2Engine.init(false, localEcPrivateKeyParameters);try {byte[] result = localSm2Engine.processBlock(encodeData, 0, encodeData.length);return new String(result);} catch (InvalidCipherTextException e) {log.error("SM2解密失敗:{}", e.getMessage(), e);return null;}}/*** 私鑰,數(shù)據(jù),生成簽名*/public static String signByPrivateKey(String dataStr, String privateKeyBase64) throws Exception {PrivateKey privateKey = createPrivateKey(privateKeyBase64);byte[] data = Base64.getDecoder().decode(dataStr);Signature sig = Signature.getInstance(GMObjectIdentifiers.sm2sign_with_sm3.toString(), BouncyCastleProvider.PROVIDER_NAME);sig.initSign(privateKey);sig.update(data);byte[] sign = sig.sign();return Base64.getEncoder().encodeToString(sign);}/*** 公鑰與簽名驗(yàn)證數(shù)據(jù)合法性*/public static boolean verifyByPublicKey(String dataStr, String publicKeyBase64, String signatureBase64) throws Exception {PublicKey publicKey = createPublicKey(publicKeyBase64);byte[] signature = Base64.getDecoder().decode(signatureBase64);byte[] data = Base64.getDecoder().decode(dataStr);Signature sig = Signature.getInstance(GMObjectIdentifiers.sm2sign_with_sm3.toString(), BouncyCastleProvider.PROVIDER_NAME);sig.initVerify(publicKey);sig.update(data);return sig.verify(signature);}public static void test() throws Exception {// 生成公私鑰對Map<String, String> keys = generateSmKey();String publicKey = keys.get(PUBLIC_KEY);String privateKey = keys.get(PRIVATE_KEY);String testStr = "123456";System.out.println("原始字符串:" + testStr);System.out.println("公鑰:" + keys.get(PUBLIC_KEY));System.out.println("私鑰:" + keys.get(PRIVATE_KEY));System.out.println();// 公鑰加密String encrypt = encrypt(testStr.getBytes(), publicKey);System.out.println("加密數(shù)據(jù):" + encrypt);// 私鑰簽名,后續(xù)根據(jù)數(shù)據(jù)與公鑰驗(yàn)簽String sign = signByPrivateKey(encrypt, privateKey);System.out.println("數(shù)據(jù)簽名:" + sign);// 公鑰驗(yàn)簽,驗(yàn)證通過后再進(jìn)行數(shù)據(jù)解密boolean b = verifyByPublicKey(encrypt, publicKey, sign);System.out.println("數(shù)據(jù)驗(yàn)簽:" + b);//私鑰解密String decrypt = decrypt(encrypt, privateKey);System.out.println("解密數(shù)據(jù):" + decrypt);}public static void uesCase() throws Exception {//生成公私鑰對String publicKey = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEsrdE0XrAO2S7Ize0tm0r3diH9cPH23t0J9yVDtiVux6g71msH5YGTWW6/ogQSCVJ4iaofgCS/ly5+wkXa+/IGg==";String privateKey = "MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgNxb+Jcu1vhGt9UEbeFUYeCC+RWL7+sfUL1vnhBp2KtKgCgYIKoEcz1UBgi2hRANCAASyt0TResA7ZLsjN7S2bSvd2If1w8fbe3Qn3JUO2JW7HqDvWawflgZNZbr+iBBIJUniJqh+AJL+XLn7CRdr78ga";String testStr = "123456";System.out.println("原始字符串:" + testStr);System.out.println("公鑰:" + publicKey);System.out.println("私鑰:" + privateKey);System.out.println();//公鑰加密String encrypt = encrypt(testStr.getBytes(), publicKey);System.out.println("加密數(shù)據(jù):" + encrypt);// 私鑰簽名,后續(xù)根據(jù)數(shù)據(jù)與公鑰驗(yàn)簽String sign = signByPrivateKey(encrypt, privateKey);System.out.println("數(shù)據(jù)簽名:" + sign);//公鑰驗(yàn)簽,驗(yàn)證通過后再進(jìn)行數(shù)據(jù)解密boolean b = verifyByPublicKey(encrypt, publicKey, sign);System.out.println("數(shù)據(jù)驗(yàn)簽:" + b);//私鑰解密String decrypt = decrypt(encrypt, privateKey);System.out.println("解密數(shù)據(jù):" + decrypt);}public static void main(String[] args) {try {test();
// uesCase();} catch (Exception e) {throw new RuntimeException(e);}}
}
使用案例
驗(yàn)證密碼
接受到用戶輸入的用戶與密碼之后,在數(shù)據(jù)庫中查詢出舊的密碼,并進(jìn)行舊密碼進(jìn)行驗(yàn)簽、解密。解密后判斷用戶輸入的密碼與數(shù)據(jù)庫存儲的密碼是否相同。
// 驗(yàn)證密碼是否正確String password = "123456";String passwordOld = user.getPassword();String signature = user.getSignature();String decryptPasswordOld = null;try {// 公鑰驗(yàn)簽,查看數(shù)據(jù)與簽名是否有效boolean b = Sm2SignatureUtils.verifyByPublicKey(passwordOld, public, signature);if(!b){throw new CommonException("數(shù)據(jù)損壞");}decryptPasswordOld = Sm2SignatureUtils.decrypt(passwordOld, private);}catch (Exception e){throw new CommonException("數(shù)據(jù)損壞");}if (decryptPasswordOld == null || !decryptPasswordOld.equals(password)) {throw new CommonException("用戶密碼錯誤");}
修改密碼
接收到用戶的密碼后,根據(jù)公私要生成加密數(shù)據(jù)串與私鑰簽名。并存儲到數(shù)據(jù)庫,用于之后的密碼驗(yàn)證。
// 公鑰加密String password = "123456";String encrypt = Sm2SignatureUtils.encrypt(password.getBytes(), public);// 根據(jù)數(shù)據(jù)與私鑰,生成私鑰簽名String sign = Sm2SignatureUtils.signByPrivateKey(encrypt, private);user.setPassword(encrypt);user.setSignature(sign);
隨機(jī)密碼鹽加密
工具類與測試方法
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;/*** @author fir* @date 2024/7/25 12:22*/
public class SaltUtils {/*** 生成隨機(jī)安全鹽** @return 鹽*/public static String generateSalt() {// 使用SecureRandom生成安全的隨機(jī)鹽SecureRandom random = new SecureRandom();byte[] salt = new byte[16];random.nextBytes(salt);return Base64.getEncoder().encodeToString(salt);}/*** 生成加鹽密碼** @return 鹽*/public static String hashPassword(String password, String salt){// 將密碼和鹽結(jié)合String saltedPassword = password + salt;// 使用SHA-256進(jìn)行哈希MessageDigest md;try {md = MessageDigest.getInstance("SHA-256");}catch (NoSuchAlgorithmException e){throw new RuntimeException("加密鹽處理失敗");}byte[] hashedBytes = md.digest(saltedPassword.getBytes());// 將哈希值轉(zhuǎn)換為字符串return Base64.getEncoder().encodeToString(hashedBytes);}public static void main(String[] args){String salt = generateSalt();String password = "123456";String hashPassword = hashPassword(password, salt);System.out.println("鹽:" + salt);System.out.println("密碼:" + password);System.out.println("加密密碼:" + hashPassword);}}
使用案例
驗(yàn)證密碼
查詢出用戶的密碼,將用戶輸入的密碼鹽加密,并判斷與數(shù)據(jù)庫的加密密碼是否一致。
String password = "123456";String salt = user.getSalt();String passwordOld = user.getPassword();String mPassword = SaltUtils.hashPassword(password, salt);if (!mPassword.equals(passwordOld)) {throw new CommonException("密碼錯誤");}
修改密碼
將用戶輸入的密碼md5加密之后,存在數(shù)據(jù)庫中,用于之后的密碼驗(yàn)證
String password = "123456";String salt = SaltUtils.generateSalt();String hashPassword = SaltUtils.hashPassword(password, salt);user.setPassword(hashPassword);
MD5加密
引入hutool包,以使用md5加密。
<dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.19</version></dependency>
測試方法
import cn.hutool.crypto.digest.MD5;/*** @author fir* @date 2024/7/22 10:07*/
public class Md5Utils {public static void main(String[] args){// MD5取值String mPassword = MD5.create().digestHex("123456");System.out.println(mPassword);}
}
使用案例
驗(yàn)證密碼
查詢出用戶的密碼,將用戶輸入的密碼MD5加密,并判斷與數(shù)據(jù)庫的加密密碼是否一致。
String password = "123456";String mPassword = MD5.create().digestHex(password);String passwordOld = user.getPassword();if (!mPassword.equals(passwordOld)) {throw new CommonException("密碼錯誤");}
修改密碼
將用戶輸入的密碼md5加密之后,存在數(shù)據(jù)庫中,用于之后的密碼驗(yàn)證
String password = "123456";String passwordMd5 = MD5.create().digestHex(password);user.setPassword(passwordMd5);