加密分为对称加密非对称加密

对称加密:加密解密使用相同的密钥串来完成

非对称加密:分为公钥(publicKey)与私钥(privateKey)

大致步骤

1)服务端使用RSA算法生成密钥对,其中公钥给调用方,私钥保存到服务器或者服务器数据库中,防止泄露

2)请求前,对约定的参数使用公钥进行加密,并将加密结果作为参数传递给服务端

3)服务端接收到参数,对签名部分使用私钥解密,得到明文数据,并验证,再对数据进行相应处理

此处以同步接口一为例说明:

{
param1 : xxx, // 业务参数1
param2 : xxx, // 业务参数2
param3 : xxx, // 业务参数2
nonce: uuidStr, // 取32位uuid,用于不可重放校验
timestamp: yyyyMMddHHmmss, // 时间戳
sign : sign("nonce=uuidStr×tamp=yyyyMMddHHmmss¶m1=xxx¶m2=xxx¶m3=xxx")
}

param1、param2、param3是业务参数入参示例;

nonce由客户端自己uuid()生成,去除横杠,小写32位,用于服务端做 不可重放校验

sign是签名串,用于保护业务入参及nonce不被篡改,sign()指的顶象的签名函数伪代码.

1、签名原文内容中的参数使用&分隔,参数顺序在每个接口文档自行定义好;

2、签名内容中的参数param2的值如果为空或者不存在或null等,无需该参数param2,例如:sign : sign("nonce=uuidStr&param1=xxx&param3=xxx");

3、参与签名的参数为入参中排除signature列表外的所有参数,字符串顺序按照参数名ASCII码从小到大排序

以下为调用端加密实例:

package com.yuntai.ssm.manager.util;

import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.text.SimpleDateFormat;
import java.util.*;

public class RSAUtil {
    
    public static void main(String[] args) throws Exception {
        String publicKey = "<您的公钥>";
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        //验证签名
        Map<String, String> signMap = new HashMap<>();
        signMap.put("timestamp", df.format(new Date()));
        signMap.put("nonce", UUID.randomUUID().toString().replaceAll("-",""));
        signMap.put("orderId", "123");
        signMap.put("orderCode", "orderCode123456");
        signMap.put("orderTime", df.format(new Date()));
        signMap.put("payTime", df.format(new Date()));
        signMap.put("payOrderNo", df.format(new Date()));
        signMap.put("customerPhone", "13333333333");
        signMap.put("totalPrice", "0.01");
        signMap.put("openId", "1234567");
        signMap.put("discountCost", "0.01");
        signMap.put("discountType", "满减");
        signMap.put("payType", "13333333333");
        signMap.put("orderSource", "0.01");
        signMap.put("receiveName", "xxxx");
        signMap.put("receivePhone","13333333333");
        signMap.put("receiveAddress", "门诊一楼");
        signMap.put("patientDept", "耳鼻喉科");
        signMap.put("refundAmount", null);
        signMap.put("refundApplyTime", null);
        signMap.put("refundStatus", null);
        signMap.put("refundTime", null);
        
        // map排序后拼接
        List<String> keys= new ArrayList<>(signMap.keySet());
        Collections.sort(keys);
        StringBuffer content = new StringBuffer();
        for (int i=0; i<keys.size();i++) {
            String key = keys.get(i);
            String value = signMap.get(key);
            if(value != null && value.length() != 0) {
                content.append((i == 0 ? "" : "&") + key + "=" + value);
            }
        }
        
        String encryptData = RSAUtil.encrypt(RSAUtil.loadPublicKey(publicKey), 
                                             content.toString().getBytes());
        System.out.println("加密后签名encryptData:" + encryptData);
    }
    
    /**
    * 根据公钥字符串加载公钥
    *
    * @param publicKeyStr 公钥字符串
    * @return
    * @throws Exception
    */
    public static RSAPublicKey loadPublicKey(String publicKeyStr) throws Exception {
        try {
            byte[] buffer = javax.xml.bind.DatatypeConverter.parseBase64Binary(
                publicKeyStr);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
            return (RSAPublicKey) keyFactory.generatePublic(keySpec);
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此算法", e);
        } catch (InvalidKeySpecException e) {
            throw new Exception("公钥非法", e);
        } catch (NullPointerException e) {
            throw new Exception("公钥数据为空", e);
        }
    }
    
