Java實(shí)現(xiàn)支付對接常用加密方式的示例代碼
一、術(shù)語表
1.對稱算法
加密解密密鑰是相同的。這些算法也叫秘密密鑰算法或單密鑰算法,它要求發(fā)送者和接收者在安全通信之前,商定一個(gè)密鑰。對稱算法的安全性依賴于密鑰,泄漏密鑰就意味著任何人都能對消息進(jìn)行加密解密。只要通信需要保密,密鑰就必須保密。
對稱算法可分為兩類。一次只對明文中的單個(gè)位(有時(shí)對字節(jié))運(yùn)算的算法稱為序列算法或序列密碼。另一類算法是對明文的一組位進(jìn)行運(yùn)算,這些位組稱為分組,相應(yīng)的算法稱為分組算法或分組密碼?,F(xiàn)代計(jì)算機(jī)密碼算法的典型分組長度為64位――這個(gè)長度大到足以防止分析破譯,但又小到足以方便作用。
2.非對稱算法
非對稱算法也叫公開密鑰加密,它是用兩個(gè)數(shù)學(xué)相關(guān)的密鑰對信息進(jìn)行編碼。在此系統(tǒng)中,其中一個(gè)密鑰叫公開密鑰,可隨意發(fā)給期望與密鑰持有者進(jìn)行安全通信的人。公開密鑰用于對信息加密。第二個(gè)密鑰是私有密鑰,屬于密鑰持有者,此人要仔細(xì)保存私有密鑰。密鑰持有者用私有密鑰對收到的信息進(jìn)行解密。
一般來說,都是公鑰加密,私鑰解密。如果系統(tǒng)雙方需要相互通訊,可以生成兩對密鑰對。各自保存好自己的私鑰和對方的公鑰,用公鑰加密,私鑰進(jìn)行解密
3.可逆加密算法
一般來說,涉及到秘鑰之類的算法,都是可逆的。意思就是通過算法和秘鑰加密之后,可以再次通過解密算法還原。常見的有DES、3DES、AES128、AES192、AES256 。其中AES后面的數(shù)字代表的是密鑰長度。對稱加密算法的安全性相對較低,比較適用的場景就是內(nèi)網(wǎng)環(huán)境中的加解密。
4.不可逆算法
常見的不可逆加密算法有MD5,HMAC,SHA1、SHA-224、SHA-256、SHA-384,和SHA-512,其中SHA-224、SHA-256、SHA-384,和SHA-512我們可以統(tǒng)稱為SHA2加密算法,SHA加密算法的安全性要比MD5更高,而SHA2加密算法比SHA1的要高。其中SHA后面的數(shù)字表示的是加密后的字符串長度,SHA1默認(rèn)會(huì)產(chǎn)生一個(gè)160位的信息摘要。
不可逆加密算法最大的特點(diǎn)就是不需要密鑰
5.加密鹽
加密鹽也是比較常聽到的一個(gè)概念,鹽就是一個(gè)隨機(jī)字符串用來和我們的加密串拼接后進(jìn)行加密。加鹽主要是為了保證加密字符串的安全性。假如有一個(gè)加鹽后的加密串,黑客通過一定手段得到這個(gè)加密串,他解密后拿到的明文,并不是我們加密前的字符串,而是加密前的字符串和鹽組合的字符串,這樣相對來說又增加了字符串的安全性
或者也可以用在簽名,例如簽名是對明文或者密文加鹽后的簽名,有人想串改數(shù)據(jù),如果不知道這個(gè)鹽和規(guī)則,那么接收方驗(yàn)簽就會(huì)不通過,從而保證通訊的安全。
二、傳統(tǒng)加密算法介紹
DES(Data Encryption Standard)
對稱算法,數(shù)據(jù)加密標(biāo)準(zhǔn),速度較快,適用于加密大量數(shù)據(jù)的場合。
AES算法
是DES的升級版,屬于對稱算法??赡?/p>
代碼:AESUtil
package AES; import java.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.Base64; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.KeyGenerator; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; public class AESUtil { /** * AES加密字符串 * * @param content * 需要被加密的字符串 * @param password * 加密需要的密碼 * @return 密文 */ public static byte[] encrypt(String content, String password) { try { KeyGenerator kgen = KeyGenerator.getInstance("AES");// 創(chuàng)建AES的Key生產(chǎn)者 kgen.init(128, new SecureRandom(password.getBytes()));// 利用用戶密碼作為隨機(jī)數(shù)初始化出 //加密沒關(guān)系,SecureRandom是生成安全隨機(jī)數(shù)序列,password.getBytes()是種子,只要種子相同,序列就一樣,所以解密只要有password就行 SecretKey secretKey = kgen.generateKey();// 根據(jù)用戶密碼,生成一個(gè)密鑰 byte[] enCodeFormat = secretKey.getEncoded();// 返回基本編碼格式的密鑰,如果此密鑰不支持編碼,則返回 SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");// 轉(zhuǎn)換為AES專用密鑰 Cipher cipher = Cipher.getInstance("AES");// 創(chuàng)建密碼器 byte[] byteContent = content.getBytes("utf-8"); cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化為加密模式的密碼器 byte[] result = cipher.doFinal(byteContent);// 加密 return result; } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } return null; } /** * 解密AES加密過的字符串 * * @param content * AES加密過過的內(nèi)容 * @param password * 加密時(shí)的密碼 * @return 明文 */ public static byte[] decrypt(byte[] content, String password) { try { KeyGenerator kgen = KeyGenerator.getInstance("AES");// 創(chuàng)建AES的Key生產(chǎn)者 kgen.init(128, new SecureRandom(password.getBytes())); SecretKey secretKey = kgen.generateKey();// 根據(jù)用戶密碼,生成一個(gè)密鑰 byte[] enCodeFormat = secretKey.getEncoded();// 返回基本編碼格式的密鑰 SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");// 轉(zhuǎn)換為AES專用密鑰 Cipher cipher = Cipher.getInstance("AES");// 創(chuàng)建密碼器 cipher.init(Cipher.DECRYPT_MODE, key);// 初始化為解密模式的密碼器 byte[] result = cipher.doFinal(content); return result; // 明文 } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } return null; } public static void main(String[] args) throws Exception { String a = Base64.getEncoder().encodeToString("435678(*&*&^*&^%&^$%^#%#!@#$%^&%%90|8752$".getBytes(StandardCharsets.UTF_8)); String b = new String(Base64.getDecoder().decode(a.getBytes(StandardCharsets.UTF_8))); System.out.println(a+":"+b); String content = "{'aaa':'111','bbb':'222'}"; String secretKey = "43567890|8752$"; System.out.println("需要加密的內(nèi)容:" + content); byte[] encrypt = encrypt(content, secretKey); System.out.println("加密后的2進(jìn)制密文:"); System.out.println(new String(encrypt)); String hexStr = Base64.getEncoder().encodeToString(encrypt); System.out.println("base64編碼后密文:" + hexStr); byte[] byte2 = Base64.getDecoder().decode(hexStr); System.out.println("解碼后轉(zhuǎn)換為2進(jìn)制后密文:"); System.out.println(new String(byte2)); byte[] decrypt = decrypt(byte2, secretKey); System.out.println("解密后的內(nèi)容:" + new String(decrypt,"utf-8")); } }
RSA算法
公鑰加密算法,非對稱,可逆
代碼:RSAUtil
package RSASha256; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import java.io.UnsupportedEncodingException; import java.security.*; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; import java.util.HashMap; import java.util.Map; public class RSAUtil { private static final int DEFAULT_RSA_KEY_SIZE = 2048; private static final String KEY_ALGORITHM = "RSA"; private static final String SIGNATURE_ALGORITHM = "MD5withRSA"; public static void main(String [] args){ Map<String,String> result = generateRsaKey(DEFAULT_RSA_KEY_SIZE); String publicKey = result.get("publicKey"); String privateKey = result.get("privateKey"); // String publicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuazymB25s/ueIfwMM2H74736dQQE4bvRDgNfWltM4PzduNC84ntav41qEgbZtby47O57m/YDVf6vMXJH25ejlsBBhls7FD7xHTkOrskZqLekH0xCs2xa/akdwllbewUNzvWMuQy8X3j2VnrILXzVjvqYxkFY0itMo8fq1xSO6B/S5/RaeKpTtIepFB2cIU9vMrtLoCsnjVoTuPdWcoC79LtS0g1Q5BV4dn+Uca+8/gUbhS7vbth3oLZt6gXTRjJrPxhT4lqWsCZqwkQHPG/8JQgnFqs7+R0KcyemG/411IaZI9WYRGTsQji8sQ2q7HAlZRXvJn+As7jJPvyi67JGKwIDAQAB"; // String privateKey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC5rPKYHbmz+54h/AwzYfvjvfp1BAThu9EOA19aW0zg/N240Lzie1q/jWoSBtm1vLjs7nub9gNV/q8xckfbl6OWwEGGWzsUPvEdOQ6uyRmot6QfTEKzbFr9qR3CWVt7BQ3O9Yy5DLxfePZWesgtfNWO+pjGQVjSK0yjx+rXFI7oH9Ln9Fp4qlO0h6kUHZwhT28yu0ugKyeNWhO491ZygLv0u1LSDVDkFXh2f5Rxr7z+BRuFLu9u2Hegtm3qBdNGMms/GFPiWpawJmrCRAc8b/wlCCcWqzv5HQpzJ6Yb/jXUhpkj1ZhEZOxCOLyxDarscCVlFe8mf4CzuMk+/KLrskYrAgMBAAECggEABjC+8dVj4J1N+2IU4g2tQT2PQSF+LCx/3tC7+B49JO8pUUUcVwy3zNUhKTKzRXziSXv2ARAlslNIcgSWYrreiGMmjB00jgs/LLM/SxKHWXmt7iEzxBmjuvtNc7JY+3QCrti+9Vh4W1KEHAQB8opL8HVobIu3M2KgLoG20a7syM5gPiCUb3EQ6P30u47y7I2wsbMnMmu79rPu3Q7lSNeoB9KdVV6EA6vukL3chY1QxFh6oFZS1yNjFjxy5RxT8U5Juhr5LYRnOP8MEVptbtrh34SSm0uyckkgJ6jOsrDJqo/DJxjPxixj+BWgG5/XbNq3PFEyEAqkrTZY+FMHSufoaQKBgQDpqPxpl05mDUXCnc7I6F8LZqjnjZU1h9zwKuQe94Fs3I3siWS62sJx1pZ66P9eeIOsT0T4Ye9nDx2x04yyfUcENhydFTQ5M5M9e2q1kKdNeBUT6Xy4WcRibKozUq07mLYSn0kDbuimp/Erp2w3hl5nZu/eLKDjt7yM0otv3uPp5wKBgQDLbYC4hcvk+f1kd/xJQ2ljwF+c8kHHFrlLRipf7qTB24dcrauWXbAGbbcKXx2G+3Waswyf09NdALiWab/jUUUHJm0YGxyr0k5lHci0/eC7hD3tWTL/HRyeF3umKZEkBQypzNeymE6t0lSqjL3MXFQLqumu4h677qA9/DNh7DYhHQKBgQCfAm/bj6s7iba6jVfmozPi91bkVRaAWlgBXL7nT/nU0ncGzC0vd6WxgJ3hQORgLtU0krFV8pfP45qKpHNwGA8XD5gDUiW68500zuM8chdYgeqeJVvJvNUHQfnFeXMIRpFJNPqkCnrqxwk5cvMTCi7+YS/FW0uWDDiVAMcBN4aUawKBgCOgvAiVNk6WEfEEqqTSL6UOzjAYpbiOnEk4src2fpiNMDnlGMYvBmM51/LzEaLQa5p6fV2Ipd4GAE4nmznew+4qprSwGudk3+IJw1sfk7qDwKzPEIVpvddaWYeShB8A22TpwWVAE5eR3M459AvUp8ubVW4RoDxd4Ka6gu1Fh31pAoGAYNPKJNCrdOsQH0nkH0Ld44cH6HD+zcZtoad2eQk0T2SPnKBsIS1S9W0adreOXUv1edO/hN3P/BcTuCaax7ijTscS2f0atc/NIa6LjBnK7oUBBzib9v21L72ZVZ5st4c/H7IzbQCXfS81489a7TTHP+e1HzS/XePftO0pAkr1GJ0="; System.out.println("公鑰為:" + publicKey); System.out.println("私鑰為:" + privateKey); String plaintext = "{'a':'1111','b':'2222'}"; String ciphertext = null; try { System.out.println("開始加密明文:"+plaintext); ciphertext = encrypt(plaintext,publicKey); } catch (Exception e) { System.out.println("加密失敗"); throw new RuntimeException(e); } System.out.println("得到的密文為:"+ciphertext); String deciphering = decrypt(ciphertext,privateKey); System.out.println("解密后:"+deciphering); } /** * 生成RSA 公私鑰,可選長度為1025,2048位. */ public static Map<String,String> generateRsaKey(int keySize) { Map<String,String> result = new HashMap<>(2); try { KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM); // 初始化密鑰對生成器,密鑰大小為1024 2048位 keyPairGen.initialize(keySize, new SecureRandom()); // 生成一個(gè)密鑰對,保存在keyPair中 KeyPair keyPair = keyPairGen.generateKeyPair(); // 得到公鑰字符串 result.put("publicKey", new String(Base64.getEncoder().encode(keyPair.getPublic().getEncoded()))); // 得到私鑰字符串 result.put("privateKey", new String(Base64.getEncoder().encode(keyPair.getPrivate().getEncoded()))); } catch (GeneralSecurityException e) { e.printStackTrace(); } return result; } /** * RSA私鑰解密 * @param str 解密字符串 * @param privateKey 私鑰 * @return 明文 */ public static String decrypt(String str, String privateKey) { //64位解碼加密后的字符串 byte[] inputByte; String outStr = ""; try { inputByte = Base64.getDecoder().decode(str.getBytes("UTF-8")); //base64編碼的私鑰 byte[] decoded = Base64.getDecoder().decode(privateKey); RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded)); //RSA解密 Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.DECRYPT_MODE, priKey); outStr = new String(cipher.doFinal(inputByte)); } catch (UnsupportedEncodingException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException | InvalidKeySpecException | NoSuchAlgorithmException e) { e.printStackTrace(); } return outStr; } /** * RSA公鑰加密 * @param str 需要加密的字符串 * @param publicKey 公鑰 * @return 密文 * @throws Exception 加密過程中的異常信息 */ public static String encrypt(String str, String publicKey) throws Exception { //base64編碼的公鑰 byte[] decoded = Base64.getDecoder().decode(publicKey); RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded)); //RSA加密 Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.ENCRYPT_MODE, pubKey); String outStr = Base64.getEncoder().encodeToString(cipher.doFinal(str.getBytes("UTF-8"))); return outStr; } public static String sign(String data, String priKey) throws Exception { byte[] decoded = Base64.getDecoder().decode(priKey); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PrivateKey privateKey = keyFactory.generatePrivate(new X509EncodedKeySpec(decoded)); Signature signature = Signature.getInstance("MD5withRSA"); signature.initSign(privateKey); signature.update(data.getBytes()); return new String(Base64.getEncoder().encode(signature.sign())); } public static boolean verify(String pubKey, String sign, String data) throws Exception{ //獲取KeyFactory,指定RSA算法 KeyFactory keyFactory = KeyFactory.getInstance("RSA"); //將BASE64編碼的公鑰字符串進(jìn)行解碼 byte[] encodeByte = Base64.getDecoder().decode(pubKey); //將BASE64解碼后的字節(jié)數(shù)組,構(gòu)造成X509EncodedKeySpec對象,生成公鑰對象 PublicKey publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodeByte)); Signature signature = Signature.getInstance("MD5withRSA"); //加載公鑰 signature.initVerify(publicKey); //更新原數(shù)據(jù) signature.update(data.getBytes("UTF-8")); //公鑰驗(yàn)簽(true-驗(yàn)簽通過;false-驗(yàn)簽失?。? return signature.verify(Base64.getDecoder().decode(sign)); } }
MD5算法
信息摘要(Hash安全散列)算法,也叫哈希算法,哈希值也叫散列值,不可逆,不需要秘鑰。
代碼: MD5Util
package MD5; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; public class MD5Util { public static void main(String[] args) { String sha256Str = getSha256Str("123456"); System.out.println(sha256Str); } /** * sha256加密 * * @param str 要加密的字符串 * @return 加密后的字符串 */ public static String getSha256Str(String str) { MessageDigest messageDigest; String encodeStr = ""; try { messageDigest = MessageDigest.getInstance("MD5"); messageDigest.update(str.getBytes(StandardCharsets.UTF_8)); encodeStr = byte2Hex(messageDigest.digest()); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return encodeStr; } /** * sha256加密 將byte轉(zhuǎn)為16進(jìn)制 * * @param bytes 字節(jié)碼 * @return 加密后的字符串 */ private static String byte2Hex(byte[] bytes) { StringBuilder stringBuilder = new StringBuilder(); String temp; for (byte aByte : bytes) { temp = Integer.toHexString(aByte & 0xFF); if (temp.length() == 1) { //1得到一位的進(jìn)行補(bǔ)0操作 stringBuilder.append("0"); } stringBuilder.append(temp); } return stringBuilder.toString(); } }
SHA-256算法
sha256算法也是一種密碼散列函數(shù),對于任意長度的消息,SHA256都會(huì)產(chǎn)生一個(gè)256bit長的散列值(哈希值),用于確保信息傳輸完整一致,稱作消息摘要。這個(gè)摘要相當(dāng)于是個(gè)長度為32個(gè)字節(jié)的數(shù)組,通常用一個(gè)長度為64的十六進(jìn)制字符串來表示。
和MD5算法對比
相同點(diǎn):
1、都是密碼散列函數(shù),加密不可逆。
2、都可以實(shí)現(xiàn)對任意長度對象加密,都不能防止碰撞。
安全性方面:
1、SHA256(稱SHA2)的安全性最高,但是耗時(shí)要其他兩種多很多。
2、md5相對來說比較容易碰撞,安全性沒這么高。
性能方面:
以個(gè)60M的件為測試樣本,經(jīng)過1000次的測試平均值,這兩種算法的表現(xiàn)如下:
MD5算法運(yùn)1000次的平均時(shí)間為:226ms
SHA256算法運(yùn)1000次的平均時(shí)間為:473ms
總而言之,md5和sha256都是密碼散列函數(shù),加密不可逆。雖然都不能防止碰撞,但是相對而言,md5比較容易碰撞,安全性沒有sha256高。
代碼:SHA256Util
package RSASha256; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; public class SHA256Util { public static void main(String[] args) { String sha256Str = getSha256Str("123456"); System.out.println(sha256Str); } /** * sha256加密 * * @param str 要加密的字符串 * @return 加密后的字符串 */ public static String getSha256Str(String str) { MessageDigest messageDigest; String encodeStr = ""; try { messageDigest = MessageDigest.getInstance("SHA-256"); messageDigest.update(str.getBytes(StandardCharsets.UTF_8)); encodeStr = byte2Hex(messageDigest.digest()); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return encodeStr; } /** * sha256加密 將byte轉(zhuǎn)為16進(jìn)制 * * @param bytes 字節(jié)碼 * @return 加密后的字符串 */ private static String byte2Hex(byte[] bytes) { StringBuilder stringBuilder = new StringBuilder(); String temp; for (byte aByte : bytes) { temp = Integer.toHexString(aByte & 0xFF); if (temp.length() == 1) { //1得到一位的進(jìn)行補(bǔ)0操作 stringBuilder.append("0"); } stringBuilder.append(temp); } return stringBuilder.toString(); } }
三、國密算法介紹
簡介
為保障國家密碼應(yīng)用安全,2011年GJMM管理局發(fā)布《關(guān)于做好公鑰密碼算法升級工作的通知》,
要求自2011年3月1日起在建和擬建公鑰密碼基礎(chǔ)設(shè)施電子認(rèn)證系統(tǒng)和密鑰管理系統(tǒng)應(yīng)使用國密算法。
《金融和重要領(lǐng)域密碼應(yīng)用與創(chuàng)新發(fā)展工作規(guī)劃(2018-2022年) 》以及相關(guān)法規(guī)文件也要求我國金融
和重要領(lǐng)域密碼應(yīng)用采用SM2國產(chǎn)密碼算法體系。
國密算法是指GJMM管理局認(rèn)定的一系列國產(chǎn)密碼算法,包括SM1-SM9以及ZUC等。
其中SM1、SM4、SM5、SM6、SM7、SM8、ZUC等屬于對稱密碼,SM2、SM9等屬于公鑰密碼,SM3屬于單向散列函數(shù)。
目前我國主要使用公開的SM2、SM3、SM4作為商用密碼。
SM1
SM1也叫商密1號(hào)算法,是一種國產(chǎn)的對稱算法,分組長度和密鑰長度都為 128 比特,該算法不公開,調(diào)用該算法時(shí),需要通過加密芯片的接口進(jìn)行調(diào)用。算法安全保密強(qiáng)度及相關(guān)軟硬件實(shí)現(xiàn)性能與 AES 相當(dāng)
SM2
SM2算法和RSA算法都是公鑰密碼算法,SM2算法是一種更先進(jìn)安全的算法,在我們國家商用密碼體系中被用來替換RSA算法。
隨著密碼技術(shù)和計(jì)算機(jī)技術(shù)的發(fā)展,目前常用的1024位RSA算法面臨嚴(yán)重的安全威脅,我們國家密碼管理部門經(jīng)過研究,決定采用SM2橢圓曲線算法替換RSA算法。
代碼:SM2Util
package GMSM; import org.bouncycastle.asn1.gm.GMNamedCurves; import org.bouncycastle.asn1.x9.X9ECParameters; import org.bouncycastle.crypto.CryptoException; import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.engines.SM2Engine; import org.bouncycastle.crypto.params.*; import org.bouncycastle.crypto.signers.SM2Signer; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.Strings; import org.bouncycastle.util.encoders.Base64; import org.bouncycastle.util.encoders.Hex; import java.math.BigInteger; import java.nio.charset.Charset; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.spec.ECGenParameterSpec; /** * SM2 工具類 * */ public class SM2Util { //SM2推薦曲線名稱 static String SM2_CURVE_NAME = "sm2p256v1"; public static final Charset UTF_8 = Charset.forName("utf-8"); /** * 生成密鑰 * * @return * @throws Exception */ public static KeyPair genKeyPair() throws Exception { final ECGenParameterSpec sm2Spec = new ECGenParameterSpec(SM2_CURVE_NAME); // 獲取一個(gè)橢圓曲線類型的密鑰對生成器 final KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider()); SecureRandom random = new SecureRandom(); // 使用SM2的算法區(qū)域初始化密鑰生成器 kpg.initialize(sm2Spec, random); // 獲取密鑰對 KeyPair keyPair = kpg.generateKeyPair(); return keyPair; } /**SM2根據(jù)公鑰加密 * param: message 待加密內(nèi)容 , publicKey 加密公鑰(BASE64編碼) * return: 加密信息的Base64編碼 * @throws InvalidCipherTextException * */ public static String encryptBySM2(String message, String publicKey) throws InvalidCipherTextException { ECDomainParameters domin = getDomain(); //公鑰對象 ECPublicKeyParameters pubKeyParameters = getPubKey(publicKey,domin); byte[] cipherBytes = new byte[0]; cipherBytes = encrypt(SM2Engine.Mode.C1C3C2, pubKeyParameters, message.getBytes(UTF_8)); return Base64.toBase64String(cipherBytes); } /** * SM2根據(jù)私鑰解密 * param: cipherText 待解密密文 privateKey-私鑰(BASE64編碼) * */ public static String decryptBySM2(String cipherText, String privateKey) throws InvalidCipherTextException { BigInteger d = new BigInteger(1,Base64.decode(privateKey)); ECDomainParameters domin = getDomain(); //私鑰對象 ECPrivateKeyParameters ecPrivateKeyParameters = new ECPrivateKeyParameters(d, domin); byte[] decrypt = decrypt(SM2Engine.Mode.C1C3C2, ecPrivateKeyParameters, Base64.decode(cipherText)); return new String(decrypt,UTF_8); } /** * 根據(jù)公鑰字符串創(chuàng)建公鑰對象 * * */ public static ECPublicKeyParameters getPubKey(String publicKey, ECDomainParameters domain) { ECCurve curve = domain.getCurve(); ECPoint point = curve.decodePoint(Base64.decode(publicKey)); ECPublicKeyParameters PublicKey = new ECPublicKeyParameters(point, domain); return PublicKey; } /** * @param mode 指定密文結(jié)構(gòu),舊標(biāo)準(zhǔn)的為C1C2C3,新的[《SM2密碼算法使用規(guī)范》 GM/T 0009-2012]標(biāo)準(zhǔn)為C1C3C2 * @param pubKeyParameters 公鑰 * @param srcData 原文 * @return 根據(jù)mode不同,輸出的密文C1C2C3排列順序不同。C1為65字節(jié)第1字節(jié)為壓縮標(biāo)識(shí),這里固定為0x04,后面64字節(jié)為xy分量各32字節(jié)。C3為32字節(jié)。C2長度與原文一致。 * @throws InvalidCipherTextException */ public static byte[] encrypt(SM2Engine.Mode mode, ECPublicKeyParameters pubKeyParameters, byte[] srcData) throws InvalidCipherTextException { SM2Engine engine = new SM2Engine(mode); ParametersWithRandom pwr = new ParametersWithRandom(pubKeyParameters, new SecureRandom()); engine.init(true, pwr); return engine.processBlock(srcData, 0, srcData.length); } /** * @param mode 指定密文結(jié)構(gòu),舊標(biāo)準(zhǔn)的為C1C2C3,新的[《SM2密碼算法使用規(guī)范》 GM/T 0009-2012]標(biāo)準(zhǔn)為C1C3C2 * @param priKeyParameters 私鑰 * @param sm2Cipher 根據(jù)mode不同,需要輸入的密文C1C2C3排列順序不同。C1為65字節(jié)第1字節(jié)為壓縮標(biāo)識(shí),這里固定為0x04,后面64字節(jié)為xy分量各32字節(jié)。C3為32字節(jié)。C2長度與原文一致。 * @return 原文。SM2解密返回了數(shù)據(jù)則一定是原文,因?yàn)镾M2自帶校驗(yàn),如果密文被篡改或者密鑰對不上,都是會(huì)直接報(bào)異常的。 * @throws InvalidCipherTextException */ public static byte[] decrypt(SM2Engine.Mode mode, ECPrivateKeyParameters priKeyParameters, byte[] sm2Cipher) throws InvalidCipherTextException { SM2Engine engine = new SM2Engine(mode); engine.init(false, priKeyParameters); return engine.processBlock(sm2Cipher, 0, sm2Cipher.length); } public static ECDomainParameters getDomain() { //獲取一條SM2曲線參數(shù) X9ECParameters x9ECParameters = GMNamedCurves.getByName(SM2_CURVE_NAME); //構(gòu)造domain參數(shù) ECDomainParameters domain = new ECDomainParameters( x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN(), x9ECParameters.getH() ); return domain; } /** * 私鑰簽名 * @param privateKey 私鑰 * @param content 待簽名內(nèi)容 * @return */ public static String sign(String privateKey, String content) throws CryptoException, CryptoException { //待簽名內(nèi)容轉(zhuǎn)為字節(jié)數(shù)組 byte[] message = content.getBytes(); BigInteger domainParameters = new BigInteger(1,Base64.decode(privateKey)); ECDomainParameters domin = getDomain(); //私鑰對象 ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(domainParameters, domin); //創(chuàng)建簽名實(shí)例 SM2Signer sm2Signer = new SM2Signer(); //初始化簽名實(shí)例,帶上ID,國密的要求,ID默認(rèn)值:1234567812345678 try { sm2Signer.init(true, new ParametersWithID(new ParametersWithRandom(privateKeyParameters, SecureRandom.getInstance("SHA1PRNG")), Strings.toByteArray("1234567812345678"))); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } sm2Signer.update(message, 0, message.length); //生成簽名,簽名分為兩部分r和s,分別對應(yīng)索引0和1的數(shù)組 byte[] signBytes = sm2Signer.generateSignature(); String sign = Base64.toBase64String(signBytes); return sign; } /** * 驗(yàn)證簽名 * @param publicKey 公鑰 * @param content 待簽名內(nèi)容 * @param sign 簽名值 * @return */ public static boolean verify(String publicKey, String content, String sign) { //待簽名內(nèi)容 byte[] message = content.getBytes(); byte[] signData = Base64.decode(sign); // 獲取一條SM2曲線參數(shù) X9ECParameters sm2ECParameters = GMNamedCurves.getByName(SM2_CURVE_NAME); // 構(gòu)造domain參數(shù) ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN()); //提取公鑰點(diǎn) ECPoint pukPoint = sm2ECParameters.getCurve().decodePoint(java.util.Base64.getDecoder().decode(publicKey)); // 公鑰前面的02或者03表示是壓縮公鑰,04表示未壓縮公鑰, 04的時(shí)候,可以去掉前面的04 ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, domainParameters); //創(chuàng)建簽名實(shí)例 SM2Signer sm2Signer = new SM2Signer(); ParametersWithID parametersWithID = new ParametersWithID(publicKeyParameters, Strings.toByteArray("1234567812345678")); sm2Signer.init(false, parametersWithID); sm2Signer.update(message, 0, message.length); //驗(yàn)證簽名結(jié)果 boolean verify = sm2Signer.verifySignature(signData); return verify; } }
SM3
國產(chǎn)哈希算法,也叫消息摘要算法,可以用MD5作為對比理解。該算法已公開。校驗(yàn)結(jié)果為256位。SM3采用的一種密碼散列函數(shù)標(biāo)準(zhǔn),由GJMM管理局于2010年12月17日發(fā)布。相關(guān)標(biāo)準(zhǔn)為“GM/T 0004-2012 《SM3密碼雜湊算法》”。
在商用密碼體系中,SM3主要用于數(shù)字簽名及驗(yàn)證、消息認(rèn)證碼生成及驗(yàn)證、隨機(jī)數(shù)生成等,其算法公開。據(jù)GJMM管理局表示,其安全性及效率與SHA-256相當(dāng)。
代碼:SM3Util
package GMSM; import org.bouncycastle.crypto.digests.SM3Digest; import org.bouncycastle.util.encoders.Hex; import java.util.Arrays; public class SM3Util { /** * 計(jì)算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; } /** * 驗(yàn)證摘要 * * @param srcData 原文 * @param sm3Hash 摘要值 * @return 返回true標(biāo)識(shí)驗(yàn)證成功,false標(biāo)識(shí)驗(yàn)證失敗 */ public static boolean verify(byte[] srcData, byte[] sm3Hash) { byte[] newHash = hash(srcData); System.out.println(Hex.toHexString(newHash)); System.out.println(Hex.toHexString(sm3Hash)); if (Arrays.equals(newHash, sm3Hash)) { return true; } else { return false; } } }
SM4
無線局域網(wǎng)標(biāo)準(zhǔn)的分組數(shù)據(jù)算法。對稱加密,密鑰長度和分組長度均為128位。對標(biāo)AES
代碼:SM4Util
package GMSM; import org.bouncycastle.jce.provider.BouncyCastleProvider; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.security.*; public class SM4Util { static { Security.addProvider(new BouncyCastleProvider()); } public static final String ALGORITHM_NAME = "SM4"; // AES public static final String DEFAULT_KEY = "random_seed"; // 128-32位16進(jìn)制;256-64位16進(jìn)制 public static final int DEFAULT_KEY_SIZE = 128; public static byte[] generateKey() throws NoSuchAlgorithmException, NoSuchProviderException { return generateKey(DEFAULT_KEY, DEFAULT_KEY_SIZE); } public static byte[] generateKey(String seed) throws NoSuchAlgorithmException, NoSuchProviderException { return generateKey(seed, DEFAULT_KEY_SIZE); } public static byte[] generateKey(String seed, int keySize) throws NoSuchAlgorithmException, NoSuchProviderException { KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM_NAME, BouncyCastleProvider.PROVIDER_NAME); SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); if (null != seed && !"".equals(seed)) { random.setSeed(seed.getBytes()); } kg.init(keySize, random); return kg.generateKey().getEncoded(); } /** * @description 加密 */ public static byte[] encrypt(String algorithmName, byte[] key, byte[] iv, byte[] data) throws Exception { return sm4core(algorithmName, Cipher.ENCRYPT_MODE, key, iv, data); } /** * @description 解密 */ public static byte[] decrypt(String algorithmName, byte[] key, byte[] iv, byte[] data) throws Exception { return sm4core(algorithmName, Cipher.DECRYPT_MODE, key, iv, data); } private static byte[] sm4core(String algorithmName, int type, byte[] key, byte[] iv, byte[] data) throws Exception { Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME); Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME); if (algorithmName.contains("/ECB/")) { cipher.init(type, sm4Key); } else { if(iv == null ){ cipher.init(type, sm4Key); }else{ IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); cipher.init(type, sm4Key, ivParameterSpec); } } return cipher.doFinal(data); } }
四、各類支付系統(tǒng)常見加密方式組合介紹
在支付系統(tǒng)交互中,有多種多樣的加密方式組合,這里就簡單介紹兩鐘常用的。
1.傳統(tǒng)方式
用可逆,非對稱算法(rsa,sm2等)使用對方公鑰對報(bào)文內(nèi)的關(guān)鍵信息進(jìn)行加密,得到的密文進(jìn)行編碼,然后再對密文編碼后的串加鹽后使用不可逆算法(sha256,md5,sm3等)進(jìn)行簽名,使用這些算法簽名后得到的是一個(gè)16進(jìn)制的串,簽名放到另一個(gè)字段,一般是和加密后的信息并列的。
步驟:
1.使用對方的RSA公鑰對明文進(jìn)行加密,得到的密文進(jìn)行base64編碼,作為data
2.將data的值加上約定好的鹽,使用sha256算法進(jìn)行簽名,得到的簽名是16進(jìn)制的串,放到sign
加密后的json:
{ "data": "auwPDINowtdseZTfcCR8B1OPsevJE1MqIVOLn56WDvw3gjksSjkpMGp4lpYiqZkJ9G2PTaYC2DZSiyhbjtLvfnJE54mZz649gTWQVIHAn5fzV8Vs3JL3Kf9WB0Br9EbR07qrfGlWCvkkgktmTfDS/4qQGywn7TVQ3EH6HwQI7kUf0GlZ9CYcYHKi4/F61s6H9Hws+CPrhchGfJmHnu+835dSVS4IJOnA+0S5JXWVEN/is5i5cmxFb3RUYZbfpu+7JLDgcXLC8oNKIZxSE1aXhoFMj4CTTYCyRxKNCD015fnBMKh+7TYHEksyyybtVjkPqDeczgT3z+Qvj9vdyUZB1Q==", "sign": "d3b36e7a837d6241445e506faf72c8d8e3467ee9c11efae0c67dfb5b15f44b6e" }
接收后,先拿到密文,加入約定好的鹽進(jìn)行驗(yàn)簽,驗(yàn)簽通過后再用私鑰進(jìn)行解密。返回報(bào)文同理,只不過返回報(bào)文用的也是對方的公鑰加密,對方接收后用自己的私鑰解密。
demo:RSASha256Test
package RSASha256; import java.util.HashMap; import java.util.Map; public class RSASha256Test { public static void main(String[] args) { Map<String,String> jsonData = new HashMap<>(); String publicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuazymB25s/ueIfwMM2H74736dQQE4bvRDgNfWltM4PzduNC84ntav41qEgbZtby47O57m/YDVf6vMXJH25ejlsBBhls7FD7xHTkOrskZqLekH0xCs2xa/akdwllbewUNzvWMuQy8X3j2VnrILXzVjvqYxkFY0itMo8fq1xSO6B/S5/RaeKpTtIepFB2cIU9vMrtLoCsnjVoTuPdWcoC79LtS0g1Q5BV4dn+Uca+8/gUbhS7vbth3oLZt6gXTRjJrPxhT4lqWsCZqwkQHPG/8JQgnFqs7+R0KcyemG/411IaZI9WYRGTsQji8sQ2q7HAlZRXvJn+As7jJPvyi67JGKwIDAQAB"; String privateKey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC5rPKYHbmz+54h/AwzYfvjvfp1BAThu9EOA19aW0zg/N240Lzie1q/jWoSBtm1vLjs7nub9gNV/q8xckfbl6OWwEGGWzsUPvEdOQ6uyRmot6QfTEKzbFr9qR3CWVt7BQ3O9Yy5DLxfePZWesgtfNWO+pjGQVjSK0yjx+rXFI7oH9Ln9Fp4qlO0h6kUHZwhT28yu0ugKyeNWhO491ZygLv0u1LSDVDkFXh2f5Rxr7z+BRuFLu9u2Hegtm3qBdNGMms/GFPiWpawJmrCRAc8b/wlCCcWqzv5HQpzJ6Yb/jXUhpkj1ZhEZOxCOLyxDarscCVlFe8mf4CzuMk+/KLrskYrAgMBAAECggEABjC+8dVj4J1N+2IU4g2tQT2PQSF+LCx/3tC7+B49JO8pUUUcVwy3zNUhKTKzRXziSXv2ARAlslNIcgSWYrreiGMmjB00jgs/LLM/SxKHWXmt7iEzxBmjuvtNc7JY+3QCrti+9Vh4W1KEHAQB8opL8HVobIu3M2KgLoG20a7syM5gPiCUb3EQ6P30u47y7I2wsbMnMmu79rPu3Q7lSNeoB9KdVV6EA6vukL3chY1QxFh6oFZS1yNjFjxy5RxT8U5Juhr5LYRnOP8MEVptbtrh34SSm0uyckkgJ6jOsrDJqo/DJxjPxixj+BWgG5/XbNq3PFEyEAqkrTZY+FMHSufoaQKBgQDpqPxpl05mDUXCnc7I6F8LZqjnjZU1h9zwKuQe94Fs3I3siWS62sJx1pZ66P9eeIOsT0T4Ye9nDx2x04yyfUcENhydFTQ5M5M9e2q1kKdNeBUT6Xy4WcRibKozUq07mLYSn0kDbuimp/Erp2w3hl5nZu/eLKDjt7yM0otv3uPp5wKBgQDLbYC4hcvk+f1kd/xJQ2ljwF+c8kHHFrlLRipf7qTB24dcrauWXbAGbbcKXx2G+3Waswyf09NdALiWab/jUUUHJm0YGxyr0k5lHci0/eC7hD3tWTL/HRyeF3umKZEkBQypzNeymE6t0lSqjL3MXFQLqumu4h677qA9/DNh7DYhHQKBgQCfAm/bj6s7iba6jVfmozPi91bkVRaAWlgBXL7nT/nU0ncGzC0vd6WxgJ3hQORgLtU0krFV8pfP45qKpHNwGA8XD5gDUiW68500zuM8chdYgeqeJVvJvNUHQfnFeXMIRpFJNPqkCnrqxwk5cvMTCi7+YS/FW0uWDDiVAMcBN4aUawKBgCOgvAiVNk6WEfEEqqTSL6UOzjAYpbiOnEk4src2fpiNMDnlGMYvBmM51/LzEaLQa5p6fV2Ipd4GAE4nmznew+4qprSwGudk3+IJw1sfk7qDwKzPEIVpvddaWYeShB8A22TpwWVAE5eR3M459AvUp8ubVW4RoDxd4Ka6gu1Fh31pAoGAYNPKJNCrdOsQH0nkH0Ld44cH6HD+zcZtoad2eQk0T2SPnKBsIS1S9W0adreOXUv1edO/hN3P/BcTuCaax7ijTscS2f0atc/NIa6LjBnK7oUBBzib9v21L72ZVZ5st4c/H7IzbQCXfS81489a7TTHP+e1HzS/XePftO0pAkr1GJ0="; String salt = "E4bvRDg"; System.out.println("公鑰為:" + publicKey); String plaintext = "{'a':'1111','b':'2222'}"; String ciphertext = null; String sign = null; try { ciphertext = RSAUtil.encrypt(plaintext,publicKey); jsonData.put("data",ciphertext); System.out.println("第一步,使用對方的RSA公鑰對明文進(jìn)行加密,得到的密文進(jìn)行base64編碼,作為data:"+jsonData.get("data")); sign = SHA256Util.getSha256Str(ciphertext+salt); jsonData.put("sign",sign); System.out.println("第二步,將data的值加上約定好的鹽,使用sha256算法進(jìn)行簽名,得到的簽名是16進(jìn)制的串,放到sign:"+jsonData.get("sign")); } catch (Exception e) { System.out.println("加密失敗"); throw new RuntimeException(e); } System.out.println("得到的密文和簽名為:"+jsonData); System.out.println("模擬另一方收到密文和簽名之后.........."); System.out.println("私鑰為:" + privateKey); String ciphertext1 = jsonData.get("data"); String sign1 = jsonData.get("sign"); String deciphering1 = null; if(sign1.equals(SHA256Util.getSha256Str(ciphertext1+salt))){ System.out.println("簽名驗(yàn)證成功"); deciphering1 = RSAUtil.decrypt(ciphertext1,privateKey); System.out.printf("數(shù)據(jù)解密成功,data值為:"+deciphering1); }else{ System.out.println("簽名驗(yàn)證失敗,解密失敗"); } } }
2. 一次一密+簽名身份驗(yàn)證
生成一個(gè)對稱算法秘鑰,使用該秘鑰對明文進(jìn)行加密,然后將該秘鑰使用對方的公鑰進(jìn)行加密,加密后編碼,然后再對明文使用自己的私鑰進(jìn)行簽名,因?yàn)樗借€只有自己有,所以用自己的私鑰簽名后,對方使用你提供的公鑰進(jìn)行驗(yàn)簽,就可以驗(yàn)證你的身份。
步驟:
1. 隨機(jī)生成一個(gè)SM4密鑰;
2. 使用SM4密鑰加密密文,得到的密文進(jìn)行base64編碼,作為textToDecrypt
3. 將SM4密鑰進(jìn)行base64編碼后,再使用對方的SM2公鑰對這個(gè)base64串進(jìn)行加密,得到的密文進(jìn)行base64編碼,作為keyCiphertext;
4. 對第二步的明文使用第三方sm2私鑰簽名,得到的簽名進(jìn)行base64編碼后作為signature。
加密后json:
{ "textToDecrypt": "cU0ymFMho3HXmVq0hwDHvZg9oLsuZT19GLBKcvHzdZ4=", "signature": "MEUCIA8nO1g705B3zzmaYv7yK8jajz9r2fKuvSPZliY8k1xBAiEA0noAEiBos3fU7RwEt81jTjwlrQL+tbQfy77VK/h/ES4=", "keyCiphertext": "BJv91ULAd6fNchtKF/+b1JW2o7il0Hjbr6erQrY96S8QrDrkE7Y6EpgzM/eY1OumNsr6VbRuK0wx6yo8fq3QapYHZU5X1LzjgyMC+AiCnhzut5V3xxz4Yd0M1Zx2D4ljaOnJTDED4nS5kU2+C5VFF81DyzVDmWw6ZQ==" }
對方接收后,按照以上步驟再進(jìn)行驗(yàn)簽,解密等操作。
demo:GMSMTest
package GMSM; import java.util.HashMap; import java.util.Map; import java.util.Base64; public class GMSMTest { public static void main(String[] args) throws Exception { // 你這邊拿到的sm2公私鑰,對方的公鑰和自己的私鑰 String priK = "AMz5m/WOgpgYiXnlckC7+zdvZXnQYMBcWXt1n7khfdVO"; String otherPubK = "BJkVGU87s7xhLrFCI8MGPzp2X+lizte+2So52CQ1tVEDDqOe+Q4vdQXglV2JtIOZeWc/bE8Rzdo29svmTj8+7mA="; String messge = "{'aaa':'111','bbb':'222'}"; Map<String, String> jsonData = new HashMap<>(); byte[] keyB = SM4Util.generateKey(null); String key = Base64.getEncoder().encodeToString(keyB); String algorithmName = ("SM4/ECB/PKCS5PADDING"); System.out.println("第一步,隨機(jī)生成一個(gè)SM4秘鑰:"+key); System.out.println("選定算法:"+algorithmName); // 用隨機(jī)生成的SM4秘鑰加密 byte[] encryptMSG = SM4Util.encrypt(algorithmName, keyB, null, messge.getBytes()); jsonData.put("textToDecrypt",Base64.getEncoder().encodeToString(encryptMSG)); System.out.println("第二步,使用SM4密鑰加密明文,得到的密文進(jìn)行base64編碼,作為textToDecrypt :"+jsonData.get("textToDecrypt")); // 將隨機(jī)生成的SM4秘鑰,使用對方的SM2公鑰進(jìn)行加密 String keyCiphertext = SM2Util.encryptBySM2(key, otherPubK); jsonData.put("keyCiphertext",keyCiphertext); System.out.println("第三步,將SM4密鑰進(jìn)行base64編碼后,再使用對方的SM2公鑰對這個(gè)base64串進(jìn)行加密,得到的密文進(jìn)行base64編碼,作為 keyCiphertext;"+jsonData.get("keyCiphertext")); // 對明文使用自己的sm2私鑰進(jìn)行簽名 如果有需要,可以加點(diǎn)鹽 String signature = SM2Util.sign(priK,messge); jsonData.put("signature",signature); System.out.println("第四步,對第二步的明文使用對方sm2私鑰簽名,得到的簽名進(jìn)行base64編碼后作為 signature:"+jsonData.get("signature")); System.out.println("完整json串:"+jsonData); // 模擬接到j(luò)son傳之后的處理 // 對方拿到的,你的公鑰,和對方自己的私鑰 String PubK = "BNTFr3oo1xm3biGoQQnk20QJfbFvK4HNFgs1DkZJuUT9pW8I7K0D9L9P4mW+tQhWwfJlD/Nsx3BY+oriF1B25bA="; String otherPriK = "I0sHzcDm+oS2FIAHFLxxkWJi1AbrHRxEYs1AxpZTlNw="; // 將sm4秘鑰進(jìn)行解密 String keyCiphertext1 = SM2Util.decryptBySM2(jsonData.get("keyCiphertext"), otherPriK); System.out.println("對方使用sm2私鑰解密sm4秘鑰:"+keyCiphertext1); // 通過sm4秘鑰解密數(shù)據(jù)密文 byte[] textToDecrypts = SM4Util.decrypt(algorithmName, Base64.getDecoder().decode(keyCiphertext1), null, Base64.getDecoder().decode(jsonData.get("textToDecrypt").getBytes())); String textToDecryptsS = new String(textToDecrypts); System.out.println("對方使用sm4秘鑰解密后的數(shù)據(jù):"+ textToDecryptsS); // 驗(yàn)證簽名 boolean proving = SM2Util.verify(PubK, textToDecryptsS, jsonData.get("signature")); System.out.println("驗(yàn)證簽名是否通過:"+proving); } }
國密依賴pom
<dependencies> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>30.1-jre</version> </dependency> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15on</artifactId> <version>1.70</version> </dependency> </dependencies>
以上就是Java實(shí)現(xiàn)支付對接常用加密方式的示例代碼的詳細(xì)內(nèi)容,更多關(guān)于Java支付加密方式的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Springmvc如何實(shí)現(xiàn)向前臺(tái)傳遞數(shù)據(jù)
這篇文章主要介紹了Springmvc如何實(shí)現(xiàn)向前臺(tái)傳遞數(shù)據(jù),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07java實(shí)現(xiàn)監(jiān)控rtsp流轉(zhuǎn)flv方法實(shí)例(前端播放,前后端代碼都有)
這篇文章主要給大家介紹了關(guān)于java實(shí)現(xiàn)監(jiān)控rtsp流轉(zhuǎn)flv的相關(guān)資料,文中介紹的是前端播放,前后端代碼都有,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-06-06java 讀取網(wǎng)頁內(nèi)容的實(shí)例詳解
這篇文章主要介紹了java 讀取網(wǎng)頁內(nèi)容的實(shí)例詳解的相關(guān)資料,希望通過本文能幫助到大家,讓大家學(xué)習(xí)理解這部分內(nèi)容,需要的朋友可以參考下2017-09-09學(xué)習(xí)不同 Java.net 語言中類似的函數(shù)結(jié)構(gòu)
這篇文章主要介紹了學(xué)習(xí)不同 Java.net 語言中類似的函數(shù)結(jié)構(gòu),函數(shù)式編程語言包含多個(gè)系列的常見函數(shù)。但開發(fā)人員有時(shí)很難在語言之間進(jìn)行切換,因?yàn)槭煜さ暮瘮?shù)具有不熟悉的名稱。函數(shù)式語言傾向于基于函數(shù)范例來命名這些常見函數(shù)。,需要的朋友可以參考下2019-06-06mac下修改idea的jvm運(yùn)行參數(shù)解決idea卡頓的情況
這篇文章主要介紹了mac下修改idea的jvm運(yùn)行參數(shù)解決idea卡頓的情況,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12WebDriver中實(shí)現(xiàn)對特定的Web區(qū)域截圖方法
這篇文章主要介紹了WebDriver中實(shí)現(xiàn)對特定的Web區(qū)域截圖方法,本文直接給出實(shí)現(xiàn)代碼,需要的朋友可以參考下2015-06-06