package com.railway.common.utils.sign; import static com.railway.common.core.domain.dto.ReturnCode.SIGN_CHECK_FAIL; import com.railway.common.core.domain.AjaxResult; import com.railway.common.core.domain.dto.ReturnCode; import com.railway.common.utils.DateUtils; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.time.LocalDateTime; import java.util.Arrays; import java.util.Map; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import javax.xml.bind.DatatypeConverter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; /** * 接口签名检查 * * @author zhaomn */ @Slf4j public class SignatureUtil { public static final String STR_TIMESTAMP = "timestamp"; public static final String STR_SIGNATURE = "signature"; public static final long MAX_TIME_DIFF = 600L; private static final String ENCODING = "UTF-8"; private static final String HMAC_SHA256 = "HmacSHA256"; public static String getParamString(Map paramMap) throws UnsupportedEncodingException { if (paramMap == null || paramMap.isEmpty()) { return ""; } // 对参数进行排序 String[] sortedKeys = paramMap.keySet().toArray(new String[]{}); Arrays.sort(sortedKeys); final String SEPARATOR = "&"; // 生成stringToSign字符串 StringBuilder stringToSign = new StringBuilder(); StringBuilder canonicalizedQueryString = new StringBuilder(); boolean isFirst = true; for (String key : sortedKeys) { if (isFirst) { isFirst = false; } else { canonicalizedQueryString.append(SEPARATOR); } // 这里注意对key和value进行编码 canonicalizedQueryString.append(percentEncode(key)).append("=") .append(percentEncode(paramMap.get(key))); } // 这里注意对canonicalizedQueryString进行编码 stringToSign.append(percentEncode(canonicalizedQueryString.substring(0))); return stringToSign.toString(); } private static String percentEncode(String value) throws UnsupportedEncodingException { return value != null ? URLEncoder.encode(value, ENCODING).replace("+", "%20").replace("*", "%2A").replace("%7E", "~") : null; } public static AjaxResult checkTimestamp(String requestId, String userTimestamp){ if (StringUtils.isBlank(userTimestamp)) { log.debug("checkSignature, missing parameters, requestId {}, timestamp {}", requestId, userTimestamp); return AjaxResult.error(ReturnCode.REQUIRED_PARAM_MISSING); } LocalDateTime urlDateTime = DateUtils.parseDateTime(userTimestamp); if (null == urlDateTime) { log.debug("checkSignature, time format error, requestId {}, timestamp {}", requestId, userTimestamp); return AjaxResult.error(ReturnCode.DATETIME_FORMAT_ERROR); } LocalDateTime now = LocalDateTime.now(); if (urlDateTime.isAfter(now.plusSeconds(SignatureUtil.MAX_TIME_DIFF)) || urlDateTime.isBefore(now.minusSeconds(SignatureUtil.MAX_TIME_DIFF))) { log.debug("checkSignature, time is shift too many, in {}, now {}", urlDateTime, now); return AjaxResult.error(ReturnCode.DATETIME_SHIFT_TOO_MANY); } return AjaxResult.success(); } public static AjaxResult checkSignature(String requestId, String strToSign, String userSignature, String aesKey) { if (StringUtils.isBlank(strToSign)) { return AjaxResult.success(); } if (StringUtils.isBlank(userSignature)) { log.debug("checkSignature, missing parameters, requestId {}", requestId); return AjaxResult.error(ReturnCode.REQUIRED_PARAM_MISSING); } String signature = createSignature(strToSign, aesKey); if (StringUtils.isEmpty(signature)) { log.debug("checkSignature, calc signature null {}", requestId); return AjaxResult.error(SIGN_CHECK_FAIL); } boolean check = signature.equals(userSignature); if (!check) { log.debug( "checkSignature, signature not equal, strToSign {}, user signature {}, calc signature {}", strToSign, userSignature, signature); return AjaxResult.error(SIGN_CHECK_FAIL); } return AjaxResult.success(); } private static String createSignature(String strToSign, String key) { try { SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(ENCODING), SignatureUtil.HMAC_SHA256); Mac mac = Mac.getInstance(SignatureUtil.HMAC_SHA256); mac.init(signingKey); byte[] rawHmac = mac.doFinal(strToSign.getBytes(ENCODING)); log.debug("printHexBinary= {}", DatatypeConverter.printHexBinary(rawHmac)); log.debug("printBase64Binary= {}", DatatypeConverter.printBase64Binary(rawHmac)); return DatatypeConverter.printBase64Binary(rawHmac); } catch (NoSuchAlgorithmException | InvalidKeyException | UnsupportedEncodingException ignore) { } return null; } public static void main(String[] args) { createSignature( "code%3D12%26password%3DdB123456%26username%3D1088%26uuid%3Dacca0f11d8e2450e85fd45d76c49b951", "s#e@5f98H*^his%t"); } }