    /**
    * 公钥加密
    *
    * @param publicKey     公钥
    * @param plainTextData 明文数据
    * @return
    * @throws Exception 加密过程中的异常信息
    */
    public static String encrypt(RSAPublicKey publicKey, byte[] plainTextData) throws Exception {
        if (publicKey == null) {
            throw new Exception("加密公钥为空, 请设置");
        }
        Cipher cipher = null;
        try {
            // 使用默认RSA
            cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            InputStream ins = new ByteArrayInputStream(plainTextData);
            ByteArrayOutputStream writer = new ByteArrayOutputStream();
            // rsa加密的字节大小最多是117,将需要解密的内容,按117位拆开解密
            byte[] buf = new byte[117];
            int bufl;
            
            while ((bufl = ins.read(buf)) != -1) {
                byte[] block = null;
                if (buf.length == bufl) {
                    block = buf;
                } else {
                    block = new byte[bufl];
                    for (int i = 0; i < bufl; i++) {
                        block[i] = buf[i];
                    }
                }
                writer.write(cipher.doFinal(block));
            }
            return Base64.encode(writer.toByteArray());
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此加密算法");
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
            return null;
        } catch (InvalidKeyException e) {
            throw new Exception("加密公钥非法,请检查");
        } catch (IllegalBlockSizeException e) {
            throw new Exception("明文长度非法");
        } catch (BadPaddingException e) {
            throw new Exception("明文数据已损坏");
        }
    }
}

服务端接收到参数后,解密并验证

@PostMapping("/model1/syncOrderInfo")
    public SmartCabinetResponse syncOrderDataFromSmartCabinet(@Validated(SmartCabinetOrder.SyncOrderInfo.class) @RequestBody SmartCabinetOrder order) throws Exception {

        if (StringUtils.isEmpty(order.getSignature())) {
            return SmartCabinetResponse.builder().message("签名为空").code(-1).build();
        }
        SecretKey cabinetSecretKey = SecretKey.builder().privateKey("取的私钥").build();

        // 验证签名
        Map<String, String> signMap = new HashMap<>();
        signMap.put("timestamp", order.getTimestamp());
        signMap.put("nonce", order.getNonce());
        signMap.put("orderId", order.getOrderId().toString());
        signMap.put("orderCode", order.getOrderCode());
        signMap.put("orderTime", order.getOrderTime());
        signMap.put("payTime", order.getPayTime());
        signMap.put("payOrderNo", order.getPayOrderNo());
        signMap.put("customerPhone", order.getCustomerPhone());
        signMap.put("totalPrice", order.getTotalPrice().toString());
        signMap.put("openId", order.getOpenId());
        signMap.put("discountCost", order.getDiscountCost());
        signMap.put("discountType", order.getDiscountType());
        signMap.put("payType", order.getPayType());
        signMap.put("orderSource", order.getOrderSource());
        signMap.put("receiveName", order.getReceiveName());
        signMap.put("receivePhone",order.getReceivePhone());
        signMap.put("receiveAddress", order.getReceiveAddress());
        signMap.put("patientDept", order.getPatientDept());
        signMap.put("refundAmount", order.getRefundAmount());
        signMap.put("refundApplyTime", order.getRefundApplyTime());
        signMap.put("refundStatus", order.getRefundStatus());
        signMap.put("refundTime", order.getRefundTime());

        List<String> keys= new ArrayList<>(signMap.keySet());
        Collections.sort(keys);
        StringBuffer content = new StringBuffer();
        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            String value = signMap.get(key);
            if(value != null && value.length() != 0) {
                content.append((i == 0 ? "" : "&") + key + "=" + value);
            }
        }

        String decryptStr = RSAUtil.decrypt(RSAUtil.loadPrivateKey(cabinetSecretKey.getPrivateKey()), Base64.getDecoder().decode(order.getSignature()));
        if (!StringUtils.equals(decryptStr, content.toString())) {
            return SmartCabinetResponse.builder().message("签名信息不正确").code(-1).build();
        }
        return 《相应数据处理接口》;
    }

附RSAUtil工具

package com.lecshop.util;


import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
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.HashMap;
import java.util.Map;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

public class RSAUtil {

