java sm3&sm4国密算法
CreationTime--2018年7月5日09点20分
Author:Marydon
1.实现方式
借助bcprov-jdk15on-1.59.jar实现
2.sm3哈希算法
导包
import org.bouncycastle.crypto.digests.SM3Digest; import org.bouncycastle.crypto.macs.HMac; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.pqc.math.linearalgebra.ByteUtils; import java.security.Security; import java.util.Arrays;
代码实现
1 /** 2 * sm3加密算法工具类 3 * @explain 加密与加密结果验证 4 * @author Marydon 5 * @creationTime 2018年7月5日上午10:01:24 6 * @version 1.0 7 * @since 8 * @email [email protected] 9 */ 10 public class Sm3Util { 11 static { 12 Security.addProvider(new BouncyCastleProvider()); 13 } 14 15 /** 16 * 将字符串使用sm3算法进行加密 17 * @explain 18 * @param paramStr 带加密字符串 19 * @return 返回加密后,长度=32的16进制字符串 20 */ 21 public static String encrypt(String paramStr) { 22 // 将字符串转换成byte数组 23 byte[] srcData = paramStr.getBytes(); 24 // 调用hash() 25 byte[] resultHash = hash(srcData); 26 // 将返回的hash值转换成16进制字符串 27 String resultHexString = ByteUtils.toHexString(resultHash); 28 return resultHexString; 29 } 30 31 /** 32 * 返回长度=32的byte数组 33 * @explain 生成对应的hash值 34 * @param srcData 35 * @return 36 */ 37 public static byte[] hash(byte[] srcData) { 38 SM3Digest digest = new SM3Digest(); 39 digest.update(srcData, 0, srcData.length); 40 byte[] hash = new byte[digest.getDigestSize()]; 41 digest.doFinal(hash, 0); 42 return hash; 43 } 44 45 /** 46 * 判断源数据是否一致 47 * @explain 通过验证原数组和生成的hash数组是否为同一数组,验证2者是否为同一数据 48 * @param srcData 49 * 原数组 50 * @param sm3Hash 51 * hash值数组 52 * @return 53 */ 54 public static boolean verify(byte[] srcData, byte[] sm3Hash) { 55 byte[] newHash = hash(srcData); 56 if (Arrays.equals(newHash, sm3Hash)) 57 return true; 58 else 59 return false; 60 } 61 62 /** 63 * 通过密钥进行加密 64 * @explain 指定密钥进行加密 65 * @param key 66 * 密钥 67 * @param srcData 68 * 被加密的byte数组 69 * @return 70 */ 71 public static byte[] hmac(byte[] key, byte[] srcData) { 72 KeyParameter keyParameter = new KeyParameter(key); 73 SM3Digest digest = new SM3Digest(); 74 HMac mac = new HMac(digest); 75 mac.init(keyParameter); 76 mac.update(srcData, 0, srcData.length); 77 byte[] result = new byte[mac.getMacSize()]; 78 mac.doFinal(result, 0); 79 return result; 80 } 81 82 public static void main(String[] args) { 83 // SM3加密结果展示 84 // 测试二:json 85 String json = "{\"name\":\"Marydon\",\"website\":\"http://www.cnblogs.com/Marydon20170307\"}"; 86 // 0b0880f6f2ccd817809a432420e42b66d3772dc18d80789049d0f9654efeae5c 87 System.out.println(Sm3Util.encrypt(json)); 88 // 测试三:中文 89 String str = "迪士尼真人版《花木兰》终于定下男主角,但并非此前大众猜测的国内男星,来自新西兰的华裔演员安佑森(Yoson An)将饰演本片男主角陈宏辉。安佑森此前曾在《卧虎藏龙Ⅱ青冥宝剑》中出演角色,还曾主演新西兰出品的恐怖片《幽灵新娘》。据悉,安佑森出演的这版陈宏辉是一名自信且雄心勃勃的新兵,随着剧情发展,他将成为木兰的朋友和恋人。据此前美国媒体的报道,真人版《花木兰》已经定下由刘亦菲饰演花木兰,李连杰饰演皇帝,巩俐饰演大反派女巫,甄子丹饰演木兰导师Tung,迪士尼为本片投入的预算高达2.9亿美元,本片的导演妮基·卡罗曾执导《鲸骑士》。据悉,《花木兰》将于今年8月在中国以及新西兰开拍,将于2020年3月27日在北美上映。"; 90 String result = Sm3Util.encrypt(str); 91 // 455c383de153ee7b82dab05e91e92d3ac16163f270ca317854c23fad2d807222 92 System.out.println(result); 93 94 // 验证加密后的16进制字符串与加密前的字符串是否相同 95 boolean flag = Sm3Util.verify(str.getBytes(), ByteUtils.fromHexString(result)); 96 System.out.println(flag);// true 97 98 } 99 }
3.sm4哈希算法
导包
import java.security.InvalidKeyException; import java.security.Key; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.SecureRandom; import java.security.Security; import java.util.Arrays; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.KeyGenerator; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.SecretKeySpec; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
代码实现
1 public class Sm4Util { 2 static { 3 Security.addProvider(new BouncyCastleProvider()); 4 } 5 6 public static final String ALGORITHM_NAME = "SM4"; 7 // 加密算法/分组加密模式/分组填充方式 8 // PKCS5Padding-以8个字节为一组进行分组加密 9 // 定义分组加密模式使用:PKCS5Padding 10 public static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS5Padding"; 11 // 128-32位16进制;256-64位16进制 12 public static final int DEFAULT_KEY_SIZE = 128; 13 14 /** 15 * 自动生成密钥 16 * @explain 17 * @return 18 * @throws NoSuchAlgorithmException 19 * @throws NoSuchProviderException 20 */ 21 public static byte[] generateKey() throws NoSuchAlgorithmException, NoSuchProviderException { 22 return generateKey(DEFAULT_KEY_SIZE); 23 } 24 25 /** 26 * @explain 27 * @param keySize 28 * @return 29 * @throws NoSuchAlgorithmException 30 * @throws NoSuchProviderException 31 */ 32 public static byte[] generateKey(int keySize) throws NoSuchAlgorithmException, NoSuchProviderException { 33 KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM_NAME, BouncyCastleProvider.PROVIDER_NAME); 34 kg.init(keySize, new SecureRandom()); 35 return kg.generateKey().getEncoded(); 36 } 37 38 /** 39 * 将字符串使用sm4算法进行加密 40 * @explain 加密模式采用ECB 41 * @param paramStr 带加密字符串 42 * @return 返回16进制的key和16进制的加密字符串 43 */ 44 public static String[] encryptEcb(String paramStr) { 45 // 返回16进制的key和16进制的加密字符串 46 String resultHexStringArray[] = new String[2]; 47 try { 48 // 生成key 49 byte[] keyData = generateKey(); 50 // 将字符串转换成byte数组 51 byte[] srcData = paramStr.getBytes(); 52 // 加密后的数组 53 byte[] cipherArray = encrypt_Ecb_Padding(keyData, srcData); 54 // 将返回的数组转换成16进制字符串 55 String cipherHexString = ByteUtils.toHexString(cipherArray); 56 // 将key值转换成16进制字符串 57 String keyHexString = ByteUtils.toHexString(keyData); 58 // 16进制key 59 resultHexStringArray[0] = keyHexString; 60 // 16进制加密字符串 61 resultHexStringArray[1] = cipherHexString; 62 } catch (Exception e) { 63 e.printStackTrace(); 64 } 65 return resultHexStringArray; 66 } 67 68 /** 69 * 使用sm4算法对加密后的字符串进行解密 70 * @explain 解密模式采用ECB 71 * 特殊字符串转换成String可能出现误差,比如解密后的\\所代表的byte转成String时,只返回一个\ 72 * @param hexKey 16进制密钥 73 * @param cipherText 16进制加密字符串 74 * @return 解密后的字符串 75 */ 76 public static String decryptEcb(String hexKey, String cipherText) { 77 // 用于接收解密后的字符串 78 String decryptStr = ""; 79 try { 80 // 将16进制key转换成数组 81 byte[] keyData = ByteUtils.fromHexString(hexKey); 82 // 将16进制字符串转换成数组 83 byte[] cipherData = ByteUtils.fromHexString(cipherText); 84 // 解密 85 byte[] srcData = decrypt_Ecb_Padding(keyData, cipherData); 86 // 将byte[]转换成String 87 decryptStr = new String(srcData); 88 } catch (Exception e) { 89 e.printStackTrace(); 90 } 91 return decryptStr; 92 } 93 94 /** 95 * 校验加密前后的字符串是否为同一数据 96 * @explain 97 * @param hexKey 16进制密钥 98 * @param cipherText 16进制加密后的字符串 99 * @param paramStr 加密前的字符串 100 * @return 是否为同一数据 101 */ 102 public static boolean verifyEcb(String hexKey, String cipherText, String paramStr) { 103 // 用于接收校验结果 104 boolean flag = false; 105 try { 106 // 将16进制key转换成数组 107 byte[] keyData = ByteUtils.fromHexString(hexKey); 108 // 将16进制字符串转换成数组 109 byte[] cipherData = ByteUtils.fromHexString(cipherText); 110 // 解密 111 byte[] decryptData = decrypt_Ecb_Padding(keyData, cipherData); 112 // 将原字符串转换成byte[] 113 byte[] srcData = paramStr.getBytes("utf-8"); 114 // 判断2个数组是否一致 115 flag = Arrays.equals(decryptData, srcData); 116 } catch (Exception e) { 117 e.printStackTrace(); 118 } 119 return flag; 120 } 121 122 /** 123 * 加密模式之Ecb 124 * @explain 125 * @param key 126 * @param data 127 * @return 128 * @throws InvalidKeyException 129 * @throws NoSuchAlgorithmException 130 * @throws NoSuchProviderException 131 * @throws NoSuchPaddingException 132 * @throws IllegalBlockSizeException 133 * @throws BadPaddingException 134 */ 135 public static byte[] encrypt_Ecb_Padding(byte[] key, byte[] data) 136 throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, 137 IllegalBlockSizeException, BadPaddingException { 138 Cipher cipher = generateEcbCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.ENCRYPT_MODE, key); 139 return cipher.doFinal(data); 140 } 141 142 /** 143 * 解密 144 * @explain 145 * @param key 146 * @param cipherText 147 * @return 148 * @throws IllegalBlockSizeException 149 * @throws BadPaddingException 150 * @throws InvalidKeyException 151 * @throws NoSuchAlgorithmException 152 * @throws NoSuchProviderException 153 * @throws NoSuchPaddingException 154 */ 155 public static byte[] decrypt_Ecb_Padding(byte[] key, byte[] cipherText) 156 throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, NoSuchAlgorithmException, 157 NoSuchProviderException, NoSuchPaddingException { 158 Cipher cipher = generateEcbCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.DECRYPT_MODE, key); 159 return cipher.doFinal(cipherText); 160 } 161 162 /** 163 * 生成ECB暗号 164 * @explain ECB模式(电子密码本模式:Electronic codebook) 165 * @param algorithmName 166 * 算法名称 167 * @param mode 168 * 模式 169 * @param key 170 * @return 171 * @throws NoSuchAlgorithmException 172 * @throws NoSuchProviderException 173 * @throws NoSuchPaddingException 174 * @throws InvalidKeyException 175 */ 176 private static Cipher generateEcbCipher(String algorithmName, int mode, byte[] key) 177 throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, InvalidKeyException { 178 Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME); 179 Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME); 180 cipher.init(mode, sm4Key); 181 return cipher; 182 } 183 184 public static void main(String[] args) { 185 String str = "迪士尼真人版《花木兰》终于定下男主角,但并非此前大众猜测的国内男星,来自新西兰的华裔演员安佑森(Yoson An)将饰演本片男主角陈宏辉。安佑森此前曾在《卧虎藏龙Ⅱ青冥宝剑》中出演角色,还曾主演新西兰出品的恐怖片《幽灵新娘》。据悉,安佑森出演的这版陈宏辉是一名自信且雄心勃勃的新兵,随着剧情发展,他将成为木兰的朋友和恋人。据此前美国媒体的报道,真人版《花木兰》已经定下由刘亦菲饰演花木兰,李连杰饰演皇帝,巩俐饰演大反派女巫,甄子丹饰演木兰导师Tung,迪士尼为本片投入的预算高达2.9亿美元,本片的导演妮基·卡罗曾执导《鲸骑士》/\\、,。据悉,《花木兰》将于今年8月在中国以及新西兰开拍,将于2020年3月27日在北美上映。"; 186 // 加密 187 String[] encrytData = Sm4Util.encryptEcb(str); 188 // 密钥 189 String hexKey = encrytData[0]; 190 // 密文 191 String cipherText = encrytData[1]; 192 // 解密后的字符串,少了一个\ 193 System.out.println(Sm4Util.decryptEcb(hexKey, cipherText)); 194 // 判断是否为加密前后,数据是否一致 195 System.out.println(Sm4Util.verifyEcb(hexKey, cipherText, str));// true 196 } 197 }