数字签名 Signature 的使用
程序员文章站
2022-03-14 20:29:14
...
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> {
@Override
public 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 {
@Override
public 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里面的参数,并decode
private 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 {
@Override
public 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;
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public int read() throws IOException {
return byteArrayInputStream.read();
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public 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 {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public 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);
}
}
@Override
public void destroy() {
}
}
推荐阅读
-
浅析PHP函数ereg()的具体使用方法
-
在浏览器中实现图片粘贴的jQuery插件-- pasteimg使用指南
-
快速入门:使用Azure 数据 Studio进行连接和查询 Azure SQL 数据仓库中的数据
-
PHP 链接DBF资料所使用的SQL语句
-
使用POI将Mysql或Oracle中的数据导入到Excel中去_MySQL
-
php使用pclzip类实现文件压缩的方法(附pclzip类下载地址),
-
python使用webbrowser浏览指定url的方法
-
使用mysqldump定时备份数据库的脚本_MySQL
-
MySQL的使用中实现读写分离的教程_MySQL
-
使用C#给PDF文档添加注释的示例代码分享(图)