    /**
     * 生成RAS公钥与私钥字符串,直接返回
     *
     * @return
     */
    public static HashMap<String, String> getKeys() {
        HashMap<String, String> map = new HashMap<String, String>();
        KeyPairGenerator keyPairGen = null;
        try {
            keyPairGen = KeyPairGenerator.getInstance("RSA");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        // 初始化密钥对生成器,密钥大小为96-1024位
        keyPairGen.initialize(1024, new SecureRandom());
        // 生成一个密钥对,保存在keyPair中
        KeyPair keyPair = keyPairGen.generateKeyPair();
        // 得到公钥字符串
        String publicKey = base64ToStr(keyPair.getPublic().getEncoded());
        // 得到私钥字符串
        String privateKey = base64ToStr(keyPair.getPrivate().getEncoded());
        map.put("publicKey", publicKey);
        map.put("privateKey", privateKey);
        return map;
    }

    public static String base64ToStr(byte[] b) {
        return javax.xml.bind.DatatypeConverter.printBase64Binary(b);
    }

    /**
     * 根据公钥字符串加载公钥
     *
     * @param publicKeyStr 公钥字符串
     * @return
     * @throws Exception
     */
    public static RSAPublicKey loadPublicKey(String publicKeyStr) throws Exception {
        try {
            byte[] buffer = javax.xml.bind.DatatypeConverter.parseBase64Binary(publicKeyStr);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
            return (RSAPublicKey) keyFactory.generatePublic(keySpec);
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此算法", e);
        } catch (InvalidKeySpecException e) {
            throw new Exception("公钥非法", e);
        } catch (NullPointerException e) {
            throw new Exception("公钥数据为空", e);
        }
    }

    /**
     * 根据私钥字符串加载私钥
     *
     * @param privateKeyStr 私钥字符串
     * @return
     * @throws Exception
     */
    public static RSAPrivateKey loadPrivateKey(String privateKeyStr) throws Exception {
        try {
            byte[] buffer = javax.xml.bind.DatatypeConverter.parseBase64Binary(privateKeyStr);
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此算法", e);
        } catch (InvalidKeySpecException e) {
            throw new Exception("私钥非法", e);
        } catch (NullPointerException e) {
            throw new Exception("私钥数据为空", e);
        }
    }

    /**
     * 公钥加密
     *
     * @param publicKey     公钥
     * @param plainTextData 明文数据
     * @return
     * @throws Exception 加密过程中的异常信息
     */
    public static String encrypt(RSAPublicKey publicKey, byte[] plainTextData) throws Exception {
        if (publicKey == null) {
            throw new Exception("加密公钥为空, 请设置");
        }
        Cipher cipher = null;
        try {
            // 使用默认RSA
            cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            InputStream ins = new ByteArrayInputStream(plainTextData);
            ByteArrayOutputStream writer = new ByteArrayOutputStream();
            // rsa加密的字节大小最多是117,将需要解密的内容,按117位拆开解密
            byte[] buf = new byte[117];
            int bufl;

            while ((bufl = ins.read(buf)) != -1) {
                byte[] block = null;
                if (buf.length == bufl) {
                    block = buf;
                } else {
                    block = new byte[bufl];
                    for (int i = 0; i < bufl; i++) {
                        block[i] = buf[i];
                    }
                }
                writer.write(cipher.doFinal(block));
            }
            return Base64.encode(writer.toByteArray());
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此加密算法");
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
            return null;
        } catch (InvalidKeyException e) {
            throw new Exception("加密公钥非法,请检查");
        } catch (IllegalBlockSizeException e) {
            throw new Exception("明文长度非法");
        } catch (BadPaddingException e) {
            throw new Exception("明文数据已损坏");
        }
    }

    /**
     * 私钥解密
     *
     * @param privateKey 私钥
     * @param cipherData 密文数据
     * @return 明文
     * @throws Exception 解密过程中的异常信息
     */
    public static String decrypt(RSAPrivateKey privateKey, byte[] cipherData) throws Exception {
        if (privateKey == null) {
            throw new Exception("解密私钥为空, 请设置");
        }
        Cipher cipher = null;
        try {
            // 使用默认RSA
            cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            InputStream ins = new ByteArrayInputStream(cipherData);
            ByteArrayOutputStream writer = new ByteArrayOutputStream();
            //rsa解密的字节大小最多是128,将需要解密的内容,按128位拆开解密
            byte[] buf = new byte[128];
            int bufl;

            while ((bufl = ins.read(buf)) != -1) {
                byte[] block = null;

                if (buf.length == bufl) {
                    block = buf;
                } else {
                    block = new byte[bufl];
                    for(int i = 0; i < bufl; i++) {
                        block[i] = buf[i];
                    }
                }
                writer.write(cipher.doFinal(block));
            }
            return new String(writer.toByteArray(), "utf-8");
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此解密算法");
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
            return null;
        } catch (InvalidKeyException e) {
            throw new Exception("解密私钥非法,请检查");
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
            throw new Exception("密文长度非法");
        } catch (BadPaddingException e) {
            e.printStackTrace();
            throw new Exception("密文数据已损坏");
        }
    }
}