方式1:
使用javax.security.cert.X509Certificate进行解析
URL url = Demo.class.getClassLoader().getResource("C000024.crt"); //证书路径 System.out.println("公钥所在路径:"+url.getFile()); X509Certificate cert = X509Certificate.getInstance(new FileInputStream(url.getFile())); PublicKey publicKey = cert.getPublicKey(); BASE64Encoder base64Encoder=new BASE64Encoder(); String publicKeyString = base64Encoder.encode(publicKey.getEncoded()); System.out.println("-----------------公钥--------------------"); System.out.println(publicKeyString); System.out.println("-----------------公钥--------------------");
方式2:
使用java.security.cert.X509Certificate进行解析
URL url = Demo.class.getClassLoader().getResource("C000024.crt"); //证书路径 System.out.println("公钥所在路径:"+url.getFile()); CertificateFactory cf = CertificateFactory.getInstance("X.509"); X509Certificate cert = (X509Certificate)cf.generateCertificate(new FileInputStream(url.getFile())); PublicKey publicKey = cert.getPublicKey(); BASE64Encoder base64Encoder=new BASE64Encoder(); String publicKeyString = base64Encoder.encode(publicKey.getEncoded()); System.out.println("-----------------公钥--------------------"); System.out.println(publicKeyString); System.out.println("-----------------公钥--------------------");
说明:
因为只做示例,没有进行异常处理和流的释放,方式1的代码可能少点,方式2需要强转,美观上可能方式1更好看点,但方式1的实质还是调用的方式2,方式2内部有实现缓存策略。更多可以参考下api文档,文档上有提供示例。
补充:JAVA生成RSA公钥和私钥及RSA对数据的加签和验签
背景:
最近来到了新的公司,公司做的是保险支付相关业务,对接渠道的时候经常会用到数据的加签和验签,初次涉及RSA加签验签,通过网站生成了RSA公钥和私钥,用私钥将我要传送的数据进行了加签,并将我的公钥提供给了渠道方进行验签,结果在联调的时候,验签总是错误,渠道方用自己的私钥对数据加签后再用自己的公钥对数据进行验签却能通过,于是我也用自己的私钥对数据进行加签后再用自己的公钥对数据进行验签,结果让我惊讶,居然没有通过!
到了这里,产生错误的原因基本上已经一目了然了,我通过网站生成的公私钥是无法配对的,这当中可能涉及到了网站生成公私钥的时候已经对公私钥进行了处理,比如说PKCS8的处理,所以决定自己用Java来生成RSA公钥和私钥进行验证测试,文档写出来了,测试结果自然已经知道了,是通过的。
以下为完整的验签过程:
启动类:ZhongbaohuiApplication.java
package com.test.zhongbaohui; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ZhongbaohuiApplication { public static void main(String[] args) { SpringApplication.run(ZhongbaohuiApplication.class, args); } }
请求Controller:RequestController.java
package com.test.zhongbaohui.controller; import com.alibaba.fastjson.JSONObject; import com.test.zhongbaohui.utils.RSASignUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.HashMap; import java.util.Map; /** * @program: zhongbaohui * @package: com.test.zhongbaohui.controller * @description: * @auther: chengjunyu * @createDate: 2019/12/6 22:05 */ @RestController @RequestMapping(value="/test") @Slf4j public class RequestController { @Autowired private ResponseController responseController; @PostMapping("/getJsonObject") private String getJsonObject(HttpServletRequest request, HttpServletResponse response) { JSONObject jsonObject = new JSONObject(); jsonObject.put("signType", "RSA"); jsonObject.put("enterpriseId", "201975538583911110"); jsonObject.put("nonce", "b0eed33073664f5fa983c5b774dbd4b6"); jsonObject.put("timestamp", "2019-12-07 01:19:25"); Map<String, Object> map = new HashMap<>(); map.put("bankCode", "其他"); map.put("batchNo", "201975538583911110b1084fa29f6c"); map.put("bankCardNumber", "6217856100077026406"); map.put("paymentNote", "佣金发放"); map.put("idCardNumber", "320123199103104650"); map.put("mobile", "15365176555"); map.put("bankName", "中国银行"); map.put("outEnterpriseOrderNo", "T20191207011545663692017"); map.put("realPayment", "1.00"); map.put("serviceId", "201968613430727001"); map.put("userName", "程俊予"); jsonObject.put("data", map); //私钥内容 String privateKey = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgFnOID56YNquwenrgnW1Ud+GBcSFojPOY00+TYq/qHVaprGPeuKlAcBebkyj4+G3H4t7e1DTOblQtZk/yi+2VcbDnhHQl3UVdkLkVMRXXCBPBJtjSo3RMMJFC6OCiKfzhujhhio7MJWWrMYLtAgMBAAECgYBplZud/CZv1KLzIA5bdbF2yk36FYoc3hl3iXLeiyp91NGc6hqhFSEyXPhvrZP0aAym9IC824Bjq4Gg7pkkHzYT3IGDCqqyodBYcdof8Jsk9t0G0Ll7G1dlQwl9R6+SAvauF5RUbwz5Byos6cnFbybfqAdRUdF96yH0Hw0QF1u8XQJBAPrpHvZpeOZNSY/M1wlJZv5gV1OoI9s+PZgJQHgWbT7FaiPDkZiAa7B6hGNBgUa7m4vEzGJNAOHxhdl1QMtlTjMCQQD3VInIf9EjKZn7LNcPQsl1AkXbwuXjtMceeuX43lcdapgQ+4Y6G5QU3fhwZxwsdZnUbLqJWzFgXw/F2E2DxopfAkBxGErgfsID7KpPquDySqel2P8DsjIXTIKu2Ny6REGRnaIt5KTnvFrN/StXIduHamC+K0KEvHi9XwQZ9IP0KgGJAkEA3hUzzywuP3OYhzhhN5vRx1YuIkGkKU3nSdAy9b+323seZoljooOm+QHDljKP0sAaS+sBqFqRQKa7Q/yQxdWd4wJBAIUXethFnMr3U9FetKHmWKwOPh23EHM0xPdVzMcb24WwK7QAXCMo71ugG6qqmBA+wYCrjPwbMu5XysB5+d5ZNC0="; String sign = RSASignUtils.sign(jsonObject, privateKey); log.info("验签sign为:{}", sign); jsonObject.put("sign", sign); String message = responseController.returnMsg(jsonObject.toJSONString()); return message; } }
响应Controller:ResponseController.java
package com.test.zhongbaohui.controller; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.test.zhongbaohui.utils.RSASignUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RestController; /** * @program: zhongbaohui * @package: com.test.zhongbaohui.controller * @description: * @auther: chengjunyu * @createDate: 2019/12/7 20:46 */ @RestController @Slf4j public class ResponseController { @PostMapping("/returnMsg") public String returnMsg(String message) { JSONObject jsonObject = JSONObject.parseObject(message); log.info("接受请求内容为:{}", jsonObject.toJSONString()); String sign = jsonObject.getString("sign"); jsonObject.remove("sign"); String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQD5gn/eAnLyf6xZziA+emDarsHp64J1tVHfhgXEhaIzzmNNPk2Kv6h1Wqaxj3ripQHAXm5Mo+Phtx+Le3tQ0zm5ULWZP8ovtlXGw54R0Jd1FXZC5FTEV1wgTwSbY0qN0TDCRQujgoin84bo4YYqOzCVlqzGC7QIDAQAB"; boolean flag = RSASignUtils.verify(jsonObject, publicKey, sign); JSONObject object = new JSONObject(); if(flag) { object.put("code", "200"); object.put("status", "success"); object.put("message:", "验签成功"); }else { object.put("code", "400"); object.put("status", "failure"); object.put("message:", "验签失败"); } return object.toJSONString(); } }
RSA工具类:RSASignUtils.java
package com.test.zhongbaohui.utils; import com.alibaba.fastjson.JSONObject; import lombok.extern.slf4j.Slf4j; import org.apache.tomcat.util.codec.binary.Base64; import org.springframework.stereotype.Component; import sun.misc.BASE64Decoder; import sun.misc.BASE64Encoder; import java.io.IOException; 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.HashMap; import java.util.Map; /** * @program: zhongbaohui * @package: com.test.zhongbaohui.controller * @description: * @auther: chengjunyu * @createDate: 2019/12/6 23:03 */ @Slf4j @Component public class RSASignUtils { public static final String KEY_ALGORITHM = "RSA"; private static final String PUBLIC_KEY = "RSAPublicKey"; private static final String PRIVATE_KEY = "RSAPrivateKey"; public static final String SIGNATURE_ALGORITHM="MD5withRSA"; public static final Integer RSA_KEY_SIZE = 1024; /* * @function: 使用字符串格式的私钥为JSONObject格式的内容加签 * @param: [jsonObject, privateKey] * @return: java.lang.String * @auther: chengjunyu * @date: 2019/12/7 21:06 */ public static String sign(JSONObject jsonObject, String privateKey) { String signMsg = ""; String data = jsonObject.toString(); log.info("加签对象内容为:{}", data); try { byte[] keyBytes = decryptBASE64(privateKey); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PrivateKey key = keyFactory.generatePrivate(keySpec); Signature signature = Signature.getInstance("MD5withRSA"); signature.initSign(key); signature.update(data.getBytes("ISO-8859-1")); signMsg = Base64.encodeBase64String(signature.sign()); } catch (Exception var8) { var8.printStackTrace(); } return signMsg; } /* * @function: 使用字符串格式的公钥为JSONObject格式的内容验签 * @param: [jsonObject, publicKey, sign] * @return: boolean * @auther: chengjunyu * @date: 2019/12/8 14:56 */ public static boolean verify(JSONObject jsonObject, String publicKey, String sign) { String s = jsonObject.toJSONString(); log.info("s:{}",s); boolean rs = false; try { byte[] keyBytes = decryptBASE64(publicKey); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PublicKey key = keyFactory.generatePublic(keySpec); Signature signature = Signature.getInstance("MD5withRSA"); signature.initVerify(key); signature.update(s.getBytes("ISO-8859-1")); return signature.verify(Base64.decodeBase64(sign.getBytes())); } catch (Exception var9) { var9.printStackTrace(); return rs; } } /* * @function: 获取PublicKey格式的公钥,本例中未使用 * @param: [key] * @return: java.security.PublicKey * @auther: chengjunyu * @date: 2019/12/8 16:10 */ public static PublicKey getPublicKey(String key) { PublicKey publicKey = null; try { byte[] keyBytes = (new BASE64Decoder()).decodeBuffer(key); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); // RSA对称加密算法 KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); // 取公钥匙对象 publicKey = keyFactory.generatePublic(keySpec); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (InvalidKeySpecException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return publicKey; } /* * @function: 获取PublicKey格式的私钥,本例中未使用 * @param: [key] * @return: java.security.PrivateKey * @auther: chengjunyu * @date: 2019/12/8 16:10 */ public static PrivateKey getPrivateKey(String key) { PrivateKey privateKey = null; try { byte[] keyBytes = (new BASE64Decoder()).decodeBuffer(key); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); privateKey = keyFactory.generatePrivate(keySpec); } catch (IOException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (InvalidKeySpecException e) { e.printStackTrace(); } return privateKey; } /* * @function: 初始化公钥和私钥 * @param: [] * @return: java.util.Map<java.lang.String,java.lang.Object> * @auther: chengjunyu * @date: 2019/12/8 14:34 */ public static Map<String, Object> initKey() { KeyPairGenerator keyPairGen = null; try { keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } keyPairGen.initialize(RSA_KEY_SIZE); KeyPair keyPair = keyPairGen.generateKeyPair(); RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); Map<String, Object> keyMap = new HashMap<String, Object>(2); keyMap.put(PUBLIC_KEY, publicKey); keyMap.put(PRIVATE_KEY, privateKey); return keyMap; } //获得公钥字符串 public static String getPublicKeyStr(Map<String, Object> keyMap) { //获得map中的公钥对象 转为key对象 Key key = (Key) keyMap.get(PUBLIC_KEY); //编码返回字符串 return encryptBASE64(key.getEncoded()); } //获得私钥字符串 public static String getPrivateKeyStr(Map<String, Object> keyMap) { //获得map中的私钥对象 转为key对象 Key key = (Key) keyMap.get(PRIVATE_KEY); //编码返回字符串 return encryptBASE64(key.getEncoded()); } //编码返回字符串 public static String encryptBASE64(byte[] key) { return (new BASE64Encoder()).encodeBuffer(key); } //解码返回byte public static byte[] decryptBASE64(String key) { byte[] bytes = null; try { return (new BASE64Decoder()).decodeBuffer(key); } catch (IOException e) { return bytes; } } public static void main(String[] args) { Map<String, Object> keyMap = initKey(); String publicKey = getPublicKeyStr(keyMap); log.info("JAVA生成RSA公钥:{}", publicKey); String privateKey = getPrivateKeyStr(keyMap); log.info("JAVA生成RSA私钥:{}", privateKey); } }
注意:
本文中请求和响应类中的私钥和公钥均是不完整的,在请求和响应类中的私钥和公钥由RSASignUtils工具类生成后,再替代入请求和响应类中,请求类中使用私钥加签,响应类中使用公钥验签。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持易盾网络。如有错误或未考虑完全的地方,望不吝赐教。