数字签名 Signature 的使用
1、密钥的获取
package cn.snowpa.common;import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Enumeration;@Component
public class MiSecretKey {/*** 加密密钥*/private static String signaturePublic;@Value("${signature.public}")public void setSignaturePublic(String signaturePublic) {MiSecretKey.signaturePublic = signaturePublic;}/*** 加密密钥*/private static String signaturePrivate;@Value("${signature.private}")public void setSignaturePrivate(String signaturePrivate) {MiSecretKey.signaturePrivate = signaturePrivate;}/*** 从文件中获取公钥(公钥的比特编码是X.509格式)** @return* @throws Exception*/public PublicKey getPublicKeyByFile() throws Exception {//建立文件对象File file = new File(signaturePublic);FileInputStream fis = new FileInputStream(file);ByteArrayOutputStream bos = new ByteArrayOutputStream();byte[] buffer = new byte[1024];int len;while ((len = fis.read(buffer)) > 0) {bos.write(buffer, 0, len);}byte[] pbks = Base64.getDecoder().decode(bos.toByteArray());X509EncodedKeySpec encodedKeySpec = new X509EncodedKeySpec(pbks);PublicKey newPbk = KeyFactory.getInstance("RSA").generatePublic(encodedKeySpec);return newPbk;}/*** 从文件中获取公钥(私钥的比特编码是pkcs8格式)** @return* @throws Exception*/public PrivateKey getPrivateKeyByFile() throws Exception {//建立文件对象File file = new File(signaturePrivate);FileInputStream fis = new FileInputStream(file);ByteArrayOutputStream bos = new ByteArrayOutputStream();byte[] buffer = new byte[1024];int len = 0;while ((len = fis.read(buffer)) > 0) {bos.write(buffer, 0, len);}byte[] prks = Base64.getDecoder().decode(bos.toByteArray());/**/PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(prks);PrivateKey newPrk = KeyFactory.getInstance("RSA").generatePrivate(pkcs8EncodedKeySpec);return newPrk;}}
2、生成签名和验证
package cn.snowpa.common;import com.alibaba.fastjson.JSONObject;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;import java.io.IOException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;public class MiSignature {/*** 签名算法*/public static final String SIGN_ALGORITHMS = "SHA1WithRSA";/*** 签名算法*/public static final String ENCODE = "utf-8";/*** RSA签名** @param content 待签名数据* @param privateKey 私钥* @return 签名值*/public static String sign(String content, PrivateKey privateKey) {try {Signature signature = Signature.getInstance(SIGN_ALGORITHMS);signature.initSign(privateKey);signature.update(content.getBytes(ENCODE));byte[] signed = signature.sign();return byte2Base64 (signed);} catch (Exception e) {e.printStackTrace();return null;}}/*** 验签** @param content 待签名数据* @param sign 签名值* @param pubKey 公钥* @return 失败时,返回false。*/public static boolean verifySignature (String content, String sign, PublicKey pubKey) {if (null == content || null == sign || "".equals(content) || "".equals(sign)) {return false;}try {byte[] signed = base642Byte(sign);Signature signatureChecker = Signature.getInstance(SIGN_ALGORITHMS);signatureChecker.initVerify(pubKey);signatureChecker.update(content.getBytes());// 验证签名是否正常return signatureChecker.verify(signed);} catch (Exception e) {e.printStackTrace();return false;}}public static String byte2Base64(byte[] bytes) {BASE64Encoder encoder = new BASE64Encoder();return encoder.encode(bytes);}public static byte[] base642Byte(String base64Key) throws IOException {BASE64Decoder decoder = new BASE64Decoder();return decoder.decodeBuffer(base64Key);}// 使json-lib来进行json到map的转换,fastjson有排序问题,不能用public static Map<String, String> jsonToMap (JSONObject json) {Map<String, String> map = new HashMap<>();for (Object key : json.keySet()) {if("sign".equals(key)){continue;}String value = json.getString((String) key);map.put((String) key, value);}Map<String, String> resultMap = sortMapByKey(map); //按Key进行排序return resultMap;}/*** 使用 Map按key进行排序* @param map* @return*/public static Map<String, String> sortMapByKey(Map<String, String> map) {if (map == null || map.isEmpty()) {return null;}Map<String, String> sortMap = new TreeMap<String, String>(new MapKeyComparator());sortMap.putAll(map);return sortMap;}static class MapKeyComparator implements Comparator<String> {@Overridepublic int compare(String str1, String str2) {return str1.compareTo(str2);}}public static void main(String[] args) {try {// 随机生成一对非对称加密的秘钥对MiSecretKey keyPair = new MiSecretKey();String content = "helloWord123" + ("测试内容");System.out.println("验签内容:" + content);String sign = MiSignature.sign(content, keyPair.getPrivateKeyByFile() );System.out.println("签名结果=" + sign);boolean verify = MiSignature.verifySignature(content, sign, keyPair.getPublicKeyByFile());System.out.println("验签结果="+verify);} catch (Exception e) {e.printStackTrace();}}
}
3、自定义拦截器验证签名
package cn.snowpa.interceptor;import cn.snowpa.common.MiSecretKey;
import cn.snowpa.common.MiSignature;
import cn.snowpa.utils.JsonUtils;
import cn.snowpa.utils.ResultUtil;
import cn.snowpa.utils.StringUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;@Slf4j
public class SystemApiInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {try {JSONObject paramsJson = getRequestParams(request);Map<String,String> content_map = MiSignature.jsonToMap(paramsJson);String sign = paramsJson.getString("sign");log.info("sign:"+sign);String content = content_map.toString();log.info("content:"+content);if (StringUtil.isEmpty(sign)) {write(request, response, JsonUtils.toJson(ResultUtil.error(500,"签名异常")));return false;}MiSecretKey keyPair = new MiSecretKey();boolean verify = MiSignature.verifySignature(content, sign, keyPair.getPublicKeyByFile());log.info("验签结果:"+verify);if(!verify){write(request, response, JsonUtils.toJson(ResultUtil.error(500,"签名异常")));return false;}return true;}catch (Exception e){log.info("异常信息:"+e.getMessage());write(request, response, JsonUtils.toJson(ResultUtil.error(500,"签名异常")));return false;}}// 获取HttpServletRequest里面的参数,并decodeprivate JSONObject getRequestParams(HttpServletRequest request) throws Exception {ServletInputStream servletInputStream = request.getInputStream();ServletInputStream inputStream = servletInputStream;StringBuilder content = new StringBuilder();byte[] b = new byte[1024];int lens;while ((lens = inputStream.read(b)) > 0) {content.append(new String(b, 0, lens));}String strContent = content.toString(); // 请求体内容log.info("请求体:{}", strContent);JSONObject params = JSON.parseObject(strContent);return params;}/*** 通过response返回错误信息给前端** @param request 请求* @param response 响应* @param content 响应内容*/private void write (HttpServletRequest request, HttpServletResponse response, String content) throws IOException {String origin = request.getHeader("Origin");response.setHeader("Access-Control-Allow-Origin", origin);response.setCharacterEncoding("UTF-8");response.setContentType("application/json; charset=utf-8");response.getWriter().write(content);}
}
4、拦截器的配置-配置拦截URL
package cn.snowpa.interceptor;import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class WebMvcConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new SystemApiInterceptor()).addPathPatterns("/systemApi/**");}}
5、解决请求体的数据只能获取一次的问题
5.1 请求体数据的处理
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.Charset;public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {private final byte[] body;private String bodyStr;public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {super(request);String bodyString = getBodyString(request);body = bodyString.getBytes(Charset.forName("UTF-8"));bodyStr=bodyString;}public String getBodyStr() {return bodyStr;}@Overridepublic ServletInputStream getInputStream() throws IOException {final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body);return new ServletInputStream() {@Overridepublic int read() throws IOException {return byteArrayInputStream.read();}@Overridepublic boolean isFinished() {return false;}@Overridepublic boolean isReady() {return false;}@Overridepublic void setReadListener(ReadListener readListener) {}};}public String getBodyString(HttpServletRequest request) throws IOException {StringBuilder sb = new StringBuilder();InputStream inputStream = null;BufferedReader reader = null;try {inputStream = request.getInputStream();reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));char[] bodyCharBuffer = new char[1024];int len = 0;while ((len = reader.read(bodyCharBuffer)) != -1) {sb.append(new String(bodyCharBuffer, 0, len));}} catch (IOException e) {e.printStackTrace();} finally {if (inputStream != null) {try {inputStream.close();} catch (IOException e) {e.printStackTrace();}}if (reader != null) {try {reader.close();} catch (IOException e) {e.printStackTrace();}}}return sb.toString();}
}
5.2 过滤器调用处理请求体的数据
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;@WebFilter(filterName = "httpServletRequestWrapperFilter", urlPatterns = {"/*"})
public class HttpServletRequestWrapperFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {ServletRequest requestWrapper = null;if (request instanceof HttpServletRequest) {HttpServletRequest httpRequest = (HttpServletRequest) request;//遇到post方法才对request进行包装String methodType = httpRequest.getMethod();if ("POST".equals(methodType)) {requestWrapper = new BodyReaderHttpServletRequestWrapper((HttpServletRequest) request);}}if (null == requestWrapper) {chain.doFilter(request, response);} else {chain.doFilter(requestWrapper, response);}}@Overridepublic void destroy() {}
}
数字签名 Signature 的使用相关推荐
- 数字签名signature是什么?(清晰!)
作者: 阮一峰 日期: 2011年8月 9日 今天,我读到一篇好文章. 它用图片通俗易懂地解释了,"数字签名"(digital signature)和"数字证书" ...
- 简单理解数字签名和验签
数字签名: "数字签名(又称公钥数字签名.电子签章)是一种类似写在纸上的普通的物理签名,但是使用了公钥加密领域的技术实现,用于鉴别数字信息的方法.一套数字签名通常定义两种互补的运算,一个用于 ...
- 用python3实现MD5withRSA数字签名的验证
在某次安全研究中.需要手工验证下某重要文件MD5withRSA的数字签名是否正确.于是写了个脚本.现记录下来,供大家学习讨论. 1. 数字签名基础知识 数字签名: 一旦选择足够安全的散列算法, 那么就 ...
- Java安全之对称加密、非对称加密、数字签名
Java中加密分为两种方式一个是对称加密,另一个是非对称加密.对称加密是因为加密和解密的钥匙相同,而非对称加密是加密和解密的钥匙不同. 对称加密与非对称加密的区别: 对称加密称为密钥加密,速度快,但加 ...
- 数字签名和数字证书使用详解
目录 1.数字签名 (1)数字签名的应用场景 (2)数字签名的实现原理 (3)如何进行数字签名和验证 (4)使用数字签名的优点和缺点 2.数字证书 (1)如何对数字证书进行颁发和管理 (2)颁发数字证 ...
- RSA签名算法,计算调用加密报文,安全传输
RSA签名算法 1. 获取当前的时间戳参数 2. 计算参数签名 3. 获取请求对象的MD5密文 4. 通过私钥计算某个参数的RSA签名 5. 转换字符集到utf8 6. MD5加密字符串 7. bas ...
- [转]Java加密算法
如基本的单向加密算法: BASE64 严格地说,属于编码格式,而非加密算法 MD5(Message Digest algorithm 5,信息摘要算法) SHA(Secure Hash Algorit ...
- java 在线rsa解密_通用的Java RSA加密工具类,可在线验证通过
/** * RSA加密工具类 * 使用PKCS1_PADDING填充,密钥长度1024 * 加解密结果在这里测试通过:http://tool.chacuo.net/cryptrsaprikey * 注 ...
- java-信息安全(十)-数字签名算法DSA
概述 信息安全基本概念: DSA算法(Digital Signature Algorithm,数据签名算法) DSA Digital Signature Algorithm (DSA)是Schnorr ...
最新文章
- nginx反向代理监听非80端口造成的端口丢失解决方案
- mysql查询锁表语句和kill对应的线程
- 检索数据_5_给字段取个有意义的名字
- leetcode 665. 非递减数列(贪心算法)
- DotNetBar 教程
- Windows操作系统的发展历程
- 梦龙即时通讯软件测试初学者,梦龙即时通讯软件
- TFN RMT 手持式路测仪 5G NR 手持式频谱分析仪
- 基于FileOpen的PDF文档加密
- DES的s盒输出CPA和DPA攻击
- 简单创意的思维导图怎么画
- 思维不要僵化在一种模式
- 七大OSINT操作系统(开源网络情报)
- 笔记本电脑常识:噪音
- ARP渗透与攻防(五)之Ettercap劫持用户流量
- 图形编程入门(VS2010)
- 青龙面板获取京东ck的新老方法
- Spring--BeanUtils工具类--使用/实例
- Excel-常用宏技巧(4)
- 操作元素总结及作业1——世纪佳缘登录案例-黑马pink老师