public class SignatureVerificationFilter implements Filter {
private final String HTTP_JOB_HEADER_PREFIX = "schedulerx-";
private final String HTTP_SIGNATURE_VERSION = "1.0";
private final Map<String, String> appKeyMap = new HashMap<>();
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// TODO: Build a list of AppKeys for your applications.
appKeyMap.put("groupId", "APPKEY*******");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
try {
// Obtain schedulerx-signature-timestamp to check for expiration.
Long signatureTimestamp = Long.parseLong(((HttpServletRequest) servletRequest).getHeader("schedulerx-signature-timestamp"));
if(System.currentTimeMillis() - signatureTimestamp > 60*1000){
((HttpServletResponse)servletResponse).sendError(HttpServletResponse.SC_UNAUTHORIZED, "Signature has timed out.");
return;
}
// Check the current version of the signature verification algorithm.
String signatureVersion = ((HttpServletRequest) servletRequest).getHeader("schedulerx-signature-version");
if(!HTTP_SIGNATURE_VERSION.equals(signatureVersion)){
((HttpServletResponse)servletResponse).sendError(HttpServletResponse.SC_UNAUTHORIZED, "The signature algorithm version has changed.");
return;
}
// Get the data to be signed.
String content = getSignContent((HttpServletRequest)servletRequest, "");
// Get the signature from the request header.
String signature = ((HttpServletRequest) servletRequest).getHeader("schedulerx-signature");
// Get the certificate.
// Perform signature verification.
boolean res = verify("/Users/yaohui/certificate.crt", content, signature);
System.out.println("Verification result: "+res);
if(res) {
filterChain.doFilter(servletRequest, servletResponse);
}else {
((HttpServletResponse)servletResponse).sendError(HttpServletResponse.SC_UNAUTHORIZED, "Signature verification failed.");
}
} catch (SignatureException e) {
((HttpServletResponse)servletResponse).sendError(HttpServletResponse.SC_UNAUTHORIZED, "Signature verification error: " + e.getMessage());
}
}
/**
* Verifies the signature of the text.
* @param publicKeyPath
* @param message
* @param signature
* @return
* @throws SignatureException
*/
public static boolean verify(String publicKeyPath, String message, String signature) throws SignatureException{
try {
Signature sign = Signature.getInstance("SHA1withRSA");
byte[] keyBytes = Files.readAllBytes(Paths.get(publicKeyPath));
X509Certificate cert = X509Certificate.getInstance(keyBytes);
PublicKey publicKey = cert.getPublicKey();
sign.initVerify(publicKey);
sign.update(message.getBytes("UTF-8"));
return sign.verify(Base64.decodeBase64(signature.getBytes("UTF-8")));
} catch (Exception ex) {
throw new SignatureException(ex);
}
}
/**
* Gets the content to be signed.
* @param request
* @param appKey
* @return
* @throws IOException
*/
private String getSignContent(HttpServletRequest request, String appKey) throws IOException {
StringBuilder sb = new StringBuilder();
// Request method
sb.append(request.getMethod());
sb.append("\n");
// HTTP request URL
String fullUrl = request.getRequestURL()+ (StringUtils.isEmpty(request.getQueryString())?"":"?"+URLDecoder.decode(request.getQueryString(), "UTF-8"));
sb.append(fullUrl);
sb.append("\n");
// The AppKey corresponding to the current request
sb.append(appKeyMap.get(request.getHeader("schedulerx-groupid")));
sb.append("\n");
// Cookie information
sb.append("cookie" + ":" + request.getHeader("cookie"));
sb.append("\n");
List<String> schedulerXHeaders = new ArrayList();
// Get request header information.
Enumeration headerNames = request.getHeaderNames();
// Traverse the request headers in a loop and use the getHeader() method to get the value of a specified header.
while (headerNames.hasMoreElements()){
String headerName = (String) headerNames.nextElement();
// Filter the signature header.
if (headerName.startsWith(HTTP_JOB_HEADER_PREFIX) && !"schedulerx-signature".equals(headerName)) {
schedulerXHeaders.add(headerName + ":" + request.getHeader(headerName));
}
}
// Sort and concatenate the SchedulerX-related request headers.
Collections.sort(schedulerXHeaders);
for (String kv : schedulerXHeaders) {
sb.append(kv);
sb.append("\n");
}
if (request.getMethod().equals("POST")) {
// For a POST request, its content is part of the data to be signed.
InputStream is = request.getInputStream();
byte[] content = new byte[request.getContentLength()];
is.read(content);
ContentType contentType = ContentType.parse(request.getContentType());
sb.append(new String(content, contentType.getCharset()));
}
return sb.toString();
}
@Override
public void destroy() {}
}