使用RSA算法對接口參數簽名及驗籤

  在不一樣的服務器或系統之間經過API接口進行交互時,兩個系統之間必須進行身份的驗證,以知足安全上的防抵賴和防篡改。java

  一般狀況下爲了達到以上所描述的目的,咱們首先會想到使用非對稱加密算法對傳輸的數據進行簽名以驗證發送方的身份,而RSA加密算法是目前比較通用的非對稱加密算法,常常被用於數字簽名及數據加密,且不少編程語言的標準庫中都自帶有RSA算法的庫,因此實現起來也是相對簡單的。web

  本文將使用Java標準庫來實現RSA密鑰對的生成及數字簽名和驗籤,密鑰對中的私鑰由請求方系統妥善保管,不能泄漏;而公鑰則交由系統的響應方用於驗證簽名。算法

  RSA使用私鑰對數據簽名,使用公鑰進行驗籤,生成RSA密鑰對的代碼以下:spring

package com.springboot.RSAdemo; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.util.Base64; /** * @description 生成RSA公/私鑰對 * * @author admin * */
public class GeneratorRSAKey { public static void main(String[] args) { jdkRSA(); } public static void jdkRSA() { GeneratorRSAKey generatorRSAKey = new GeneratorRSAKey(); try { // 初始化密鑰,產生公鑰私鑰對
            Object[] keyPairArr = generatorRSAKey.initSecretkey(); RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPairArr[0]; RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPairArr[1]; System.out.println("------------------PublicKey------------------"); System.out.println(Base64.getEncoder().encodeToString(rsaPublicKey.getEncoded())); System.out.println("\n------------------PrivateKey------------------"); System.out.println(Base64.getEncoder().encodeToString(rsaPrivateKey.getEncoded())); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } } /** * 初始化密鑰,生成公鑰私鑰對 * * @return Object[] * @throws NoSuchAlgorithmException */
    private    Object[] initSecretkey() throws NoSuchAlgorithmException { KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); keyPairGenerator.initialize(512); KeyPair keyPair = keyPairGenerator.generateKeyPair(); RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic(); RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate(); Object[] keyPairArr = new Object[2]; keyPairArr[0] = rsaPublicKey; keyPairArr[1] = rsaPrivateKey; return keyPairArr; } }

 

package com.springboot.RSAdemo; import java.security.InvalidKeyException; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.Signature; import java.security.SignatureException; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; /** * @description RSA簽名工具類 * * @author admin * */
public class JdkSignatureUtil { private final static String RSA = "RSA"; private final static String MD5_WITH_RSA = "MD5withRSA"; /** * 執行簽名 * * @param rsaPrivateKey 私鑰 * @param toSignStr 參數內容 * @return 簽名後的內容,base64後的字符串 * @throws NoSuchAlgorithmException * @throws InvalidKeySpecException * @throws InvalidKeyException * @throws SignatureException */
    public static String executeSignature(String rsaPrivateKey, String toSignStr) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException { // base64解碼私鑰
        byte[] decodePrivateKey = Base64.getDecoder().decode(rsaPrivateKey.replace("\r\n", "")); PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(decodePrivateKey); KeyFactory keyFactory = KeyFactory.getInstance(RSA); PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec); Signature signature = Signature.getInstance(MD5_WITH_RSA); signature.initSign(privateKey); signature.update(toSignStr.getBytes()); // 生成簽名
        byte[] result = signature.sign(); // base64編碼簽名爲字符串
        return Base64.getEncoder().encodeToString(result); } /** * 驗證簽名 * * @param rsaPublicKey 公鑰 * @param sign 簽名 * @param src 參數內容 * @return 驗證結果 * @throws NoSuchAlgorithmException * @throws InvalidKeySpecException * @throws InvalidKeyException * @throws SignatureException */
    public static boolean verifySignature(String rsaPublicKey, String sign, String src) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException { // base64解碼公鑰
        byte[] decodePublicKey = Base64.getDecoder().decode(rsaPublicKey.replace("\r\n", "")); X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(decodePublicKey); KeyFactory keyFactory = KeyFactory.getInstance(RSA); PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec); Signature signature = Signature.getInstance(MD5_WITH_RSA); signature.initVerify(publicKey); signature.update(src.getBytes()); // base64解碼簽名爲字節數組
        byte[] decodeSign = Base64.getDecoder().decode(sign); // 驗證簽名
        return signature.verify(decodeSign); } }

 

