欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java實(shí)現(xiàn)支付對接常用加密方式的示例代碼

 更新時(shí)間:2023年02月09日 11:29:35   作者:京東云開發(fā)者  
這篇文章主要為大家詳細(xì)介紹了Java如何實(shí)現(xiàn)支付對接時(shí)常用加密方式,文中的示例代碼講解詳細(xì),對我們學(xué)習(xí)Java有一點(diǎ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ù)

    這篇文章主要介紹了Springmvc如何實(shí)現(xiàn)向前臺(tái)傳遞數(shù)據(jù),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-07-07
  • Go Java算法之字符串中第一個(gè)唯一字符詳解

    Go Java算法之字符串中第一個(gè)唯一字符詳解

    這篇文章主要為大家介紹了Go Java算法之字符串中第一個(gè)唯一字符詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08
  • SpringBoot控制配置類加載順序方式

    SpringBoot控制配置類加載順序方式

    這篇文章主要介紹了SpringBoot控制配置類加載順序方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • java實(shí)現(xiàn)監(jiān)控rtsp流轉(zhuǎn)flv方法實(shí)例(前端播放,前后端代碼都有)

    java實(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-06
  • Java中常見的文件拷貝方式小結(jié)

    Java中常見的文件拷貝方式小結(jié)

    這篇文章主要為大家詳細(xì)介紹了JAVA?四種拷貝文件的方式,分析一下他們對內(nèi)存使用的方式和各自應(yīng)用的場景,其實(shí)也是對之前學(xué)過的知識(shí)做一個(gè)回顧吧,快跟隨小編一起學(xué)習(xí)起來吧
    2024-03-03
  • java 讀取網(wǎng)頁內(nèi)容的實(shí)例詳解

    java 讀取網(wǎng)頁內(nèi)容的實(shí)例詳解

    這篇文章主要介紹了java 讀取網(wǎng)頁內(nèi)容的實(shí)例詳解的相關(guān)資料,希望通過本文能幫助到大家,讓大家學(xué)習(xí)理解這部分內(nèi)容,需要的朋友可以參考下
    2017-09-09
  • 解決SpringBoot整合RocketMQ遇到的坑

    解決SpringBoot整合RocketMQ遇到的坑

    這篇文章主要介紹了解決SpringBoot整合RocketMQ遇到的坑,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • 學(xué)習(xí)不同 Java.net 語言中類似的函數(shù)結(jié)構(gòu)

    學(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-06
  • mac下修改idea的jvm運(yùn)行參數(shù)解決idea卡頓的情況

    mac下修改idea的jvm運(yùn)行參數(shù)解決idea卡頓的情況

    這篇文章主要介紹了mac下修改idea的jvm運(yùn)行參數(shù)解決idea卡頓的情況,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • WebDriver中實(shí)現(xiàn)對特定的Web區(qū)域截圖方法

    WebDriver中實(shí)現(xiàn)對特定的Web區(qū)域截圖方法

    這篇文章主要介紹了WebDriver中實(shí)現(xiàn)對特定的Web區(qū)域截圖方法,本文直接給出實(shí)現(xiàn)代碼,需要的朋友可以參考下
    2015-06-06

最新評論