JAVA-国密算法SM3和SM4应用Example
參考資料:
https://baijiahao.baidu.com/s?id=1629915330021466224&wfr=spider&for=pc
https://github.com/chenliwu/gmhelper
Maven引入加密算法庫
<!-- 國密算法 --> <dependency><groupId>org.bouncycastle</groupId><artifactId>bcprov-jdk15on</artifactId><version>1.65</version> </dependency> <dependency><groupId>org.bouncycastle</groupId><artifactId>bcpkix-jdk15on</artifactId><version>1.65</version> </dependency> <!-- 國密算法 -->SM3(雜湊算法)
1、SM3為雜湊算法(消息摘要,也稱為散列函數(shù))??梢杂肕D5作為對比理解。該算法已公開。校驗結(jié)果為256位。
2、SM3雜湊算法是我國自主設(shè)計的密碼雜湊算法,適用于商用密碼應(yīng)用中的數(shù)字簽名和驗證消息認證碼的生成與驗證以及隨機數(shù)的生成,可滿足多種密碼應(yīng)用的安全需求。為了保證雜湊算法的安全性,其產(chǎn)生的雜湊值的長度不應(yīng)太短,例如MD5輸出128比特雜湊值,輸出長度太短,影響其安全性。SHA-1算法的輸出長度為160比特,SM3算法的輸出長度為256比特,因此SM3算法的安全性要高于MD5算法和SHA-1算法。
3、SM3用于生成消息摘要,應(yīng)用于數(shù)字簽名的生成和驗證。
SM3工具類代碼
public class SM3Utils {static {Security.addProvider(new BouncyCastleProvider());}/*** 計算SM3摘要值** @param srcData 原文* @return 摘要值,對于SM3算法來說是32字節(jié)*/public static byte[] hash(byte[] srcData) {SM3Digest digest = new SM3Digest();digest.update(srcData, 0, srcData.length);byte[] hash = new byte[digest.getDigestSize()];digest.doFinal(hash, 0);return hash;}/*** 驗證摘要** @param srcData 原文* @param sm3Hash 摘要值* @return 返回true標(biāo)識驗證成功,false標(biāo)識驗證失敗*/public static boolean verify(byte[] srcData, byte[] sm3Hash) {byte[] newHash = hash(srcData);if (Arrays.equals(newHash, sm3Hash)) {return true;} else {return false;}}/*** 計算SM3 Mac值** @param key key值,可以是任意長度的字節(jié)數(shù)組* @param srcData 原文* @return Mac值,對于HMac-SM3來說是32字節(jié)*/public static byte[] hmac(byte[] key, byte[] srcData) {KeyParameter keyParameter = new KeyParameter(key);SM3Digest digest = new SM3Digest();HMac mac = new HMac(digest);mac.init(keyParameter);mac.update(srcData, 0, srcData.length);byte[] result = new byte[mac.getMacSize()];mac.doFinal(result, 0);return result;} }SM3單測代碼
public class SM3UtilsTest {public static final String KEY = "1234567812345678";public static final String CHARSET_UTF8 = StandardCharsets.UTF_8.name();@Testpublic void testGenerateHash() throws Exception {String data = "測試SM3雜湊算法";// 生成消息摘要 消息摘要=SM3(原文)byte[] signByte = SM3Utils.hash(data.getBytes(CHARSET_UTF8));String signBase64String = Base64.toBase64String(signByte);System.out.println("\n消息摘要 Base64 String:" + signBase64String);// 生成數(shù)字簽名 數(shù)字簽名 = SM3(原文+密鑰)String digitalSign = Base64.toBase64String(SM3Utils.hash((data + KEY).getBytes(CHARSET_UTF8)));System.out.println("\n數(shù)字簽名 Base64 String:" + digitalSign);}@Testpublic void testHashAndVerify() {try {String data = "SM3UtilsTest";byte[] hash = SM3Utils.hash(data.getBytes(CHARSET_UTF8));System.out.println("hash:" + Arrays.toString(hash));String hashBase64String = Base64.toBase64String(hash);System.out.println("SM3 Base64 String:\n" + hashBase64String);System.out.println("hash:\n" + Arrays.toString(Base64.decode(hashBase64String)));boolean flag = SM3Utils.verify(data.getBytes(CHARSET_UTF8), hash);System.out.println("驗證結(jié)果:" + flag);System.out.println("======");Assert.assertEquals(true, flag);} catch (Exception ex) {ex.printStackTrace();}}}運行結(jié)果:
消息摘要 Base64 String:+nNWZysGWq8yrqAUs1OUfVakiADNOrqgQf6tc3F08EM= 數(shù)字簽名 Base64 String:9zQNDWGfGsNlN/12Dgu1TvuslfAJVkoU1TWnkUJR1qI=hash:[-48, -34, 61, -18, 35, 57, -55, 66, -72, 19, -117, -81, -7, -14, 57, -65, 85, 51, 16, 58, 36, -23, -108, 77, -27, -97, -27, 73, 88, -126, 15, -118] SM3 Base64 String: 0N497iM5yUK4E4uv+fI5v1UzEDok6ZRN5Z/lSViCD4o= hash: [-48, -34, 61, -18, 35, 57, -55, 66, -72, 19, -117, -81, -7, -14, 57, -65, 85, 51, 16, 58, 36, -23, -108, 77, -27, -97, -27, 73, 88, -126, 15, -118] 驗證結(jié)果:trueSM4(分組密碼算法)
1、SM4 無線局域網(wǎng)標(biāo)準(zhǔn)的分組密碼算法。對稱加密,密鑰長度和分組長度均為128位。
2、SM4分組密碼算法是我國自主設(shè)計的分組對稱密碼算法,用于實現(xiàn)數(shù)據(jù)的加密/解密運算,以保證數(shù)據(jù)和信息的機密性。要保證一個對稱密碼算法的安全性的基本條件是其具備足夠的密鑰長度,SM4算法與AES算法具有相同的密鑰長度分組長度128比特,因此在安全性上高于3DES算法。
SM4工具類
public class SM4Utils {static {Security.addProvider(new BouncyCastleProvider());}public static final String ALGORITHM_NAME = "SM4";public static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS5Padding";public static final String ALGORITHM_NAME_ECB_NOPADDING = "SM4/ECB/NoPadding";public static final String ALGORITHM_NAME_CBC_PADDING = "SM4/CBC/PKCS5Padding";public static final String ALGORITHM_NAME_CBC_NOPADDING = "SM4/CBC/NoPadding";/*** SM4算法目前只支持128位(即密鑰16字節(jié))*/public static final int DEFAULT_KEY_SIZE = 128;public static byte[] generateKey() throws NoSuchAlgorithmException, NoSuchProviderException {return generateKey(DEFAULT_KEY_SIZE);}public static byte[] generateKey(int keySize) throws NoSuchAlgorithmException, NoSuchProviderException {KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM_NAME, BouncyCastleProvider.PROVIDER_NAME);kg.init(keySize, new SecureRandom());return kg.generateKey().getEncoded();}public static byte[] encrypt_ECB_Padding(byte[] key, byte[] data)throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException,NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {Cipher cipher = generateECBCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.ENCRYPT_MODE, key);return cipher.doFinal(data);}public static byte[] decrypt_ECB_Padding(byte[] key, byte[] cipherText)throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException,NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException {Cipher cipher = generateECBCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.DECRYPT_MODE, key);return cipher.doFinal(cipherText);}public static byte[] encrypt_ECB_NoPadding(byte[] key, byte[] data)throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException,NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {Cipher cipher = generateECBCipher(ALGORITHM_NAME_ECB_NOPADDING, Cipher.ENCRYPT_MODE, key);return cipher.doFinal(data);}public static byte[] decrypt_ECB_NoPadding(byte[] key, byte[] cipherText)throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException,NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException {Cipher cipher = generateECBCipher(ALGORITHM_NAME_ECB_NOPADDING, Cipher.DECRYPT_MODE, key);return cipher.doFinal(cipherText);}public static byte[] encrypt_CBC_Padding(byte[] key, byte[] iv, byte[] data)throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException,NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException,InvalidAlgorithmParameterException {Cipher cipher = generateCBCCipher(ALGORITHM_NAME_CBC_PADDING, Cipher.ENCRYPT_MODE, key, iv);return cipher.doFinal(data);}public static byte[] decrypt_CBC_Padding(byte[] key, byte[] iv, byte[] cipherText)throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException,NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException,InvalidAlgorithmParameterException {Cipher cipher = generateCBCCipher(ALGORITHM_NAME_CBC_PADDING, Cipher.DECRYPT_MODE, key, iv);return cipher.doFinal(cipherText);}public static byte[] encrypt_CBC_NoPadding(byte[] key, byte[] iv, byte[] data)throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException,NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException,InvalidAlgorithmParameterException {Cipher cipher = generateCBCCipher(ALGORITHM_NAME_CBC_NOPADDING, Cipher.ENCRYPT_MODE, key, iv);return cipher.doFinal(data);}public static byte[] decrypt_CBC_NoPadding(byte[] key, byte[] iv, byte[] cipherText)throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException,NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException,InvalidAlgorithmParameterException {Cipher cipher = generateCBCCipher(ALGORITHM_NAME_CBC_NOPADDING, Cipher.DECRYPT_MODE, key, iv);return cipher.doFinal(cipherText);}public static byte[] doCMac(byte[] key, byte[] data) throws NoSuchProviderException, NoSuchAlgorithmException,InvalidKeyException {Key keyObj = new SecretKeySpec(key, ALGORITHM_NAME);return doMac("SM4-CMAC", keyObj, data);}public static byte[] doGMac(byte[] key, byte[] iv, int tagLength, byte[] data) {org.bouncycastle.crypto.Mac mac = new GMac(new GCMBlockCipher(new SM4Engine()), tagLength * 8);return doMac(mac, key, iv, data);}/*** 默認使用PKCS7Padding/PKCS5Padding填充的CBCMAC** @param key* @param iv* @param data* @return*/public static byte[] doCBCMac(byte[] key, byte[] iv, byte[] data) {SM4Engine engine = new SM4Engine();org.bouncycastle.crypto.Mac mac = new CBCBlockCipherMac(engine, engine.getBlockSize() * 8, new PKCS7Padding());return doMac(mac, key, iv, data);}/*** @param key* @param iv* @param padding 可以傳null,傳null表示NoPadding,由調(diào)用方保證數(shù)據(jù)必須是BlockSize的整數(shù)倍* @param data* @return* @throws Exception*/public static byte[] doCBCMac(byte[] key, byte[] iv, BlockCipherPadding padding, byte[] data) throws Exception {SM4Engine engine = new SM4Engine();if (padding == null) {if (data.length % engine.getBlockSize() != 0) {throw new Exception("if no padding, data length must be multiple of SM4 BlockSize");}}org.bouncycastle.crypto.Mac mac = new CBCBlockCipherMac(engine, engine.getBlockSize() * 8, padding);return doMac(mac, key, iv, data);}private static byte[] doMac(org.bouncycastle.crypto.Mac mac, byte[] key, byte[] iv, byte[] data) {CipherParameters cipherParameters = new KeyParameter(key);mac.init(new ParametersWithIV(cipherParameters, iv));mac.update(data, 0, data.length);byte[] result = new byte[mac.getMacSize()];mac.doFinal(result, 0);return result;}private static byte[] doMac(String algorithmName, Key key, byte[] data) throws NoSuchProviderException,NoSuchAlgorithmException, InvalidKeyException {Mac mac = Mac.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME);mac.init(key);mac.update(data);return mac.doFinal();}private static Cipher generateECBCipher(String algorithmName, int mode, byte[] key)throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException,InvalidKeyException {Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME);Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME);cipher.init(mode, sm4Key);return cipher;}private static Cipher generateCBCCipher(String algorithmName, int mode, byte[] key, byte[] iv)throws InvalidKeyException, InvalidAlgorithmParameterException, NoSuchAlgorithmException,NoSuchProviderException, NoSuchPaddingException {Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME);Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME);IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);cipher.init(mode, sm4Key, ivParameterSpec);return cipher;} }SM4單測代碼
public class SM4UtilsTest {@Testpublic void testCustomKeySM4ECB() throws Exception {String charset = StandardCharsets.UTF_8.name();// SM4密鑰長度分組長度128bit,因此密匙長度為16String myKey = "1234567812345678";String data = "SM4UtilsTest";byte[] myKeyBytes = myKey.getBytes(charset);byte[] encryptedBytes = SM4Utils.encrypt_ECB_Padding(myKeyBytes, data.getBytes(charset));String encryptedHexString = ByteUtils.toHexString(encryptedBytes);System.out.println("ECB加密后的數(shù)據(jù)HexString:" + encryptedHexString);byte[] decryptedBytes = SM4Utils.decrypt_ECB_Padding(myKeyBytes, ByteUtils.fromHexString(encryptedHexString));System.out.println("ECB解密后的數(shù)據(jù):" + new String(decryptedBytes, charset));}@Testpublic void testCustomKeySM4CBC() throws Exception {String charset = StandardCharsets.UTF_8.name();// SM4密鑰長度分組長度128bit,因此密匙長度為16String myKey = "1234567812345678";String myIv = "8765432187654321";String data = "SM4UtilsTest";byte[] myKeyBytes = myKey.getBytes(charset);byte[] myIvBytes = myIv.getBytes(charset);byte[] encryptedBytes = SM4Utils.encrypt_CBC_Padding(myKeyBytes, myIvBytes, data.getBytes(charset));String encryptedHexString = ByteUtils.toHexString(encryptedBytes);System.out.println("CBC加密后的數(shù)據(jù)HexString:" + encryptedHexString);byte[] decryptedBytes = SM4Utils.decrypt_CBC_Padding(myKeyBytes, myIvBytes, ByteUtils.fromHexString(encryptedHexString));System.out.println("CBC解密后的數(shù)據(jù):" + new String(decryptedBytes, charset));}}運行結(jié)果:
ECB加密后的數(shù)據(jù)HexString:a7df3e6b9332d09939677631db0a3dbc ECB解密后的數(shù)據(jù):SM4UtilsTestCBC加密后的數(shù)據(jù)HexString:81335cd7f2101b1786fa620afa595e11 CBC解密后的數(shù)據(jù):SM4UtilsTest總結(jié)
以上是生活随笔為你收集整理的JAVA-国密算法SM3和SM4应用Example的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【类ChatGPT】本地CPU部署中文羊
- 下一篇: 科学划定“三区三线” 严格保护与合理利用