package com.springboot.RSAdemo; import org.springframework.http.ResponseEntity; import org.springframework.web.client.RestTemplate; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SignatureException; import java.security.spec.InvalidKeySpecException; import java.util.Comparator; import java.util.HashMap; import java.util.Map; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; /** * @description 發送端 * * @author admin * */
public class ClientController { //私鑰
    private final static String PRIVATE_KEY = "MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEAnPWR8anPt0YVkJRj+Rdu9gFdkR3leD5LxJFEP6naIYJ5CUon4XJy5uh7uEivyOHJMQqlo4U3zBbn9A1d07MOcwIDAQABAkAZhys0ddztvv1U5X2ZDsGiSziPmKAwvVkPYF0MSbDLkC6ZF6SDosPnBOQGY1YXF88eknAxEmuhWHnRAb05QnUJAiEA03SgC954tZS4OLyRf/4ArNdOBYurKQPbCapDgwAesYUCIQC+BhF3FBGyIlnh7D1iN+uNm1atga5go3widi7dqRnFlwIgEuQldEoA4MAToUX/fb7Ukpx9pPMwbG6iv/9NHsQA+f0CIALRfDjT519I+yRKqK5oPeofv61bGwb75b9tGUzYUTWJAiBLKFNjABVQWu5zYD+IGx3ooJyAFn7rwFGL8Y3h1V1ITg=="; public static String sender() throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, SignatureException, UnsupportedEncodingException { // 請求所需的參數
        Map<String, Object> requestParam = new HashMap<>(16); requestParam.put("userName", "小明"); requestParam.put("phone", "15866552236"); requestParam.put("address", "北京"); requestParam.put("status", 1); // 將須要簽名的參數內容按參數名的字典順序進行排序,並拼接爲字符串
        StringBuilder sb = new StringBuilder(); requestParam.entrySet().stream().sorted( Comparator.comparing(Map.Entry::getKey)).forEach(entry -> sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&")); String paramStr = sb.toString().substring(0, sb.length() - 1); System.out.println("paramStr:" + paramStr); // 使用私鑰生成簽名字符串
        String sign = JdkSignatureUtil.executeSignature(PRIVATE_KEY, paramStr); // 對簽名字符串進行url編碼
        String urlEncodeSign = URLEncoder.encode(sign, "UTF-8"); // String urlEncodeSign = URLEncoder.encode(sign, StandardCharsets.UTF_8.toString()); // 請求參數中需帶上簽名字符串
        requestParam.put("sign", urlEncodeSign); // 發送請求
        return postJson("http://localhost:8080/test", requestParam); } /** * 發送數據類型爲json的post請求 * * @param <T> * @param url * @param param * @return
     */
    public static <T> String postJson(String url, T param) { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON_UTF8); HttpEntity<T> httpEntity = new HttpEntity<>(param, headers); RestTemplate restTemplate = new RestTemplate(); ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, httpEntity, String.class); return responseEntity.getBody(); } public static void main(String[] args) { try { System.out.println(sender()); } catch (Exception e) { e.printStackTrace(); } } }

 

package com.springboot.RSAdemo; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SignatureException; import java.security.spec.InvalidKeySpecException; import java.util.Comparator; import java.util.Map; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @RestController public class ServerController { // 公鑰
    private final static String PUBLIC_KEY = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJz1kfGpz7dGFZCUY/kXbvYBXZEd5Xg+S8SRRD+p2iGCeQlKJ+Fycuboe7hIr8jhyTEKpaOFN8wW5/QNXdOzDnMCAwEAAQ=="; @PostMapping(value = "/test") public String server(@RequestBody Map<String, Object> param) throws InvalidKeySpecException, NoSuchAlgorithmException, InvalidKeyException, SignatureException, UnsupportedEncodingException { // 從參數中取出簽名字符串並刪除,由於sign不參與字符串拼接
        String sign = (String) param.remove("sign"); // 對簽名字符串進行url解碼
        String decodeSign = URLDecoder.decode(sign, "UTF-8"); // String decodeSign = URLDecoder.decode(sign, StandardCharsets.UTF_8.toString()); // 將簽名的參數內容按參數名的字典順序進行排序,並拼接爲字符串
        StringBuilder sb = new StringBuilder(); param.entrySet().stream().sorted(Comparator.comparing(Map.Entry::getKey)).forEach(entry -> sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&") ); String paramStr = sb.toString().substring(0, sb.length() - 1); // 使用公鑰進行驗籤
        boolean result = JdkSignatureUtil.verifySignature(PUBLIC_KEY, decodeSign, paramStr); if (result) { return "簽名驗證成功"; } return "簽名驗證失敗,非法請求"; } }

 

出處:https://blog.51cto.com/zero01/2331063編程

相關文章
相關標籤/搜索