中电联的协议《T/CEC 102.4—2016 电动汽车充换电服务信息交换 第4部分:数据传输及安全》中描述了一个名为HMAC-MD5的签名方法,具体如下图:
用于数据传输过程的报文签名。本文是记录如何实现这个签名。
(顺便吐槽一下这个协议中算法的文字描述部分非常不严谨,让人很疑惑。描述中总是用字符串代替字节数组,让我以为要把数据转为字符呢。结果根本不是。最后是看着公式实现的。)
根据协议描述,我们需要Md5的实现。我们这里直接使用之前的工具类,不再这里讨论。
这个工具类类图如下: 代码:
package net.wangds.utils; import java.io.UnsupportedEncodingException; import java.math.BigInteger; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.Security; /** * md5工具. */ public class Md5Utils { /** * 字符串的md5. * @param plainText 输入字符串. * @return md5(utf8编码). */ public static String md5(String plainText) { return md5(plainText, StandardCharsets.UTF_8); } /** * 字符串的md5. * @param plainText 输入字符串. * @param encode 字符集. * @return md5. * @see #md5(String, Charset) */ @Deprecated public static String md5(String plainText, String encode) { try { return md5(plainText.getBytes(encode)); } catch (UnsupportedEncodingException e) { return ""; } } /** * 字符串的md5. * @param plainText 输入字符串. * @param charset 字符集. * @return md5. */ public static String md5(String plainText, Charset charset) { return md5(plainText.getBytes(charset)); } /** * 字符串的md5. * @param plainText 输入字符串. * @return md5(utf8编码). */ public static String md5_16(String plainText) { return md5_16(plainText, StandardCharsets.UTF_8); } /** * 字符串的md5. * @param plainText 输入字符串. * @param encode 字符集. * @return md5. * @see #md5(String, Charset) */ @Deprecated public static String md5_16(String plainText, String encode) { try { return md5(plainText.getBytes(encode)).substring(8,24); } catch (UnsupportedEncodingException e) { return ""; } } /** * 字符串的16位md5. * @param plainText 输入字符串. * @param charset 字符集. * @return md5. */ public static String md5_16(String plainText, Charset charset){ return md5(plainText, charset).substring(8,24); } /** * 数据的md5. * @param data 输入字符串. * @return md5. */ public static byte[] md5Bytes(byte[] data) { try { // 生成一个MD5加密计算摘要 MessageDigest md = MessageDigest.getInstance("MD5"); //对字符串进行加密 md.update(data); //获得加密后的数据 return md.digest(); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("没有md5这个算法"); } } /** * 数据的md5. * @param data 输入字符串. * @return md5. */ public static String md5(byte[] data) { return HexUtils.bytes2HexString(md5Bytes(data)); } }同时,测试等工作也需要一个Hex编码工具,类图如下:
HexUtils的代码如下:
package net.wangds.utils; @SuppressWarnings("unused") public class HexUtils { /** * bytes2HexString. * 字节数组转16进制字符串. * @param bs 字节数组. * @return 16进制字符串. */ public static String bytes2HexString(byte[] bs) { StringBuilder result = new StringBuilder(); for (byte b1 : bs) { result.append(String.format("%02X", b1)); } return result.toString(); } /** * 字节数组转大写16进制字符串. * @param bs 字节数组. * @return 16进制字符串. */ public static String bytes2UpperCaseHexString(byte[] bs) { return bytes2HexString(bs).toUpperCase(); } /** * 字节数组转小写16进制字符串. * @param bs 字节数组. * @return 16进制字符串. */ public static String bytes2LowerCaseHexString(byte[] bs) { return bytes2HexString(bs).toLowerCase(); } /** * bytes2HexString. * 字节数组转16进制字符串. * @param bs 字节数组. * @return 16进制字符串. */ public static String bytes2HexString(byte[] bs, int offset, int length) { StringBuilder result = new StringBuilder(); for (int i=0; i<length; i++) { byte b1 = bs[offset+i]; result.append(String.format("%02X", b1)); } return result.toString(); } /** * hexString2Bytes. * 16进制字符串转字节数组. * @param src 16进制字符串. * @return 字节数组. * */ public static byte[] hexString2Bytes(String src) { int l = src.length() / 2; byte[] ret = new byte[l]; for (int i = 0; i < l; i++) { ret[i] = Integer.valueOf(src.substring(i * 2, i * 2 + 2), 16).byteValue(); } return ret; } /** * string2HexUTF8. * 字符UTF8串转16进制字符串. * @param strPart 字符串. * @return 16进制字符串. * */ public static String string2HexUTF8(String strPart) { return string2HexString(strPart,"UTF-8"); } /** * string2HexUTF8. * 字符UTF-16LE串转16进制字符串,此UTF-16LE等同于C#中的Unicode * @param strPart 字符串. * @return 16进制字符串. * */ public static String string2HexUTF16LE(String strPart) { return string2HexString(strPart,"UTF-16LE"); } /** * string2HexUnicode. * 字符Unicode串转16进制字符串. * @param strPart 字符串. * @return 16进制字符串. * */ public static String string2HexUnicode(String strPart) { return string2HexString(strPart,"Unicode"); } /** * string2HexGBK. * 字符GBK串转16进制字符串. * @param strPart 字符串. * @return 16进制字符串. * */ public static String string2HexGBK(String strPart) { return string2HexString(strPart,"GBK"); } /** * string2HexString. * 字符串转16进制字符串. * @param strPart 字符串. * @param tochartype hex目标编码. * @return 16进制字符串. * */ public static String string2HexString(String strPart,String tochartype) { try{ return bytes2HexString(strPart.getBytes(tochartype)); }catch (Exception e){ return ""; } } /** * hexUTF82String. * 16进制UTF-8字符串转字符串. * @param src 16进制字符串. * @return 字节数组. * */ public static String hexUTF82String(String src) { return hexString2String(src,"UTF-8","UTF-8"); } /** * hexUTF16LE2String. * 16进制UTF-8字符串转字符串,,此UTF-16LE等同于C#中的Unicode. * @param src 16进制字符串. * @return 字节数组. * */ public static String hexUTF16LE2String(String src) { return hexString2String(src,"UTF-16LE","UTF-8"); } /** * hexGBK2String. * 16进制GBK字符串转字符串. * @param src 16进制字符串. * @return 字节数组. * */ public static String hexGBK2String(String src) { return hexString2String(src,"GBK","UTF-8"); } /** * hexUnicode2String. * 16进制Unicode字符串转字符串. * @param src 16进制字符串. * @return 字节数组. * */ public static String hexUnicode2String(String src) { return hexString2String(src,"Unicode","UTF-8"); } /** * hexString2String 16进制字符串转字符串. * @param src 16进制字符串. * @return 字节数组. * */ public static String hexString2String(String src,String oldchartype, String chartype) { byte[] bts=hexString2Bytes(src); try{if(oldchartype.equals(chartype)) return new String(bts,oldchartype); else return new String(new String(bts,oldchartype).getBytes(),chartype); } catch (Exception e){ return""; } } }从协议应用场景上看,用于充电桩的信息交换,而且示例中,直接给了运营商Id,信息,时间戳,序列号等字段内容。
这些字段如何使用并没有在描述HMAC-MD5的附录C中说明,而是在协议正文。这部分内容如下图:
报文组装过程的实现我们在这里用AbstractHMacMd5Utils类封装这个组装报文的功能。类图如下:
#mermaid-svg-rRI5B4Qun7v8WVtZ .label{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);fill:#333;color:#333}#mermaid-svg-rRI5B4Qun7v8WVtZ .label text{fill:#333}#mermaid-svg-rRI5B4Qun7v8WVtZ .node rect,#mermaid-svg-rRI5B4Qun7v8WVtZ .node circle,#mermaid-svg-rRI5B4Qun7v8WVtZ .node ellipse,#mermaid-svg-rRI5B4Qun7v8WVtZ .node polygon,#mermaid-svg-rRI5B4Qun7v8WVtZ .node path{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-rRI5B4Qun7v8WVtZ .node .label{text-align:center;fill:#333}#mermaid-svg-rRI5B4Qun7v8WVtZ .node.clickable{cursor:pointer}#mermaid-svg-rRI5B4Qun7v8WVtZ .arrowheadPath{fill:#333}#mermaid-svg-rRI5B4Qun7v8WVtZ .edgePath .path{stroke:#333;stroke-width:1.5px}#mermaid-svg-rRI5B4Qun7v8WVtZ .flowchart-link{stroke:#333;fill:none}#mermaid-svg-rRI5B4Qun7v8WVtZ .edgeLabel{background-color:#e8e8e8;text-align:center}#mermaid-svg-rRI5B4Qun7v8WVtZ .edgeLabel rect{opacity:0.9}#mermaid-svg-rRI5B4Qun7v8WVtZ .edgeLabel span{color:#333}#mermaid-svg-rRI5B4Qun7v8WVtZ .cluster rect{fill:#ffffde;stroke:#aa3;stroke-width:1px}#mermaid-svg-rRI5B4Qun7v8WVtZ .cluster text{fill:#333}#mermaid-svg-rRI5B4Qun7v8WVtZ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:12px;background:#ffffde;border:1px solid #aa3;border-radius:2px;pointer-events:none;z-index:100}#mermaid-svg-rRI5B4Qun7v8WVtZ .actor{stroke:#ccf;fill:#ECECFF}#mermaid-svg-rRI5B4Qun7v8WVtZ text.actor>tspan{fill:#000;stroke:none}#mermaid-svg-rRI5B4Qun7v8WVtZ .actor-line{stroke:grey}#mermaid-svg-rRI5B4Qun7v8WVtZ .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333}#mermaid-svg-rRI5B4Qun7v8WVtZ .messageLine1{stroke-width:1.5;stroke-dasharray:2, 2;stroke:#333}#mermaid-svg-rRI5B4Qun7v8WVtZ #arrowhead path{fill:#333;stroke:#333}#mermaid-svg-rRI5B4Qun7v8WVtZ .sequenceNumber{fill:#fff}#mermaid-svg-rRI5B4Qun7v8WVtZ #sequencenumber{fill:#333}#mermaid-svg-rRI5B4Qun7v8WVtZ #crosshead path{fill:#333;stroke:#333}#mermaid-svg-rRI5B4Qun7v8WVtZ .messageText{fill:#333;stroke:#333}#mermaid-svg-rRI5B4Qun7v8WVtZ .labelBox{stroke:#ccf;fill:#ECECFF}#mermaid-svg-rRI5B4Qun7v8WVtZ .labelText,#mermaid-svg-rRI5B4Qun7v8WVtZ .labelText>tspan{fill:#000;stroke:none}#mermaid-svg-rRI5B4Qun7v8WVtZ .loopText,#mermaid-svg-rRI5B4Qun7v8WVtZ .loopText>tspan{fill:#000;stroke:none}#mermaid-svg-rRI5B4Qun7v8WVtZ .loopLine{stroke-width:2px;stroke-dasharray:2, 2;stroke:#ccf;fill:#ccf}#mermaid-svg-rRI5B4Qun7v8WVtZ .note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-rRI5B4Qun7v8WVtZ .noteText,#mermaid-svg-rRI5B4Qun7v8WVtZ .noteText>tspan{fill:#000;stroke:none}#mermaid-svg-rRI5B4Qun7v8WVtZ .activation0{fill:#f4f4f4;stroke:#666}#mermaid-svg-rRI5B4Qun7v8WVtZ .activation1{fill:#f4f4f4;stroke:#666}#mermaid-svg-rRI5B4Qun7v8WVtZ .activation2{fill:#f4f4f4;stroke:#666}#mermaid-svg-rRI5B4Qun7v8WVtZ .mermaid-main-font{font-family:"trebuchet ms", verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-rRI5B4Qun7v8WVtZ .section{stroke:none;opacity:0.2}#mermaid-svg-rRI5B4Qun7v8WVtZ .section0{fill:rgba(102,102,255,0.49)}#mermaid-svg-rRI5B4Qun7v8WVtZ .section2{fill:#fff400}#mermaid-svg-rRI5B4Qun7v8WVtZ .section1,#mermaid-svg-rRI5B4Qun7v8WVtZ .section3{fill:#fff;opacity:0.2}#mermaid-svg-rRI5B4Qun7v8WVtZ .sectionTitle0{fill:#333}#mermaid-svg-rRI5B4Qun7v8WVtZ .sectionTitle1{fill:#333}#mermaid-svg-rRI5B4Qun7v8WVtZ .sectionTitle2{fill:#333}#mermaid-svg-rRI5B4Qun7v8WVtZ .sectionTitle3{fill:#333}#mermaid-svg-rRI5B4Qun7v8WVtZ .sectionTitle{text-anchor:start;font-size:11px;text-height:14px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-rRI5B4Qun7v8WVtZ .grid .tick{stroke:#d3d3d3;opacity:0.8;shape-rendering:crispEdges}#mermaid-svg-rRI5B4Qun7v8WVtZ .grid .tick text{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-rRI5B4Qun7v8WVtZ .grid path{stroke-width:0}#mermaid-svg-rRI5B4Qun7v8WVtZ .today{fill:none;stroke:red;stroke-width:2px}#mermaid-svg-rRI5B4Qun7v8WVtZ .task{stroke-width:2}#mermaid-svg-rRI5B4Qun7v8WVtZ .taskText{text-anchor:middle;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-rRI5B4Qun7v8WVtZ .taskText:not([font-size]){font-size:11px}#mermaid-svg-rRI5B4Qun7v8WVtZ .taskTextOutsideRight{fill:#000;text-anchor:start;font-size:11px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-rRI5B4Qun7v8WVtZ .taskTextOutsideLeft{fill:#000;text-anchor:end;font-size:11px}#mermaid-svg-rRI5B4Qun7v8WVtZ .task.clickable{cursor:pointer}#mermaid-svg-rRI5B4Qun7v8WVtZ .taskText.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-rRI5B4Qun7v8WVtZ .taskTextOutsideLeft.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-rRI5B4Qun7v8WVtZ .taskTextOutsideRight.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-rRI5B4Qun7v8WVtZ .taskText0,#mermaid-svg-rRI5B4Qun7v8WVtZ .taskText1,#mermaid-svg-rRI5B4Qun7v8WVtZ .taskText2,#mermaid-svg-rRI5B4Qun7v8WVtZ .taskText3{fill:#fff}#mermaid-svg-rRI5B4Qun7v8WVtZ .task0,#mermaid-svg-rRI5B4Qun7v8WVtZ .task1,#mermaid-svg-rRI5B4Qun7v8WVtZ .task2,#mermaid-svg-rRI5B4Qun7v8WVtZ .task3{fill:#8a90dd;stroke:#534fbc}#mermaid-svg-rRI5B4Qun7v8WVtZ .taskTextOutside0,#mermaid-svg-rRI5B4Qun7v8WVtZ .taskTextOutside2{fill:#000}#mermaid-svg-rRI5B4Qun7v8WVtZ .taskTextOutside1,#mermaid-svg-rRI5B4Qun7v8WVtZ .taskTextOutside3{fill:#000}#mermaid-svg-rRI5B4Qun7v8WVtZ .active0,#mermaid-svg-rRI5B4Qun7v8WVtZ .active1,#mermaid-svg-rRI5B4Qun7v8WVtZ .active2,#mermaid-svg-rRI5B4Qun7v8WVtZ .active3{fill:#bfc7ff;stroke:#534fbc}#mermaid-svg-rRI5B4Qun7v8WVtZ .activeText0,#mermaid-svg-rRI5B4Qun7v8WVtZ .activeText1,#mermaid-svg-rRI5B4Qun7v8WVtZ .activeText2,#mermaid-svg-rRI5B4Qun7v8WVtZ .activeText3{fill:#000 !important}#mermaid-svg-rRI5B4Qun7v8WVtZ .done0,#mermaid-svg-rRI5B4Qun7v8WVtZ .done1,#mermaid-svg-rRI5B4Qun7v8WVtZ .done2,#mermaid-svg-rRI5B4Qun7v8WVtZ .done3{stroke:grey;fill:#d3d3d3;stroke-width:2}#mermaid-svg-rRI5B4Qun7v8WVtZ .doneText0,#mermaid-svg-rRI5B4Qun7v8WVtZ .doneText1,#mermaid-svg-rRI5B4Qun7v8WVtZ .doneText2,#mermaid-svg-rRI5B4Qun7v8WVtZ .doneText3{fill:#000 !important}#mermaid-svg-rRI5B4Qun7v8WVtZ .crit0,#mermaid-svg-rRI5B4Qun7v8WVtZ .crit1,#mermaid-svg-rRI5B4Qun7v8WVtZ .crit2,#mermaid-svg-rRI5B4Qun7v8WVtZ .crit3{stroke:#f88;fill:red;stroke-width:2}#mermaid-svg-rRI5B4Qun7v8WVtZ .activeCrit0,#mermaid-svg-rRI5B4Qun7v8WVtZ .activeCrit1,#mermaid-svg-rRI5B4Qun7v8WVtZ .activeCrit2,#mermaid-svg-rRI5B4Qun7v8WVtZ .activeCrit3{stroke:#f88;fill:#bfc7ff;stroke-width:2}#mermaid-svg-rRI5B4Qun7v8WVtZ .doneCrit0,#mermaid-svg-rRI5B4Qun7v8WVtZ .doneCrit1,#mermaid-svg-rRI5B4Qun7v8WVtZ .doneCrit2,#mermaid-svg-rRI5B4Qun7v8WVtZ .doneCrit3{stroke:#f88;fill:#d3d3d3;stroke-width:2;cursor:pointer;shape-rendering:crispEdges}#mermaid-svg-rRI5B4Qun7v8WVtZ .milestone{transform:rotate(45deg) scale(0.8, 0.8)}#mermaid-svg-rRI5B4Qun7v8WVtZ .milestoneText{font-style:italic}#mermaid-svg-rRI5B4Qun7v8WVtZ .doneCritText0,#mermaid-svg-rRI5B4Qun7v8WVtZ .doneCritText1,#mermaid-svg-rRI5B4Qun7v8WVtZ .doneCritText2,#mermaid-svg-rRI5B4Qun7v8WVtZ .doneCritText3{fill:#000 !important}#mermaid-svg-rRI5B4Qun7v8WVtZ .activeCritText0,#mermaid-svg-rRI5B4Qun7v8WVtZ .activeCritText1,#mermaid-svg-rRI5B4Qun7v8WVtZ .activeCritText2,#mermaid-svg-rRI5B4Qun7v8WVtZ .activeCritText3{fill:#000 !important}#mermaid-svg-rRI5B4Qun7v8WVtZ .titleText{text-anchor:middle;font-size:18px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-rRI5B4Qun7v8WVtZ g.classGroup text{fill:#9370db;stroke:none;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:10px}#mermaid-svg-rRI5B4Qun7v8WVtZ g.classGroup text .title{font-weight:bolder}#mermaid-svg-rRI5B4Qun7v8WVtZ g.clickable{cursor:pointer}#mermaid-svg-rRI5B4Qun7v8WVtZ g.classGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-rRI5B4Qun7v8WVtZ g.classGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-rRI5B4Qun7v8WVtZ .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5}#mermaid-svg-rRI5B4Qun7v8WVtZ .classLabel .label{fill:#9370db;font-size:10px}#mermaid-svg-rRI5B4Qun7v8WVtZ .relation{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-rRI5B4Qun7v8WVtZ .dashed-line{stroke-dasharray:3}#mermaid-svg-rRI5B4Qun7v8WVtZ #compositionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-rRI5B4Qun7v8WVtZ #compositionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-rRI5B4Qun7v8WVtZ #aggregationStart{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-rRI5B4Qun7v8WVtZ #aggregationEnd{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-rRI5B4Qun7v8WVtZ #dependencyStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-rRI5B4Qun7v8WVtZ #dependencyEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-rRI5B4Qun7v8WVtZ #extensionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-rRI5B4Qun7v8WVtZ #extensionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-rRI5B4Qun7v8WVtZ .commit-id,#mermaid-svg-rRI5B4Qun7v8WVtZ .commit-msg,#mermaid-svg-rRI5B4Qun7v8WVtZ .branch-label{fill:lightgrey;color:lightgrey;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-rRI5B4Qun7v8WVtZ .pieTitleText{text-anchor:middle;font-size:25px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-rRI5B4Qun7v8WVtZ .slice{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-rRI5B4Qun7v8WVtZ g.stateGroup text{fill:#9370db;stroke:none;font-size:10px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-rRI5B4Qun7v8WVtZ g.stateGroup text{fill:#9370db;fill:#333;stroke:none;font-size:10px}#mermaid-svg-rRI5B4Qun7v8WVtZ g.statediagram-cluster .cluster-label text{fill:#333}#mermaid-svg-rRI5B4Qun7v8WVtZ g.stateGroup .state-title{font-weight:bolder;fill:#000}#mermaid-svg-rRI5B4Qun7v8WVtZ g.stateGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-rRI5B4Qun7v8WVtZ g.stateGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-rRI5B4Qun7v8WVtZ .transition{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-rRI5B4Qun7v8WVtZ .stateGroup .composit{fill:white;border-bottom:1px}#mermaid-svg-rRI5B4Qun7v8WVtZ .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px}#mermaid-svg-rRI5B4Qun7v8WVtZ .state-note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-rRI5B4Qun7v8WVtZ .state-note text{fill:black;stroke:none;font-size:10px}#mermaid-svg-rRI5B4Qun7v8WVtZ .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.7}#mermaid-svg-rRI5B4Qun7v8WVtZ .edgeLabel text{fill:#333}#mermaid-svg-rRI5B4Qun7v8WVtZ .stateLabel text{fill:#000;font-size:10px;font-weight:bold;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-rRI5B4Qun7v8WVtZ .node circle.state-start{fill:black;stroke:black}#mermaid-svg-rRI5B4Qun7v8WVtZ .node circle.state-end{fill:black;stroke:white;stroke-width:1.5}#mermaid-svg-rRI5B4Qun7v8WVtZ #statediagram-barbEnd{fill:#9370db}#mermaid-svg-rRI5B4Qun7v8WVtZ .statediagram-cluster rect{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-rRI5B4Qun7v8WVtZ .statediagram-cluster rect.outer{rx:5px;ry:5px}#mermaid-svg-rRI5B4Qun7v8WVtZ .statediagram-state .divider{stroke:#9370db}#mermaid-svg-rRI5B4Qun7v8WVtZ .statediagram-state .title-state{rx:5px;ry:5px}#mermaid-svg-rRI5B4Qun7v8WVtZ .statediagram-cluster.statediagram-cluster .inner{fill:white}#mermaid-svg-rRI5B4Qun7v8WVtZ .statediagram-cluster.statediagram-cluster-alt .inner{fill:#e0e0e0}#mermaid-svg-rRI5B4Qun7v8WVtZ .statediagram-cluster .inner{rx:0;ry:0}#mermaid-svg-rRI5B4Qun7v8WVtZ .statediagram-state rect.basic{rx:5px;ry:5px}#mermaid-svg-rRI5B4Qun7v8WVtZ .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#efefef}#mermaid-svg-rRI5B4Qun7v8WVtZ .note-edge{stroke-dasharray:5}#mermaid-svg-rRI5B4Qun7v8WVtZ .statediagram-note rect{fill:#fff5ad;stroke:#aa3;stroke-width:1px;rx:0;ry:0}:root{--mermaid-font-family: '"trebuchet ms", verdana, arial';--mermaid-font-family: "Comic Sans MS", "Comic Sans", cursive}#mermaid-svg-rRI5B4Qun7v8WVtZ .error-icon{fill:#522}#mermaid-svg-rRI5B4Qun7v8WVtZ .error-text{fill:#522;stroke:#522}#mermaid-svg-rRI5B4Qun7v8WVtZ .edge-thickness-normal{stroke-width:2px}#mermaid-svg-rRI5B4Qun7v8WVtZ .edge-thickness-thick{stroke-width:3.5px}#mermaid-svg-rRI5B4Qun7v8WVtZ .edge-pattern-solid{stroke-dasharray:0}#mermaid-svg-rRI5B4Qun7v8WVtZ .edge-pattern-dashed{stroke-dasharray:3}#mermaid-svg-rRI5B4Qun7v8WVtZ .edge-pattern-dotted{stroke-dasharray:2}#mermaid-svg-rRI5B4Qun7v8WVtZ .marker{fill:#333}#mermaid-svg-rRI5B4Qun7v8WVtZ .marker.cross{stroke:#333} :root { --mermaid-font-family: "trebuchet ms", verdana, arial;} #mermaid-svg-rRI5B4Qun7v8WVtZ { color: rgba(0, 0, 0, 0.75); font: ; } AbstractHMacMd5Utils assembleData(opId:String, data:String, ts:String, seq:String,charset:Charset)这个类是后续开发的工具类的基类,在用工具类中可以方便地调用组装报文的方法assembleData。
类代码如下:
package net.wangds.cnpg.cpmn.utils; import java.nio.charset.Charset; /** * . * <p></p> * * @author eastone 2020/10/21 11:45. */ public class AbstractHMacMd5Utils { public static byte[] assembleData(String operatorID, String data, String timestamp, String seq, Charset charset){ return String.format("%s%s%s%s", operatorID,data, timestamp, seq).getBytes(charset); } }根据算法定义,需要有两个用于异或操作的掩码opad和ipad。
#mermaid-svg-mxR43W53bousPYsd .label{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);fill:#333;color:#333}#mermaid-svg-mxR43W53bousPYsd .label text{fill:#333}#mermaid-svg-mxR43W53bousPYsd .node rect,#mermaid-svg-mxR43W53bousPYsd .node circle,#mermaid-svg-mxR43W53bousPYsd .node ellipse,#mermaid-svg-mxR43W53bousPYsd .node polygon,#mermaid-svg-mxR43W53bousPYsd .node path{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-mxR43W53bousPYsd .node .label{text-align:center;fill:#333}#mermaid-svg-mxR43W53bousPYsd .node.clickable{cursor:pointer}#mermaid-svg-mxR43W53bousPYsd .arrowheadPath{fill:#333}#mermaid-svg-mxR43W53bousPYsd .edgePath .path{stroke:#333;stroke-width:1.5px}#mermaid-svg-mxR43W53bousPYsd .flowchart-link{stroke:#333;fill:none}#mermaid-svg-mxR43W53bousPYsd .edgeLabel{background-color:#e8e8e8;text-align:center}#mermaid-svg-mxR43W53bousPYsd .edgeLabel rect{opacity:0.9}#mermaid-svg-mxR43W53bousPYsd .edgeLabel span{color:#333}#mermaid-svg-mxR43W53bousPYsd .cluster rect{fill:#ffffde;stroke:#aa3;stroke-width:1px}#mermaid-svg-mxR43W53bousPYsd .cluster text{fill:#333}#mermaid-svg-mxR43W53bousPYsd div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:12px;background:#ffffde;border:1px solid #aa3;border-radius:2px;pointer-events:none;z-index:100}#mermaid-svg-mxR43W53bousPYsd .actor{stroke:#ccf;fill:#ECECFF}#mermaid-svg-mxR43W53bousPYsd text.actor>tspan{fill:#000;stroke:none}#mermaid-svg-mxR43W53bousPYsd .actor-line{stroke:grey}#mermaid-svg-mxR43W53bousPYsd .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333}#mermaid-svg-mxR43W53bousPYsd .messageLine1{stroke-width:1.5;stroke-dasharray:2, 2;stroke:#333}#mermaid-svg-mxR43W53bousPYsd #arrowhead path{fill:#333;stroke:#333}#mermaid-svg-mxR43W53bousPYsd .sequenceNumber{fill:#fff}#mermaid-svg-mxR43W53bousPYsd #sequencenumber{fill:#333}#mermaid-svg-mxR43W53bousPYsd #crosshead path{fill:#333;stroke:#333}#mermaid-svg-mxR43W53bousPYsd .messageText{fill:#333;stroke:#333}#mermaid-svg-mxR43W53bousPYsd .labelBox{stroke:#ccf;fill:#ECECFF}#mermaid-svg-mxR43W53bousPYsd .labelText,#mermaid-svg-mxR43W53bousPYsd .labelText>tspan{fill:#000;stroke:none}#mermaid-svg-mxR43W53bousPYsd .loopText,#mermaid-svg-mxR43W53bousPYsd .loopText>tspan{fill:#000;stroke:none}#mermaid-svg-mxR43W53bousPYsd .loopLine{stroke-width:2px;stroke-dasharray:2, 2;stroke:#ccf;fill:#ccf}#mermaid-svg-mxR43W53bousPYsd .note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-mxR43W53bousPYsd .noteText,#mermaid-svg-mxR43W53bousPYsd .noteText>tspan{fill:#000;stroke:none}#mermaid-svg-mxR43W53bousPYsd .activation0{fill:#f4f4f4;stroke:#666}#mermaid-svg-mxR43W53bousPYsd .activation1{fill:#f4f4f4;stroke:#666}#mermaid-svg-mxR43W53bousPYsd .activation2{fill:#f4f4f4;stroke:#666}#mermaid-svg-mxR43W53bousPYsd .mermaid-main-font{font-family:"trebuchet ms", verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-mxR43W53bousPYsd .section{stroke:none;opacity:0.2}#mermaid-svg-mxR43W53bousPYsd .section0{fill:rgba(102,102,255,0.49)}#mermaid-svg-mxR43W53bousPYsd .section2{fill:#fff400}#mermaid-svg-mxR43W53bousPYsd .section1,#mermaid-svg-mxR43W53bousPYsd .section3{fill:#fff;opacity:0.2}#mermaid-svg-mxR43W53bousPYsd .sectionTitle0{fill:#333}#mermaid-svg-mxR43W53bousPYsd .sectionTitle1{fill:#333}#mermaid-svg-mxR43W53bousPYsd .sectionTitle2{fill:#333}#mermaid-svg-mxR43W53bousPYsd .sectionTitle3{fill:#333}#mermaid-svg-mxR43W53bousPYsd .sectionTitle{text-anchor:start;font-size:11px;text-height:14px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-mxR43W53bousPYsd .grid .tick{stroke:#d3d3d3;opacity:0.8;shape-rendering:crispEdges}#mermaid-svg-mxR43W53bousPYsd .grid .tick text{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-mxR43W53bousPYsd .grid path{stroke-width:0}#mermaid-svg-mxR43W53bousPYsd .today{fill:none;stroke:red;stroke-width:2px}#mermaid-svg-mxR43W53bousPYsd .task{stroke-width:2}#mermaid-svg-mxR43W53bousPYsd .taskText{text-anchor:middle;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-mxR43W53bousPYsd .taskText:not([font-size]){font-size:11px}#mermaid-svg-mxR43W53bousPYsd .taskTextOutsideRight{fill:#000;text-anchor:start;font-size:11px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-mxR43W53bousPYsd .taskTextOutsideLeft{fill:#000;text-anchor:end;font-size:11px}#mermaid-svg-mxR43W53bousPYsd .task.clickable{cursor:pointer}#mermaid-svg-mxR43W53bousPYsd .taskText.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-mxR43W53bousPYsd .taskTextOutsideLeft.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-mxR43W53bousPYsd .taskTextOutsideRight.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-mxR43W53bousPYsd .taskText0,#mermaid-svg-mxR43W53bousPYsd .taskText1,#mermaid-svg-mxR43W53bousPYsd .taskText2,#mermaid-svg-mxR43W53bousPYsd .taskText3{fill:#fff}#mermaid-svg-mxR43W53bousPYsd .task0,#mermaid-svg-mxR43W53bousPYsd .task1,#mermaid-svg-mxR43W53bousPYsd .task2,#mermaid-svg-mxR43W53bousPYsd .task3{fill:#8a90dd;stroke:#534fbc}#mermaid-svg-mxR43W53bousPYsd .taskTextOutside0,#mermaid-svg-mxR43W53bousPYsd .taskTextOutside2{fill:#000}#mermaid-svg-mxR43W53bousPYsd .taskTextOutside1,#mermaid-svg-mxR43W53bousPYsd .taskTextOutside3{fill:#000}#mermaid-svg-mxR43W53bousPYsd .active0,#mermaid-svg-mxR43W53bousPYsd .active1,#mermaid-svg-mxR43W53bousPYsd .active2,#mermaid-svg-mxR43W53bousPYsd .active3{fill:#bfc7ff;stroke:#534fbc}#mermaid-svg-mxR43W53bousPYsd .activeText0,#mermaid-svg-mxR43W53bousPYsd .activeText1,#mermaid-svg-mxR43W53bousPYsd .activeText2,#mermaid-svg-mxR43W53bousPYsd .activeText3{fill:#000 !important}#mermaid-svg-mxR43W53bousPYsd .done0,#mermaid-svg-mxR43W53bousPYsd .done1,#mermaid-svg-mxR43W53bousPYsd .done2,#mermaid-svg-mxR43W53bousPYsd .done3{stroke:grey;fill:#d3d3d3;stroke-width:2}#mermaid-svg-mxR43W53bousPYsd .doneText0,#mermaid-svg-mxR43W53bousPYsd .doneText1,#mermaid-svg-mxR43W53bousPYsd .doneText2,#mermaid-svg-mxR43W53bousPYsd .doneText3{fill:#000 !important}#mermaid-svg-mxR43W53bousPYsd .crit0,#mermaid-svg-mxR43W53bousPYsd .crit1,#mermaid-svg-mxR43W53bousPYsd .crit2,#mermaid-svg-mxR43W53bousPYsd .crit3{stroke:#f88;fill:red;stroke-width:2}#mermaid-svg-mxR43W53bousPYsd .activeCrit0,#mermaid-svg-mxR43W53bousPYsd .activeCrit1,#mermaid-svg-mxR43W53bousPYsd .activeCrit2,#mermaid-svg-mxR43W53bousPYsd .activeCrit3{stroke:#f88;fill:#bfc7ff;stroke-width:2}#mermaid-svg-mxR43W53bousPYsd .doneCrit0,#mermaid-svg-mxR43W53bousPYsd .doneCrit1,#mermaid-svg-mxR43W53bousPYsd .doneCrit2,#mermaid-svg-mxR43W53bousPYsd .doneCrit3{stroke:#f88;fill:#d3d3d3;stroke-width:2;cursor:pointer;shape-rendering:crispEdges}#mermaid-svg-mxR43W53bousPYsd .milestone{transform:rotate(45deg) scale(0.8, 0.8)}#mermaid-svg-mxR43W53bousPYsd .milestoneText{font-style:italic}#mermaid-svg-mxR43W53bousPYsd .doneCritText0,#mermaid-svg-mxR43W53bousPYsd .doneCritText1,#mermaid-svg-mxR43W53bousPYsd .doneCritText2,#mermaid-svg-mxR43W53bousPYsd .doneCritText3{fill:#000 !important}#mermaid-svg-mxR43W53bousPYsd .activeCritText0,#mermaid-svg-mxR43W53bousPYsd .activeCritText1,#mermaid-svg-mxR43W53bousPYsd .activeCritText2,#mermaid-svg-mxR43W53bousPYsd .activeCritText3{fill:#000 !important}#mermaid-svg-mxR43W53bousPYsd .titleText{text-anchor:middle;font-size:18px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-mxR43W53bousPYsd g.classGroup text{fill:#9370db;stroke:none;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:10px}#mermaid-svg-mxR43W53bousPYsd g.classGroup text .title{font-weight:bolder}#mermaid-svg-mxR43W53bousPYsd g.clickable{cursor:pointer}#mermaid-svg-mxR43W53bousPYsd g.classGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-mxR43W53bousPYsd g.classGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-mxR43W53bousPYsd .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5}#mermaid-svg-mxR43W53bousPYsd .classLabel .label{fill:#9370db;font-size:10px}#mermaid-svg-mxR43W53bousPYsd .relation{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-mxR43W53bousPYsd .dashed-line{stroke-dasharray:3}#mermaid-svg-mxR43W53bousPYsd #compositionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-mxR43W53bousPYsd #compositionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-mxR43W53bousPYsd #aggregationStart{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-mxR43W53bousPYsd #aggregationEnd{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-mxR43W53bousPYsd #dependencyStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-mxR43W53bousPYsd #dependencyEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-mxR43W53bousPYsd #extensionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-mxR43W53bousPYsd #extensionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-mxR43W53bousPYsd .commit-id,#mermaid-svg-mxR43W53bousPYsd .commit-msg,#mermaid-svg-mxR43W53bousPYsd .branch-label{fill:lightgrey;color:lightgrey;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-mxR43W53bousPYsd .pieTitleText{text-anchor:middle;font-size:25px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-mxR43W53bousPYsd .slice{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-mxR43W53bousPYsd g.stateGroup text{fill:#9370db;stroke:none;font-size:10px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-mxR43W53bousPYsd g.stateGroup text{fill:#9370db;fill:#333;stroke:none;font-size:10px}#mermaid-svg-mxR43W53bousPYsd g.statediagram-cluster .cluster-label text{fill:#333}#mermaid-svg-mxR43W53bousPYsd g.stateGroup .state-title{font-weight:bolder;fill:#000}#mermaid-svg-mxR43W53bousPYsd g.stateGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-mxR43W53bousPYsd g.stateGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-mxR43W53bousPYsd .transition{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-mxR43W53bousPYsd .stateGroup .composit{fill:white;border-bottom:1px}#mermaid-svg-mxR43W53bousPYsd .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px}#mermaid-svg-mxR43W53bousPYsd .state-note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-mxR43W53bousPYsd .state-note text{fill:black;stroke:none;font-size:10px}#mermaid-svg-mxR43W53bousPYsd .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.7}#mermaid-svg-mxR43W53bousPYsd .edgeLabel text{fill:#333}#mermaid-svg-mxR43W53bousPYsd .stateLabel text{fill:#000;font-size:10px;font-weight:bold;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-mxR43W53bousPYsd .node circle.state-start{fill:black;stroke:black}#mermaid-svg-mxR43W53bousPYsd .node circle.state-end{fill:black;stroke:white;stroke-width:1.5}#mermaid-svg-mxR43W53bousPYsd #statediagram-barbEnd{fill:#9370db}#mermaid-svg-mxR43W53bousPYsd .statediagram-cluster rect{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-mxR43W53bousPYsd .statediagram-cluster rect.outer{rx:5px;ry:5px}#mermaid-svg-mxR43W53bousPYsd .statediagram-state .divider{stroke:#9370db}#mermaid-svg-mxR43W53bousPYsd .statediagram-state .title-state{rx:5px;ry:5px}#mermaid-svg-mxR43W53bousPYsd .statediagram-cluster.statediagram-cluster .inner{fill:white}#mermaid-svg-mxR43W53bousPYsd .statediagram-cluster.statediagram-cluster-alt .inner{fill:#e0e0e0}#mermaid-svg-mxR43W53bousPYsd .statediagram-cluster .inner{rx:0;ry:0}#mermaid-svg-mxR43W53bousPYsd .statediagram-state rect.basic{rx:5px;ry:5px}#mermaid-svg-mxR43W53bousPYsd .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#efefef}#mermaid-svg-mxR43W53bousPYsd .note-edge{stroke-dasharray:5}#mermaid-svg-mxR43W53bousPYsd .statediagram-note rect{fill:#fff5ad;stroke:#aa3;stroke-width:1px;rx:0;ry:0}:root{--mermaid-font-family: '"trebuchet ms", verdana, arial';--mermaid-font-family: "Comic Sans MS", "Comic Sans", cursive}#mermaid-svg-mxR43W53bousPYsd .error-icon{fill:#522}#mermaid-svg-mxR43W53bousPYsd .error-text{fill:#522;stroke:#522}#mermaid-svg-mxR43W53bousPYsd .edge-thickness-normal{stroke-width:2px}#mermaid-svg-mxR43W53bousPYsd .edge-thickness-thick{stroke-width:3.5px}#mermaid-svg-mxR43W53bousPYsd .edge-pattern-solid{stroke-dasharray:0}#mermaid-svg-mxR43W53bousPYsd .edge-pattern-dashed{stroke-dasharray:3}#mermaid-svg-mxR43W53bousPYsd .edge-pattern-dotted{stroke-dasharray:2}#mermaid-svg-mxR43W53bousPYsd .marker{fill:#333}#mermaid-svg-mxR43W53bousPYsd .marker.cross{stroke:#333} :root { --mermaid-font-family: "trebuchet ms", verdana, arial;} #mermaid-svg-mxR43W53bousPYsd { color: rgba(0, 0, 0, 0.75); font: ; } AbstractHMacMd5Utils HMacMd5Utils <static> <final> - opad : byte = 0x5c; <static> <final> - ipad : byte = 0x36; <static> <final> - keyLen: byte = 64;同时定义了一个常量keyLen用来代表密钥长度。
算法中的密钥,长度为64字节,也就是64*8=512 bit。
根据算法要求,密钥长度小于64位的时候,要补"\0"。协议描述给的示例密钥是"1234567890abcdef",所以最开始补了字符"0",结果不对,要用"\0"才可以。
另外,当密钥字符串长度大于64字节的时候,我们这里选择用密钥的md5散列码作为真实密钥。
关于补0的问题,我们这里没有使用补零的过程,因为Java中byte默认是0。密钥的处理过程用到了ByteBuffer,创建固定大小的ByteBuffer对象,从起始位置写入密钥,未写入的部分自然是"\0".
Created with Raphaël 2.2.0 开始 初始化buf:ByteBuffer变量。 参数密钥长度是否大于64字节? 向buf中写入密钥的md5散列码 结束:返回buf中的数组 将密钥写入buf对象 yes no实现后,类图如下:
#mermaid-svg-CE5JlQYa9MuvsBQB .label{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);fill:#333;color:#333}#mermaid-svg-CE5JlQYa9MuvsBQB .label text{fill:#333}#mermaid-svg-CE5JlQYa9MuvsBQB .node rect,#mermaid-svg-CE5JlQYa9MuvsBQB .node circle,#mermaid-svg-CE5JlQYa9MuvsBQB .node ellipse,#mermaid-svg-CE5JlQYa9MuvsBQB .node polygon,#mermaid-svg-CE5JlQYa9MuvsBQB .node path{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-CE5JlQYa9MuvsBQB .node .label{text-align:center;fill:#333}#mermaid-svg-CE5JlQYa9MuvsBQB .node.clickable{cursor:pointer}#mermaid-svg-CE5JlQYa9MuvsBQB .arrowheadPath{fill:#333}#mermaid-svg-CE5JlQYa9MuvsBQB .edgePath .path{stroke:#333;stroke-width:1.5px}#mermaid-svg-CE5JlQYa9MuvsBQB .flowchart-link{stroke:#333;fill:none}#mermaid-svg-CE5JlQYa9MuvsBQB .edgeLabel{background-color:#e8e8e8;text-align:center}#mermaid-svg-CE5JlQYa9MuvsBQB .edgeLabel rect{opacity:0.9}#mermaid-svg-CE5JlQYa9MuvsBQB .edgeLabel span{color:#333}#mermaid-svg-CE5JlQYa9MuvsBQB .cluster rect{fill:#ffffde;stroke:#aa3;stroke-width:1px}#mermaid-svg-CE5JlQYa9MuvsBQB .cluster text{fill:#333}#mermaid-svg-CE5JlQYa9MuvsBQB div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:12px;background:#ffffde;border:1px solid #aa3;border-radius:2px;pointer-events:none;z-index:100}#mermaid-svg-CE5JlQYa9MuvsBQB .actor{stroke:#ccf;fill:#ECECFF}#mermaid-svg-CE5JlQYa9MuvsBQB text.actor>tspan{fill:#000;stroke:none}#mermaid-svg-CE5JlQYa9MuvsBQB .actor-line{stroke:grey}#mermaid-svg-CE5JlQYa9MuvsBQB .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333}#mermaid-svg-CE5JlQYa9MuvsBQB .messageLine1{stroke-width:1.5;stroke-dasharray:2, 2;stroke:#333}#mermaid-svg-CE5JlQYa9MuvsBQB #arrowhead path{fill:#333;stroke:#333}#mermaid-svg-CE5JlQYa9MuvsBQB .sequenceNumber{fill:#fff}#mermaid-svg-CE5JlQYa9MuvsBQB #sequencenumber{fill:#333}#mermaid-svg-CE5JlQYa9MuvsBQB #crosshead path{fill:#333;stroke:#333}#mermaid-svg-CE5JlQYa9MuvsBQB .messageText{fill:#333;stroke:#333}#mermaid-svg-CE5JlQYa9MuvsBQB .labelBox{stroke:#ccf;fill:#ECECFF}#mermaid-svg-CE5JlQYa9MuvsBQB .labelText,#mermaid-svg-CE5JlQYa9MuvsBQB .labelText>tspan{fill:#000;stroke:none}#mermaid-svg-CE5JlQYa9MuvsBQB .loopText,#mermaid-svg-CE5JlQYa9MuvsBQB .loopText>tspan{fill:#000;stroke:none}#mermaid-svg-CE5JlQYa9MuvsBQB .loopLine{stroke-width:2px;stroke-dasharray:2, 2;stroke:#ccf;fill:#ccf}#mermaid-svg-CE5JlQYa9MuvsBQB .note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-CE5JlQYa9MuvsBQB .noteText,#mermaid-svg-CE5JlQYa9MuvsBQB .noteText>tspan{fill:#000;stroke:none}#mermaid-svg-CE5JlQYa9MuvsBQB .activation0{fill:#f4f4f4;stroke:#666}#mermaid-svg-CE5JlQYa9MuvsBQB .activation1{fill:#f4f4f4;stroke:#666}#mermaid-svg-CE5JlQYa9MuvsBQB .activation2{fill:#f4f4f4;stroke:#666}#mermaid-svg-CE5JlQYa9MuvsBQB .mermaid-main-font{font-family:"trebuchet ms", verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-CE5JlQYa9MuvsBQB .section{stroke:none;opacity:0.2}#mermaid-svg-CE5JlQYa9MuvsBQB .section0{fill:rgba(102,102,255,0.49)}#mermaid-svg-CE5JlQYa9MuvsBQB .section2{fill:#fff400}#mermaid-svg-CE5JlQYa9MuvsBQB .section1,#mermaid-svg-CE5JlQYa9MuvsBQB .section3{fill:#fff;opacity:0.2}#mermaid-svg-CE5JlQYa9MuvsBQB .sectionTitle0{fill:#333}#mermaid-svg-CE5JlQYa9MuvsBQB .sectionTitle1{fill:#333}#mermaid-svg-CE5JlQYa9MuvsBQB .sectionTitle2{fill:#333}#mermaid-svg-CE5JlQYa9MuvsBQB .sectionTitle3{fill:#333}#mermaid-svg-CE5JlQYa9MuvsBQB .sectionTitle{text-anchor:start;font-size:11px;text-height:14px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-CE5JlQYa9MuvsBQB .grid .tick{stroke:#d3d3d3;opacity:0.8;shape-rendering:crispEdges}#mermaid-svg-CE5JlQYa9MuvsBQB .grid .tick text{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-CE5JlQYa9MuvsBQB .grid path{stroke-width:0}#mermaid-svg-CE5JlQYa9MuvsBQB .today{fill:none;stroke:red;stroke-width:2px}#mermaid-svg-CE5JlQYa9MuvsBQB .task{stroke-width:2}#mermaid-svg-CE5JlQYa9MuvsBQB .taskText{text-anchor:middle;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-CE5JlQYa9MuvsBQB .taskText:not([font-size]){font-size:11px}#mermaid-svg-CE5JlQYa9MuvsBQB .taskTextOutsideRight{fill:#000;text-anchor:start;font-size:11px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-CE5JlQYa9MuvsBQB .taskTextOutsideLeft{fill:#000;text-anchor:end;font-size:11px}#mermaid-svg-CE5JlQYa9MuvsBQB .task.clickable{cursor:pointer}#mermaid-svg-CE5JlQYa9MuvsBQB .taskText.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-CE5JlQYa9MuvsBQB .taskTextOutsideLeft.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-CE5JlQYa9MuvsBQB .taskTextOutsideRight.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-CE5JlQYa9MuvsBQB .taskText0,#mermaid-svg-CE5JlQYa9MuvsBQB .taskText1,#mermaid-svg-CE5JlQYa9MuvsBQB .taskText2,#mermaid-svg-CE5JlQYa9MuvsBQB .taskText3{fill:#fff}#mermaid-svg-CE5JlQYa9MuvsBQB .task0,#mermaid-svg-CE5JlQYa9MuvsBQB .task1,#mermaid-svg-CE5JlQYa9MuvsBQB .task2,#mermaid-svg-CE5JlQYa9MuvsBQB .task3{fill:#8a90dd;stroke:#534fbc}#mermaid-svg-CE5JlQYa9MuvsBQB .taskTextOutside0,#mermaid-svg-CE5JlQYa9MuvsBQB .taskTextOutside2{fill:#000}#mermaid-svg-CE5JlQYa9MuvsBQB .taskTextOutside1,#mermaid-svg-CE5JlQYa9MuvsBQB .taskTextOutside3{fill:#000}#mermaid-svg-CE5JlQYa9MuvsBQB .active0,#mermaid-svg-CE5JlQYa9MuvsBQB .active1,#mermaid-svg-CE5JlQYa9MuvsBQB .active2,#mermaid-svg-CE5JlQYa9MuvsBQB .active3{fill:#bfc7ff;stroke:#534fbc}#mermaid-svg-CE5JlQYa9MuvsBQB .activeText0,#mermaid-svg-CE5JlQYa9MuvsBQB .activeText1,#mermaid-svg-CE5JlQYa9MuvsBQB .activeText2,#mermaid-svg-CE5JlQYa9MuvsBQB .activeText3{fill:#000 !important}#mermaid-svg-CE5JlQYa9MuvsBQB .done0,#mermaid-svg-CE5JlQYa9MuvsBQB .done1,#mermaid-svg-CE5JlQYa9MuvsBQB .done2,#mermaid-svg-CE5JlQYa9MuvsBQB .done3{stroke:grey;fill:#d3d3d3;stroke-width:2}#mermaid-svg-CE5JlQYa9MuvsBQB .doneText0,#mermaid-svg-CE5JlQYa9MuvsBQB .doneText1,#mermaid-svg-CE5JlQYa9MuvsBQB .doneText2,#mermaid-svg-CE5JlQYa9MuvsBQB .doneText3{fill:#000 !important}#mermaid-svg-CE5JlQYa9MuvsBQB .crit0,#mermaid-svg-CE5JlQYa9MuvsBQB .crit1,#mermaid-svg-CE5JlQYa9MuvsBQB .crit2,#mermaid-svg-CE5JlQYa9MuvsBQB .crit3{stroke:#f88;fill:red;stroke-width:2}#mermaid-svg-CE5JlQYa9MuvsBQB .activeCrit0,#mermaid-svg-CE5JlQYa9MuvsBQB .activeCrit1,#mermaid-svg-CE5JlQYa9MuvsBQB .activeCrit2,#mermaid-svg-CE5JlQYa9MuvsBQB .activeCrit3{stroke:#f88;fill:#bfc7ff;stroke-width:2}#mermaid-svg-CE5JlQYa9MuvsBQB .doneCrit0,#mermaid-svg-CE5JlQYa9MuvsBQB .doneCrit1,#mermaid-svg-CE5JlQYa9MuvsBQB .doneCrit2,#mermaid-svg-CE5JlQYa9MuvsBQB .doneCrit3{stroke:#f88;fill:#d3d3d3;stroke-width:2;cursor:pointer;shape-rendering:crispEdges}#mermaid-svg-CE5JlQYa9MuvsBQB .milestone{transform:rotate(45deg) scale(0.8, 0.8)}#mermaid-svg-CE5JlQYa9MuvsBQB .milestoneText{font-style:italic}#mermaid-svg-CE5JlQYa9MuvsBQB .doneCritText0,#mermaid-svg-CE5JlQYa9MuvsBQB .doneCritText1,#mermaid-svg-CE5JlQYa9MuvsBQB .doneCritText2,#mermaid-svg-CE5JlQYa9MuvsBQB .doneCritText3{fill:#000 !important}#mermaid-svg-CE5JlQYa9MuvsBQB .activeCritText0,#mermaid-svg-CE5JlQYa9MuvsBQB .activeCritText1,#mermaid-svg-CE5JlQYa9MuvsBQB .activeCritText2,#mermaid-svg-CE5JlQYa9MuvsBQB .activeCritText3{fill:#000 !important}#mermaid-svg-CE5JlQYa9MuvsBQB .titleText{text-anchor:middle;font-size:18px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-CE5JlQYa9MuvsBQB g.classGroup text{fill:#9370db;stroke:none;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:10px}#mermaid-svg-CE5JlQYa9MuvsBQB g.classGroup text .title{font-weight:bolder}#mermaid-svg-CE5JlQYa9MuvsBQB g.clickable{cursor:pointer}#mermaid-svg-CE5JlQYa9MuvsBQB g.classGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-CE5JlQYa9MuvsBQB g.classGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-CE5JlQYa9MuvsBQB .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5}#mermaid-svg-CE5JlQYa9MuvsBQB .classLabel .label{fill:#9370db;font-size:10px}#mermaid-svg-CE5JlQYa9MuvsBQB .relation{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-CE5JlQYa9MuvsBQB .dashed-line{stroke-dasharray:3}#mermaid-svg-CE5JlQYa9MuvsBQB #compositionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-CE5JlQYa9MuvsBQB #compositionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-CE5JlQYa9MuvsBQB #aggregationStart{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-CE5JlQYa9MuvsBQB #aggregationEnd{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-CE5JlQYa9MuvsBQB #dependencyStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-CE5JlQYa9MuvsBQB #dependencyEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-CE5JlQYa9MuvsBQB #extensionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-CE5JlQYa9MuvsBQB #extensionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-CE5JlQYa9MuvsBQB .commit-id,#mermaid-svg-CE5JlQYa9MuvsBQB .commit-msg,#mermaid-svg-CE5JlQYa9MuvsBQB .branch-label{fill:lightgrey;color:lightgrey;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-CE5JlQYa9MuvsBQB .pieTitleText{text-anchor:middle;font-size:25px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-CE5JlQYa9MuvsBQB .slice{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-CE5JlQYa9MuvsBQB g.stateGroup text{fill:#9370db;stroke:none;font-size:10px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-CE5JlQYa9MuvsBQB g.stateGroup text{fill:#9370db;fill:#333;stroke:none;font-size:10px}#mermaid-svg-CE5JlQYa9MuvsBQB g.statediagram-cluster .cluster-label text{fill:#333}#mermaid-svg-CE5JlQYa9MuvsBQB g.stateGroup .state-title{font-weight:bolder;fill:#000}#mermaid-svg-CE5JlQYa9MuvsBQB g.stateGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-CE5JlQYa9MuvsBQB g.stateGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-CE5JlQYa9MuvsBQB .transition{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-CE5JlQYa9MuvsBQB .stateGroup .composit{fill:white;border-bottom:1px}#mermaid-svg-CE5JlQYa9MuvsBQB .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px}#mermaid-svg-CE5JlQYa9MuvsBQB .state-note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-CE5JlQYa9MuvsBQB .state-note text{fill:black;stroke:none;font-size:10px}#mermaid-svg-CE5JlQYa9MuvsBQB .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.7}#mermaid-svg-CE5JlQYa9MuvsBQB .edgeLabel text{fill:#333}#mermaid-svg-CE5JlQYa9MuvsBQB .stateLabel text{fill:#000;font-size:10px;font-weight:bold;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-CE5JlQYa9MuvsBQB .node circle.state-start{fill:black;stroke:black}#mermaid-svg-CE5JlQYa9MuvsBQB .node circle.state-end{fill:black;stroke:white;stroke-width:1.5}#mermaid-svg-CE5JlQYa9MuvsBQB #statediagram-barbEnd{fill:#9370db}#mermaid-svg-CE5JlQYa9MuvsBQB .statediagram-cluster rect{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-CE5JlQYa9MuvsBQB .statediagram-cluster rect.outer{rx:5px;ry:5px}#mermaid-svg-CE5JlQYa9MuvsBQB .statediagram-state .divider{stroke:#9370db}#mermaid-svg-CE5JlQYa9MuvsBQB .statediagram-state .title-state{rx:5px;ry:5px}#mermaid-svg-CE5JlQYa9MuvsBQB .statediagram-cluster.statediagram-cluster .inner{fill:white}#mermaid-svg-CE5JlQYa9MuvsBQB .statediagram-cluster.statediagram-cluster-alt .inner{fill:#e0e0e0}#mermaid-svg-CE5JlQYa9MuvsBQB .statediagram-cluster .inner{rx:0;ry:0}#mermaid-svg-CE5JlQYa9MuvsBQB .statediagram-state rect.basic{rx:5px;ry:5px}#mermaid-svg-CE5JlQYa9MuvsBQB .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#efefef}#mermaid-svg-CE5JlQYa9MuvsBQB .note-edge{stroke-dasharray:5}#mermaid-svg-CE5JlQYa9MuvsBQB .statediagram-note rect{fill:#fff5ad;stroke:#aa3;stroke-width:1px;rx:0;ry:0}:root{--mermaid-font-family: '"trebuchet ms", verdana, arial';--mermaid-font-family: "Comic Sans MS", "Comic Sans", cursive}#mermaid-svg-CE5JlQYa9MuvsBQB .error-icon{fill:#522}#mermaid-svg-CE5JlQYa9MuvsBQB .error-text{fill:#522;stroke:#522}#mermaid-svg-CE5JlQYa9MuvsBQB .edge-thickness-normal{stroke-width:2px}#mermaid-svg-CE5JlQYa9MuvsBQB .edge-thickness-thick{stroke-width:3.5px}#mermaid-svg-CE5JlQYa9MuvsBQB .edge-pattern-solid{stroke-dasharray:0}#mermaid-svg-CE5JlQYa9MuvsBQB .edge-pattern-dashed{stroke-dasharray:3}#mermaid-svg-CE5JlQYa9MuvsBQB .edge-pattern-dotted{stroke-dasharray:2}#mermaid-svg-CE5JlQYa9MuvsBQB .marker{fill:#333}#mermaid-svg-CE5JlQYa9MuvsBQB .marker.cross{stroke:#333} :root { --mermaid-font-family: "trebuchet ms", verdana, arial;} #mermaid-svg-CE5JlQYa9MuvsBQB { color: rgba(0, 0, 0, 0.75); font: ; } AbstractHMacMd5Utils HMacMd5Utils <static> <final> - opad : byte = 0x5c; <static> <final> - ipad : byte = 0x36; <static> <final> - keyLen: byte = 64; static> - generateKey(key:String)代码:
/** * 处理密钥. * <p>密钥补为64字节;长于64字节的用md5散列结果.</p> * @param key 签名密钥. * @return 结果. */ private static byte[] generateKey(String key) { ByteBuffer buf = ByteBuffer.allocate(keyLen); if(key.length()>keyLen){ buf.put(Md5Utils.md5Bytes(key.getBytes(StandardCharsets.UTF_8))) ; }else{ buf.put(key.getBytes()); } return buf.array(); }istr和ostr是密钥数组分别和ipad和opad常数做异或得到的结果。类图如下:
#mermaid-svg-ttNIGDD4mKJX3jMA .label{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);fill:#333;color:#333}#mermaid-svg-ttNIGDD4mKJX3jMA .label text{fill:#333}#mermaid-svg-ttNIGDD4mKJX3jMA .node rect,#mermaid-svg-ttNIGDD4mKJX3jMA .node circle,#mermaid-svg-ttNIGDD4mKJX3jMA .node ellipse,#mermaid-svg-ttNIGDD4mKJX3jMA .node polygon,#mermaid-svg-ttNIGDD4mKJX3jMA .node path{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-ttNIGDD4mKJX3jMA .node .label{text-align:center;fill:#333}#mermaid-svg-ttNIGDD4mKJX3jMA .node.clickable{cursor:pointer}#mermaid-svg-ttNIGDD4mKJX3jMA .arrowheadPath{fill:#333}#mermaid-svg-ttNIGDD4mKJX3jMA .edgePath .path{stroke:#333;stroke-width:1.5px}#mermaid-svg-ttNIGDD4mKJX3jMA .flowchart-link{stroke:#333;fill:none}#mermaid-svg-ttNIGDD4mKJX3jMA .edgeLabel{background-color:#e8e8e8;text-align:center}#mermaid-svg-ttNIGDD4mKJX3jMA .edgeLabel rect{opacity:0.9}#mermaid-svg-ttNIGDD4mKJX3jMA .edgeLabel span{color:#333}#mermaid-svg-ttNIGDD4mKJX3jMA .cluster rect{fill:#ffffde;stroke:#aa3;stroke-width:1px}#mermaid-svg-ttNIGDD4mKJX3jMA .cluster text{fill:#333}#mermaid-svg-ttNIGDD4mKJX3jMA div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:12px;background:#ffffde;border:1px solid #aa3;border-radius:2px;pointer-events:none;z-index:100}#mermaid-svg-ttNIGDD4mKJX3jMA .actor{stroke:#ccf;fill:#ECECFF}#mermaid-svg-ttNIGDD4mKJX3jMA text.actor>tspan{fill:#000;stroke:none}#mermaid-svg-ttNIGDD4mKJX3jMA .actor-line{stroke:grey}#mermaid-svg-ttNIGDD4mKJX3jMA .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333}#mermaid-svg-ttNIGDD4mKJX3jMA .messageLine1{stroke-width:1.5;stroke-dasharray:2, 2;stroke:#333}#mermaid-svg-ttNIGDD4mKJX3jMA #arrowhead path{fill:#333;stroke:#333}#mermaid-svg-ttNIGDD4mKJX3jMA .sequenceNumber{fill:#fff}#mermaid-svg-ttNIGDD4mKJX3jMA #sequencenumber{fill:#333}#mermaid-svg-ttNIGDD4mKJX3jMA #crosshead path{fill:#333;stroke:#333}#mermaid-svg-ttNIGDD4mKJX3jMA .messageText{fill:#333;stroke:#333}#mermaid-svg-ttNIGDD4mKJX3jMA .labelBox{stroke:#ccf;fill:#ECECFF}#mermaid-svg-ttNIGDD4mKJX3jMA .labelText,#mermaid-svg-ttNIGDD4mKJX3jMA .labelText>tspan{fill:#000;stroke:none}#mermaid-svg-ttNIGDD4mKJX3jMA .loopText,#mermaid-svg-ttNIGDD4mKJX3jMA .loopText>tspan{fill:#000;stroke:none}#mermaid-svg-ttNIGDD4mKJX3jMA .loopLine{stroke-width:2px;stroke-dasharray:2, 2;stroke:#ccf;fill:#ccf}#mermaid-svg-ttNIGDD4mKJX3jMA .note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-ttNIGDD4mKJX3jMA .noteText,#mermaid-svg-ttNIGDD4mKJX3jMA .noteText>tspan{fill:#000;stroke:none}#mermaid-svg-ttNIGDD4mKJX3jMA .activation0{fill:#f4f4f4;stroke:#666}#mermaid-svg-ttNIGDD4mKJX3jMA .activation1{fill:#f4f4f4;stroke:#666}#mermaid-svg-ttNIGDD4mKJX3jMA .activation2{fill:#f4f4f4;stroke:#666}#mermaid-svg-ttNIGDD4mKJX3jMA .mermaid-main-font{font-family:"trebuchet ms", verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-ttNIGDD4mKJX3jMA .section{stroke:none;opacity:0.2}#mermaid-svg-ttNIGDD4mKJX3jMA .section0{fill:rgba(102,102,255,0.49)}#mermaid-svg-ttNIGDD4mKJX3jMA .section2{fill:#fff400}#mermaid-svg-ttNIGDD4mKJX3jMA .section1,#mermaid-svg-ttNIGDD4mKJX3jMA .section3{fill:#fff;opacity:0.2}#mermaid-svg-ttNIGDD4mKJX3jMA .sectionTitle0{fill:#333}#mermaid-svg-ttNIGDD4mKJX3jMA .sectionTitle1{fill:#333}#mermaid-svg-ttNIGDD4mKJX3jMA .sectionTitle2{fill:#333}#mermaid-svg-ttNIGDD4mKJX3jMA .sectionTitle3{fill:#333}#mermaid-svg-ttNIGDD4mKJX3jMA .sectionTitle{text-anchor:start;font-size:11px;text-height:14px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-ttNIGDD4mKJX3jMA .grid .tick{stroke:#d3d3d3;opacity:0.8;shape-rendering:crispEdges}#mermaid-svg-ttNIGDD4mKJX3jMA .grid .tick text{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-ttNIGDD4mKJX3jMA .grid path{stroke-width:0}#mermaid-svg-ttNIGDD4mKJX3jMA .today{fill:none;stroke:red;stroke-width:2px}#mermaid-svg-ttNIGDD4mKJX3jMA .task{stroke-width:2}#mermaid-svg-ttNIGDD4mKJX3jMA .taskText{text-anchor:middle;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-ttNIGDD4mKJX3jMA .taskText:not([font-size]){font-size:11px}#mermaid-svg-ttNIGDD4mKJX3jMA .taskTextOutsideRight{fill:#000;text-anchor:start;font-size:11px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-ttNIGDD4mKJX3jMA .taskTextOutsideLeft{fill:#000;text-anchor:end;font-size:11px}#mermaid-svg-ttNIGDD4mKJX3jMA .task.clickable{cursor:pointer}#mermaid-svg-ttNIGDD4mKJX3jMA .taskText.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-ttNIGDD4mKJX3jMA .taskTextOutsideLeft.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-ttNIGDD4mKJX3jMA .taskTextOutsideRight.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-ttNIGDD4mKJX3jMA .taskText0,#mermaid-svg-ttNIGDD4mKJX3jMA .taskText1,#mermaid-svg-ttNIGDD4mKJX3jMA .taskText2,#mermaid-svg-ttNIGDD4mKJX3jMA .taskText3{fill:#fff}#mermaid-svg-ttNIGDD4mKJX3jMA .task0,#mermaid-svg-ttNIGDD4mKJX3jMA .task1,#mermaid-svg-ttNIGDD4mKJX3jMA .task2,#mermaid-svg-ttNIGDD4mKJX3jMA .task3{fill:#8a90dd;stroke:#534fbc}#mermaid-svg-ttNIGDD4mKJX3jMA .taskTextOutside0,#mermaid-svg-ttNIGDD4mKJX3jMA .taskTextOutside2{fill:#000}#mermaid-svg-ttNIGDD4mKJX3jMA .taskTextOutside1,#mermaid-svg-ttNIGDD4mKJX3jMA .taskTextOutside3{fill:#000}#mermaid-svg-ttNIGDD4mKJX3jMA .active0,#mermaid-svg-ttNIGDD4mKJX3jMA .active1,#mermaid-svg-ttNIGDD4mKJX3jMA .active2,#mermaid-svg-ttNIGDD4mKJX3jMA .active3{fill:#bfc7ff;stroke:#534fbc}#mermaid-svg-ttNIGDD4mKJX3jMA .activeText0,#mermaid-svg-ttNIGDD4mKJX3jMA .activeText1,#mermaid-svg-ttNIGDD4mKJX3jMA .activeText2,#mermaid-svg-ttNIGDD4mKJX3jMA .activeText3{fill:#000 !important}#mermaid-svg-ttNIGDD4mKJX3jMA .done0,#mermaid-svg-ttNIGDD4mKJX3jMA .done1,#mermaid-svg-ttNIGDD4mKJX3jMA .done2,#mermaid-svg-ttNIGDD4mKJX3jMA .done3{stroke:grey;fill:#d3d3d3;stroke-width:2}#mermaid-svg-ttNIGDD4mKJX3jMA .doneText0,#mermaid-svg-ttNIGDD4mKJX3jMA .doneText1,#mermaid-svg-ttNIGDD4mKJX3jMA .doneText2,#mermaid-svg-ttNIGDD4mKJX3jMA .doneText3{fill:#000 !important}#mermaid-svg-ttNIGDD4mKJX3jMA .crit0,#mermaid-svg-ttNIGDD4mKJX3jMA .crit1,#mermaid-svg-ttNIGDD4mKJX3jMA .crit2,#mermaid-svg-ttNIGDD4mKJX3jMA .crit3{stroke:#f88;fill:red;stroke-width:2}#mermaid-svg-ttNIGDD4mKJX3jMA .activeCrit0,#mermaid-svg-ttNIGDD4mKJX3jMA .activeCrit1,#mermaid-svg-ttNIGDD4mKJX3jMA .activeCrit2,#mermaid-svg-ttNIGDD4mKJX3jMA .activeCrit3{stroke:#f88;fill:#bfc7ff;stroke-width:2}#mermaid-svg-ttNIGDD4mKJX3jMA .doneCrit0,#mermaid-svg-ttNIGDD4mKJX3jMA .doneCrit1,#mermaid-svg-ttNIGDD4mKJX3jMA .doneCrit2,#mermaid-svg-ttNIGDD4mKJX3jMA .doneCrit3{stroke:#f88;fill:#d3d3d3;stroke-width:2;cursor:pointer;shape-rendering:crispEdges}#mermaid-svg-ttNIGDD4mKJX3jMA .milestone{transform:rotate(45deg) scale(0.8, 0.8)}#mermaid-svg-ttNIGDD4mKJX3jMA .milestoneText{font-style:italic}#mermaid-svg-ttNIGDD4mKJX3jMA .doneCritText0,#mermaid-svg-ttNIGDD4mKJX3jMA .doneCritText1,#mermaid-svg-ttNIGDD4mKJX3jMA .doneCritText2,#mermaid-svg-ttNIGDD4mKJX3jMA .doneCritText3{fill:#000 !important}#mermaid-svg-ttNIGDD4mKJX3jMA .activeCritText0,#mermaid-svg-ttNIGDD4mKJX3jMA .activeCritText1,#mermaid-svg-ttNIGDD4mKJX3jMA .activeCritText2,#mermaid-svg-ttNIGDD4mKJX3jMA .activeCritText3{fill:#000 !important}#mermaid-svg-ttNIGDD4mKJX3jMA .titleText{text-anchor:middle;font-size:18px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-ttNIGDD4mKJX3jMA g.classGroup text{fill:#9370db;stroke:none;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:10px}#mermaid-svg-ttNIGDD4mKJX3jMA g.classGroup text .title{font-weight:bolder}#mermaid-svg-ttNIGDD4mKJX3jMA g.clickable{cursor:pointer}#mermaid-svg-ttNIGDD4mKJX3jMA g.classGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-ttNIGDD4mKJX3jMA g.classGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-ttNIGDD4mKJX3jMA .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5}#mermaid-svg-ttNIGDD4mKJX3jMA .classLabel .label{fill:#9370db;font-size:10px}#mermaid-svg-ttNIGDD4mKJX3jMA .relation{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-ttNIGDD4mKJX3jMA .dashed-line{stroke-dasharray:3}#mermaid-svg-ttNIGDD4mKJX3jMA #compositionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-ttNIGDD4mKJX3jMA #compositionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-ttNIGDD4mKJX3jMA #aggregationStart{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-ttNIGDD4mKJX3jMA #aggregationEnd{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-ttNIGDD4mKJX3jMA #dependencyStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-ttNIGDD4mKJX3jMA #dependencyEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-ttNIGDD4mKJX3jMA #extensionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-ttNIGDD4mKJX3jMA #extensionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-ttNIGDD4mKJX3jMA .commit-id,#mermaid-svg-ttNIGDD4mKJX3jMA .commit-msg,#mermaid-svg-ttNIGDD4mKJX3jMA .branch-label{fill:lightgrey;color:lightgrey;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-ttNIGDD4mKJX3jMA .pieTitleText{text-anchor:middle;font-size:25px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-ttNIGDD4mKJX3jMA .slice{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-ttNIGDD4mKJX3jMA g.stateGroup text{fill:#9370db;stroke:none;font-size:10px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-ttNIGDD4mKJX3jMA g.stateGroup text{fill:#9370db;fill:#333;stroke:none;font-size:10px}#mermaid-svg-ttNIGDD4mKJX3jMA g.statediagram-cluster .cluster-label text{fill:#333}#mermaid-svg-ttNIGDD4mKJX3jMA g.stateGroup .state-title{font-weight:bolder;fill:#000}#mermaid-svg-ttNIGDD4mKJX3jMA g.stateGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-ttNIGDD4mKJX3jMA g.stateGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-ttNIGDD4mKJX3jMA .transition{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-ttNIGDD4mKJX3jMA .stateGroup .composit{fill:white;border-bottom:1px}#mermaid-svg-ttNIGDD4mKJX3jMA .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px}#mermaid-svg-ttNIGDD4mKJX3jMA .state-note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-ttNIGDD4mKJX3jMA .state-note text{fill:black;stroke:none;font-size:10px}#mermaid-svg-ttNIGDD4mKJX3jMA .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.7}#mermaid-svg-ttNIGDD4mKJX3jMA .edgeLabel text{fill:#333}#mermaid-svg-ttNIGDD4mKJX3jMA .stateLabel text{fill:#000;font-size:10px;font-weight:bold;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-ttNIGDD4mKJX3jMA .node circle.state-start{fill:black;stroke:black}#mermaid-svg-ttNIGDD4mKJX3jMA .node circle.state-end{fill:black;stroke:white;stroke-width:1.5}#mermaid-svg-ttNIGDD4mKJX3jMA #statediagram-barbEnd{fill:#9370db}#mermaid-svg-ttNIGDD4mKJX3jMA .statediagram-cluster rect{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-ttNIGDD4mKJX3jMA .statediagram-cluster rect.outer{rx:5px;ry:5px}#mermaid-svg-ttNIGDD4mKJX3jMA .statediagram-state .divider{stroke:#9370db}#mermaid-svg-ttNIGDD4mKJX3jMA .statediagram-state .title-state{rx:5px;ry:5px}#mermaid-svg-ttNIGDD4mKJX3jMA .statediagram-cluster.statediagram-cluster .inner{fill:white}#mermaid-svg-ttNIGDD4mKJX3jMA .statediagram-cluster.statediagram-cluster-alt .inner{fill:#e0e0e0}#mermaid-svg-ttNIGDD4mKJX3jMA .statediagram-cluster .inner{rx:0;ry:0}#mermaid-svg-ttNIGDD4mKJX3jMA .statediagram-state rect.basic{rx:5px;ry:5px}#mermaid-svg-ttNIGDD4mKJX3jMA .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#efefef}#mermaid-svg-ttNIGDD4mKJX3jMA .note-edge{stroke-dasharray:5}#mermaid-svg-ttNIGDD4mKJX3jMA .statediagram-note rect{fill:#fff5ad;stroke:#aa3;stroke-width:1px;rx:0;ry:0}:root{--mermaid-font-family: '"trebuchet ms", verdana, arial';--mermaid-font-family: "Comic Sans MS", "Comic Sans", cursive}#mermaid-svg-ttNIGDD4mKJX3jMA .error-icon{fill:#522}#mermaid-svg-ttNIGDD4mKJX3jMA .error-text{fill:#522;stroke:#522}#mermaid-svg-ttNIGDD4mKJX3jMA .edge-thickness-normal{stroke-width:2px}#mermaid-svg-ttNIGDD4mKJX3jMA .edge-thickness-thick{stroke-width:3.5px}#mermaid-svg-ttNIGDD4mKJX3jMA .edge-pattern-solid{stroke-dasharray:0}#mermaid-svg-ttNIGDD4mKJX3jMA .edge-pattern-dashed{stroke-dasharray:3}#mermaid-svg-ttNIGDD4mKJX3jMA .edge-pattern-dotted{stroke-dasharray:2}#mermaid-svg-ttNIGDD4mKJX3jMA .marker{fill:#333}#mermaid-svg-ttNIGDD4mKJX3jMA .marker.cross{stroke:#333} :root { --mermaid-font-family: "trebuchet ms", verdana, arial;} #mermaid-svg-ttNIGDD4mKJX3jMA { color: rgba(0, 0, 0, 0.75); font: ; } AbstractHMacMd5Utils HMacMd5Utils <static> <final> - opad : byte = 0x5c; <static> <final> - ipad : byte = 0x36; <static> <final> - keyLen: byte = 64; static> - generateKey(key:String) static> - generateIstr(key: byte[]) static> - generateOstr(key: byte[]) generateIstr(key:byte[]): byte[]的实现如下: /** * 生成istr数据. * @param key 签名密钥. * <p>64字节长.</p> * @param out 输出数组. * @return 结果. */ public static byte[] generateIstr(byte[] key, byte[] out){ for(int i=0;i<keyLen;i++){ out[i]=(byte)(key[i]^ipad); } return out; } generateOstr(key:byte[]): byte[]的实现如下: /** * 生成ostr数据. * @param key 签名密钥. * <p>64字节长.</p> * @param out 输出数组. * @return 结果. */ private static byte[] generateOstr(byte[] key, byte[] out) { for(int i=0;i<keyLen;i++){ out[i]=(byte)(key[i]^opad); } return out; }`在以上内容实现之后,签名过程的开发就相对简单了,流程如下:
Created with Raphaël 2.2.0 开始: 参数key为密钥文本;参数data为要加密的数据 <<参数>> key:String // 密钥文本 <<参数>> data: byte[] // 签名数据 调用generateKey(密钥文本)方法,生成密钥数据k 变量k:密钥:byte[] 变量data1:byte[] - 第一次md5计算的输入数据 计算istr,并将istr写入data1中 将参数data继续写入data1中 计算data1的md5值,并保存在变量md5One中 变量md5One:byte[] //第一次md5计算结果 变量data2 //第二次md5计算的输入数据 计算ostr并写入data2中 计算第二次md5结果res 返回值 res:byte[] 第二次md5计算的结果 结束代码如下:
/** * 数据签名. * @param key 签名密钥. * @param data 数据. * @return 签名. */ public static byte[] sign(String key, byte[] data){ byte[] k = generateKey(key); byte[] str = new byte[keyLen]; byte[] data1 = new byte[keyLen+data.length]; System.arraycopy(generateIstr(k, str), 0, data1, 0, keyLen); System.arraycopy(data, 0, data1, keyLen, data.length); byte[] md5One = Md5Utils.md5Bytes(data1); byte[] data2 = new byte[keyLen+md5One.length]; System.arraycopy(generateOstr(k, str), 0, data2, 0, keyLen); System.arraycopy(md5One, 0, data2, keyLen, md5One.length); return Md5Utils.md5Bytes(data2); }这里特别的地方是str这个变量,用来保存istr和ostr。因为这两个变量不是同时使用的,所以用了同一块内存来保存,不用分配两个字节数组。虽然对于java来讲这么操作不会有什么影响,不过还是尽量节约内存,养成良好习惯。
为了调用方便,我们增加两个常用接口:
/** * 生成签名字符串. * @param key 签名密钥. * <p>e.g.: 1234567890abcdef</p> * @param operatorID 运营商Id. * <p>e.g.: 123456789</p> * @param data 数据. * <p>e.g.: il7B0BSEjFdzpyKzfOFpvg/Se1CP802RItKYFPfSLRxJ3jf0bVl9hvYOEktPAYW2nd7S8MBcyHYyacHKbISq5iTmDzG+ivnR+SZJv3USNTYVMz9rCQVSxd0cLlqsJauko79NnwQJbzDTyLooYoIwz75qBOH2/xOMirpeEqRJrF/EQjWekJmGk9RtboXePu2rka+Xm51syBPhiXJAq0GfbfaFu9tNqs/e2Vjja/ltE1M0lqvxfXQ6da6HrThsm5id4ClZFIi0acRfrsPLRixS/IQYtksxghvJwbqOsbIsITail9Ayy4tKcogeEZiOO+4Ed264NSKmk7l3wKwJLAFjCFogBx8GE3OBz4pqcAn/ydA=</p> * @param timestamp 时间戳. * <p>e.g.: 20160729142400</p> * @param seq 序列. * <p>e.g.: 0001</p> * @return 签名. * <p>Hex格式, e.g.: 745166E8C43C84D37FFEC0F529C4136F</p> */ public static String signHex(String key, String operatorID, String data, String timestamp, String seq){ return HexUtils.bytes2HexString(sign(key, operatorID, data, timestamp, seq)); } /** * 生成签名. * @param key 签名密钥. * <p>e.g.: 1234567890abcdef</p> * @param operatorID 运营商Id. * <p>e.g.: 123456789</p> * @param data 数据. * <p>e.g.: il7B0BSEjFdzpyKzfOFpvg/Se1CP802RItKYFPfSLRxJ3jf0bVl9hvYOEktPAYW2nd7S8MBcyHYyacHKbISq5iTmDzG+ivnR+SZJv3USNTYVMz9rCQVSxd0cLlqsJauko79NnwQJbzDTyLooYoIwz75qBOH2/xOMirpeEqRJrF/EQjWekJmGk9RtboXePu2rka+Xm51syBPhiXJAq0GfbfaFu9tNqs/e2Vjja/ltE1M0lqvxfXQ6da6HrThsm5id4ClZFIi0acRfrsPLRixS/IQYtksxghvJwbqOsbIsITail9Ayy4tKcogeEZiOO+4Ed264NSKmk7l3wKwJLAFjCFogBx8GE3OBz4pqcAn/ydA=</p> * @param timestamp 时间戳. * <p>e.g.: 20160729142400</p> * @param seq 序列. * <p>e.g.: 0001</p> * @return 签名. * <p>e.g.: 745166E8C43C84D37FFEC0F529C4136F(Hex格式)</p> */ public static byte[] sign(String key, String operatorID, String data, String timestamp, String seq){ return sign(key, assembleData(operatorID,data, timestamp, seq, StandardCharsets.UTF_8)); }类图如下:
#mermaid-svg-jfOiYywF3PiufWlT .label{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);fill:#333;color:#333}#mermaid-svg-jfOiYywF3PiufWlT .label text{fill:#333}#mermaid-svg-jfOiYywF3PiufWlT .node rect,#mermaid-svg-jfOiYywF3PiufWlT .node circle,#mermaid-svg-jfOiYywF3PiufWlT .node ellipse,#mermaid-svg-jfOiYywF3PiufWlT .node polygon,#mermaid-svg-jfOiYywF3PiufWlT .node path{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-jfOiYywF3PiufWlT .node .label{text-align:center;fill:#333}#mermaid-svg-jfOiYywF3PiufWlT .node.clickable{cursor:pointer}#mermaid-svg-jfOiYywF3PiufWlT .arrowheadPath{fill:#333}#mermaid-svg-jfOiYywF3PiufWlT .edgePath .path{stroke:#333;stroke-width:1.5px}#mermaid-svg-jfOiYywF3PiufWlT .flowchart-link{stroke:#333;fill:none}#mermaid-svg-jfOiYywF3PiufWlT .edgeLabel{background-color:#e8e8e8;text-align:center}#mermaid-svg-jfOiYywF3PiufWlT .edgeLabel rect{opacity:0.9}#mermaid-svg-jfOiYywF3PiufWlT .edgeLabel span{color:#333}#mermaid-svg-jfOiYywF3PiufWlT .cluster rect{fill:#ffffde;stroke:#aa3;stroke-width:1px}#mermaid-svg-jfOiYywF3PiufWlT .cluster text{fill:#333}#mermaid-svg-jfOiYywF3PiufWlT div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:12px;background:#ffffde;border:1px solid #aa3;border-radius:2px;pointer-events:none;z-index:100}#mermaid-svg-jfOiYywF3PiufWlT .actor{stroke:#ccf;fill:#ECECFF}#mermaid-svg-jfOiYywF3PiufWlT text.actor>tspan{fill:#000;stroke:none}#mermaid-svg-jfOiYywF3PiufWlT .actor-line{stroke:grey}#mermaid-svg-jfOiYywF3PiufWlT .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333}#mermaid-svg-jfOiYywF3PiufWlT .messageLine1{stroke-width:1.5;stroke-dasharray:2, 2;stroke:#333}#mermaid-svg-jfOiYywF3PiufWlT #arrowhead path{fill:#333;stroke:#333}#mermaid-svg-jfOiYywF3PiufWlT .sequenceNumber{fill:#fff}#mermaid-svg-jfOiYywF3PiufWlT #sequencenumber{fill:#333}#mermaid-svg-jfOiYywF3PiufWlT #crosshead path{fill:#333;stroke:#333}#mermaid-svg-jfOiYywF3PiufWlT .messageText{fill:#333;stroke:#333}#mermaid-svg-jfOiYywF3PiufWlT .labelBox{stroke:#ccf;fill:#ECECFF}#mermaid-svg-jfOiYywF3PiufWlT .labelText,#mermaid-svg-jfOiYywF3PiufWlT .labelText>tspan{fill:#000;stroke:none}#mermaid-svg-jfOiYywF3PiufWlT .loopText,#mermaid-svg-jfOiYywF3PiufWlT .loopText>tspan{fill:#000;stroke:none}#mermaid-svg-jfOiYywF3PiufWlT .loopLine{stroke-width:2px;stroke-dasharray:2, 2;stroke:#ccf;fill:#ccf}#mermaid-svg-jfOiYywF3PiufWlT .note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-jfOiYywF3PiufWlT .noteText,#mermaid-svg-jfOiYywF3PiufWlT .noteText>tspan{fill:#000;stroke:none}#mermaid-svg-jfOiYywF3PiufWlT .activation0{fill:#f4f4f4;stroke:#666}#mermaid-svg-jfOiYywF3PiufWlT .activation1{fill:#f4f4f4;stroke:#666}#mermaid-svg-jfOiYywF3PiufWlT .activation2{fill:#f4f4f4;stroke:#666}#mermaid-svg-jfOiYywF3PiufWlT .mermaid-main-font{font-family:"trebuchet ms", verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-jfOiYywF3PiufWlT .section{stroke:none;opacity:0.2}#mermaid-svg-jfOiYywF3PiufWlT .section0{fill:rgba(102,102,255,0.49)}#mermaid-svg-jfOiYywF3PiufWlT .section2{fill:#fff400}#mermaid-svg-jfOiYywF3PiufWlT .section1,#mermaid-svg-jfOiYywF3PiufWlT .section3{fill:#fff;opacity:0.2}#mermaid-svg-jfOiYywF3PiufWlT .sectionTitle0{fill:#333}#mermaid-svg-jfOiYywF3PiufWlT .sectionTitle1{fill:#333}#mermaid-svg-jfOiYywF3PiufWlT .sectionTitle2{fill:#333}#mermaid-svg-jfOiYywF3PiufWlT .sectionTitle3{fill:#333}#mermaid-svg-jfOiYywF3PiufWlT .sectionTitle{text-anchor:start;font-size:11px;text-height:14px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-jfOiYywF3PiufWlT .grid .tick{stroke:#d3d3d3;opacity:0.8;shape-rendering:crispEdges}#mermaid-svg-jfOiYywF3PiufWlT .grid .tick text{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-jfOiYywF3PiufWlT .grid path{stroke-width:0}#mermaid-svg-jfOiYywF3PiufWlT .today{fill:none;stroke:red;stroke-width:2px}#mermaid-svg-jfOiYywF3PiufWlT .task{stroke-width:2}#mermaid-svg-jfOiYywF3PiufWlT .taskText{text-anchor:middle;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-jfOiYywF3PiufWlT .taskText:not([font-size]){font-size:11px}#mermaid-svg-jfOiYywF3PiufWlT .taskTextOutsideRight{fill:#000;text-anchor:start;font-size:11px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-jfOiYywF3PiufWlT .taskTextOutsideLeft{fill:#000;text-anchor:end;font-size:11px}#mermaid-svg-jfOiYywF3PiufWlT .task.clickable{cursor:pointer}#mermaid-svg-jfOiYywF3PiufWlT .taskText.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-jfOiYywF3PiufWlT .taskTextOutsideLeft.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-jfOiYywF3PiufWlT .taskTextOutsideRight.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-jfOiYywF3PiufWlT .taskText0,#mermaid-svg-jfOiYywF3PiufWlT .taskText1,#mermaid-svg-jfOiYywF3PiufWlT .taskText2,#mermaid-svg-jfOiYywF3PiufWlT .taskText3{fill:#fff}#mermaid-svg-jfOiYywF3PiufWlT .task0,#mermaid-svg-jfOiYywF3PiufWlT .task1,#mermaid-svg-jfOiYywF3PiufWlT .task2,#mermaid-svg-jfOiYywF3PiufWlT .task3{fill:#8a90dd;stroke:#534fbc}#mermaid-svg-jfOiYywF3PiufWlT .taskTextOutside0,#mermaid-svg-jfOiYywF3PiufWlT .taskTextOutside2{fill:#000}#mermaid-svg-jfOiYywF3PiufWlT .taskTextOutside1,#mermaid-svg-jfOiYywF3PiufWlT .taskTextOutside3{fill:#000}#mermaid-svg-jfOiYywF3PiufWlT .active0,#mermaid-svg-jfOiYywF3PiufWlT .active1,#mermaid-svg-jfOiYywF3PiufWlT .active2,#mermaid-svg-jfOiYywF3PiufWlT .active3{fill:#bfc7ff;stroke:#534fbc}#mermaid-svg-jfOiYywF3PiufWlT .activeText0,#mermaid-svg-jfOiYywF3PiufWlT .activeText1,#mermaid-svg-jfOiYywF3PiufWlT .activeText2,#mermaid-svg-jfOiYywF3PiufWlT .activeText3{fill:#000 !important}#mermaid-svg-jfOiYywF3PiufWlT .done0,#mermaid-svg-jfOiYywF3PiufWlT .done1,#mermaid-svg-jfOiYywF3PiufWlT .done2,#mermaid-svg-jfOiYywF3PiufWlT .done3{stroke:grey;fill:#d3d3d3;stroke-width:2}#mermaid-svg-jfOiYywF3PiufWlT .doneText0,#mermaid-svg-jfOiYywF3PiufWlT .doneText1,#mermaid-svg-jfOiYywF3PiufWlT .doneText2,#mermaid-svg-jfOiYywF3PiufWlT .doneText3{fill:#000 !important}#mermaid-svg-jfOiYywF3PiufWlT .crit0,#mermaid-svg-jfOiYywF3PiufWlT .crit1,#mermaid-svg-jfOiYywF3PiufWlT .crit2,#mermaid-svg-jfOiYywF3PiufWlT .crit3{stroke:#f88;fill:red;stroke-width:2}#mermaid-svg-jfOiYywF3PiufWlT .activeCrit0,#mermaid-svg-jfOiYywF3PiufWlT .activeCrit1,#mermaid-svg-jfOiYywF3PiufWlT .activeCrit2,#mermaid-svg-jfOiYywF3PiufWlT .activeCrit3{stroke:#f88;fill:#bfc7ff;stroke-width:2}#mermaid-svg-jfOiYywF3PiufWlT .doneCrit0,#mermaid-svg-jfOiYywF3PiufWlT .doneCrit1,#mermaid-svg-jfOiYywF3PiufWlT .doneCrit2,#mermaid-svg-jfOiYywF3PiufWlT .doneCrit3{stroke:#f88;fill:#d3d3d3;stroke-width:2;cursor:pointer;shape-rendering:crispEdges}#mermaid-svg-jfOiYywF3PiufWlT .milestone{transform:rotate(45deg) scale(0.8, 0.8)}#mermaid-svg-jfOiYywF3PiufWlT .milestoneText{font-style:italic}#mermaid-svg-jfOiYywF3PiufWlT .doneCritText0,#mermaid-svg-jfOiYywF3PiufWlT .doneCritText1,#mermaid-svg-jfOiYywF3PiufWlT .doneCritText2,#mermaid-svg-jfOiYywF3PiufWlT .doneCritText3{fill:#000 !important}#mermaid-svg-jfOiYywF3PiufWlT .activeCritText0,#mermaid-svg-jfOiYywF3PiufWlT .activeCritText1,#mermaid-svg-jfOiYywF3PiufWlT .activeCritText2,#mermaid-svg-jfOiYywF3PiufWlT .activeCritText3{fill:#000 !important}#mermaid-svg-jfOiYywF3PiufWlT .titleText{text-anchor:middle;font-size:18px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-jfOiYywF3PiufWlT g.classGroup text{fill:#9370db;stroke:none;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:10px}#mermaid-svg-jfOiYywF3PiufWlT g.classGroup text .title{font-weight:bolder}#mermaid-svg-jfOiYywF3PiufWlT g.clickable{cursor:pointer}#mermaid-svg-jfOiYywF3PiufWlT g.classGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-jfOiYywF3PiufWlT g.classGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-jfOiYywF3PiufWlT .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5}#mermaid-svg-jfOiYywF3PiufWlT .classLabel .label{fill:#9370db;font-size:10px}#mermaid-svg-jfOiYywF3PiufWlT .relation{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-jfOiYywF3PiufWlT .dashed-line{stroke-dasharray:3}#mermaid-svg-jfOiYywF3PiufWlT #compositionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-jfOiYywF3PiufWlT #compositionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-jfOiYywF3PiufWlT #aggregationStart{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-jfOiYywF3PiufWlT #aggregationEnd{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-jfOiYywF3PiufWlT #dependencyStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-jfOiYywF3PiufWlT #dependencyEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-jfOiYywF3PiufWlT #extensionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-jfOiYywF3PiufWlT #extensionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-jfOiYywF3PiufWlT .commit-id,#mermaid-svg-jfOiYywF3PiufWlT .commit-msg,#mermaid-svg-jfOiYywF3PiufWlT .branch-label{fill:lightgrey;color:lightgrey;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-jfOiYywF3PiufWlT .pieTitleText{text-anchor:middle;font-size:25px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-jfOiYywF3PiufWlT .slice{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-jfOiYywF3PiufWlT g.stateGroup text{fill:#9370db;stroke:none;font-size:10px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-jfOiYywF3PiufWlT g.stateGroup text{fill:#9370db;fill:#333;stroke:none;font-size:10px}#mermaid-svg-jfOiYywF3PiufWlT g.statediagram-cluster .cluster-label text{fill:#333}#mermaid-svg-jfOiYywF3PiufWlT g.stateGroup .state-title{font-weight:bolder;fill:#000}#mermaid-svg-jfOiYywF3PiufWlT g.stateGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-jfOiYywF3PiufWlT g.stateGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-jfOiYywF3PiufWlT .transition{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-jfOiYywF3PiufWlT .stateGroup .composit{fill:white;border-bottom:1px}#mermaid-svg-jfOiYywF3PiufWlT .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px}#mermaid-svg-jfOiYywF3PiufWlT .state-note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-jfOiYywF3PiufWlT .state-note text{fill:black;stroke:none;font-size:10px}#mermaid-svg-jfOiYywF3PiufWlT .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.7}#mermaid-svg-jfOiYywF3PiufWlT .edgeLabel text{fill:#333}#mermaid-svg-jfOiYywF3PiufWlT .stateLabel text{fill:#000;font-size:10px;font-weight:bold;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-jfOiYywF3PiufWlT .node circle.state-start{fill:black;stroke:black}#mermaid-svg-jfOiYywF3PiufWlT .node circle.state-end{fill:black;stroke:white;stroke-width:1.5}#mermaid-svg-jfOiYywF3PiufWlT #statediagram-barbEnd{fill:#9370db}#mermaid-svg-jfOiYywF3PiufWlT .statediagram-cluster rect{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-jfOiYywF3PiufWlT .statediagram-cluster rect.outer{rx:5px;ry:5px}#mermaid-svg-jfOiYywF3PiufWlT .statediagram-state .divider{stroke:#9370db}#mermaid-svg-jfOiYywF3PiufWlT .statediagram-state .title-state{rx:5px;ry:5px}#mermaid-svg-jfOiYywF3PiufWlT .statediagram-cluster.statediagram-cluster .inner{fill:white}#mermaid-svg-jfOiYywF3PiufWlT .statediagram-cluster.statediagram-cluster-alt .inner{fill:#e0e0e0}#mermaid-svg-jfOiYywF3PiufWlT .statediagram-cluster .inner{rx:0;ry:0}#mermaid-svg-jfOiYywF3PiufWlT .statediagram-state rect.basic{rx:5px;ry:5px}#mermaid-svg-jfOiYywF3PiufWlT .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#efefef}#mermaid-svg-jfOiYywF3PiufWlT .note-edge{stroke-dasharray:5}#mermaid-svg-jfOiYywF3PiufWlT .statediagram-note rect{fill:#fff5ad;stroke:#aa3;stroke-width:1px;rx:0;ry:0}:root{--mermaid-font-family: '"trebuchet ms", verdana, arial';--mermaid-font-family: "Comic Sans MS", "Comic Sans", cursive}#mermaid-svg-jfOiYywF3PiufWlT .error-icon{fill:#522}#mermaid-svg-jfOiYywF3PiufWlT .error-text{fill:#522;stroke:#522}#mermaid-svg-jfOiYywF3PiufWlT .edge-thickness-normal{stroke-width:2px}#mermaid-svg-jfOiYywF3PiufWlT .edge-thickness-thick{stroke-width:3.5px}#mermaid-svg-jfOiYywF3PiufWlT .edge-pattern-solid{stroke-dasharray:0}#mermaid-svg-jfOiYywF3PiufWlT .edge-pattern-dashed{stroke-dasharray:3}#mermaid-svg-jfOiYywF3PiufWlT .edge-pattern-dotted{stroke-dasharray:2}#mermaid-svg-jfOiYywF3PiufWlT .marker{fill:#333}#mermaid-svg-jfOiYywF3PiufWlT .marker.cross{stroke:#333} :root { --mermaid-font-family: "trebuchet ms", verdana, arial;} #mermaid-svg-jfOiYywF3PiufWlT { color: rgba(0, 0, 0, 0.75); font: ; } AbstractHMacMd5Utils HMacMd5Utils <static> <final> - opad : byte = 0x5c; <static> <final> - ipad : byte = 0x36; <static> <final> - keyLen: byte = 64; static> - generateKey(key:String) static> - generateIstr(key: byte[]) static> - generateOstr(key: byte[]) static> + sign(key: String, data: byte[]) static> + sign(key: String, operatorId:String, data:String, ts:String, seq:String) static> + signHex(key: String, operatorId:String, data:String, ts:String, seq:String)测试过程用JUnit实现,代码如下:
String want = "745166E8C43C84D37FFEC0F529C4136F"; String key = "1234567890abcdef"; String opId = "123456789"; String data = "il7B0BSEjFdzpyKzfOFpvg/Se1CP802RItKYFPfSLRxJ3jf0bVl9hvYOEktPAYW2nd7S8MBcyHYyacHKbISq5iTmDzG+ivnR+SZJv3USNTYVMz9rCQVSxd0cLlqsJauko79NnwQJbzDTyLooYoIwz75qBOH2/xOMirpeEqRJrF/EQjWekJmGk9RtboXePu2rka+Xm51syBPhiXJAq0GfbfaFu9tNqs/e2Vjja/ltE1M0lqvxfXQ6da6HrThsm5id4ClZFIi0acRfrsPLRixS/IQYtksxghvJwbqOsbIsITail9Ayy4tKcogeEZiOO+4Ed264NSKmk7l3wKwJLAFjCFogBx8GE3OBz4pqcAn/ydA="; String ts = "20160729142400"; String seq = "0001"; @Test public void test(){ TestCase.assertEquals( want, HMacMd5Utils.signHex(key, opId, data, ts, seq) ); }HMAC-MD5作为一个签名算法,当然应该有校验的过程。不过,我们这里暂时不实现校验,因为后面还有其他的工作。如果有需要,可以尝试自己实现校验过程。
JCE(Java Cryptography Extension)是Java的加密算法支持,提供了统一的加密、解密、散列、签名、密钥算法。例如,我们MD5工具类就使用了JCE的实现,如下:
/** * 数据的md5. * @param data 输入字符串. * @return md5. */ public static byte[] md5Bytes(byte[] data) { try { // 生成一个MD5加密计算摘要 MessageDigest md = MessageDigest.getInstance("MD5"); //对字符串进行加密 md.update(data); //获得加密后的数据 return md.digest(); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("没有md5这个算法"); } }我们的HMAC-MD5现在只能作为一个工具类使用。假如有另外一段代码已经用了JCE实现的其他方式签名,现在要改成HMAC-MD5,那么他需要重写算法。而如果我们能够提供JCE接口的HMAC-MD5算法。那么他就能很容易的切换算法。
密钥相关的部分分为几个内容:
KeySpec: 用来保存密钥PublicKey:加密公钥PrivateKey:解密私钥Key:密钥KeyFactory: 密钥工厂 首先HMacMd5KeySpec用于保存密钥数据。因为HMAC-MD5算法不区分公钥/私钥,所以HMacMd5Key同时实现PublicKey合PrivateKey接口。HMacMd5KeyFactory类用于生成HMacMd5相关的密钥。本类代码如下:
package net.wangds.tcec.tcec102d4.messagedigest.hmacmd5; import net.wangds.utils.Md5Utils; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.security.spec.KeySpec; /** * . * <p></p> * * @author wangds 2020/10/20 12:31. */ public class HMacMd5KeySpec implements KeySpec { private static final int LEN = 64; ByteBuffer buf = ByteBuffer.allocate(LEN); public HMacMd5KeySpec(String key){ this(key.getBytes(StandardCharsets.ISO_8859_1)); } public HMacMd5KeySpec(byte[] key){ buf.position(0); buf.limit(buf.capacity()); if(key.length>LEN){ key = Md5Utils.md5Bytes(key); } buf.put(key); buf.flip(); buf.rewind(); } public static KeySpec of(String s) { return new HMacMd5KeySpec(s); } public byte[] toBytes(){ return buf.array(); } }可以看到这个类主要是通过一个字节缓存保存密钥数据。
这个类用于实现PublicKey、PrivateKey接口,是签名、验证过程中设置密钥时所需的对象类型。
package net.wangds.tcec.tcec102d4.messagedigest.hmacmd5; import java.security.PrivateKey; import java.security.PublicKey; /** * . * <p></p> * * @author wangds 2020/10/20 12:45. */ public class HMacMd5Key implements PublicKey, PrivateKey { private final HMacMd5KeySpec spec; public HMacMd5Key(HMacMd5KeySpec spec){ this.spec = spec; } @Override public String getAlgorithm() { return "HMacMd5"; } @Override public String getFormat() { return "NOPadding"; } @Override public byte[] getEncoded() { return spec.toBytes(); } }可以看到,类中关联了一个KeySpec的属性用于保存密钥,并指定改了密钥的名称和格式。
这里需要特殊说明一下,此类的父类时KeyFactorySpi,而不是KeyFactory。JCE的工厂类会将Spi类包装为Factory类,这个过程不用我们实现,我们实现KeyFacotrySpi就可以了。
代码如下:
package net.wangds.tcec.tcec102d4.messagedigest.hmacmd5; import org.apache.commons.lang3.StringUtils; import java.security.*; import java.security.spec.InvalidKeySpecException; import java.security.spec.KeySpec; /** * . * <p></p> * * @author wangds 2020/10/20 12:34. */ public class HMacMd5KeyFactory extends KeyFactorySpi { @Override protected PublicKey engineGeneratePublic(KeySpec keySpec) throws InvalidKeySpecException { if(keySpec instanceof HMacMd5KeySpec) { return new HMacMd5Key((HMacMd5KeySpec) keySpec); } throw new InvalidKeySpecException("只支持HMacMd5KeySpec"); } @Override protected PrivateKey engineGeneratePrivate(KeySpec keySpec) throws InvalidKeySpecException { if(keySpec instanceof HMacMd5KeySpec) { return new HMacMd5Key((HMacMd5KeySpec) keySpec); } throw new InvalidKeySpecException("只支持HMacMd5KeySpec"); } @Override @SuppressWarnings({"unchecked","cast"}) protected <T extends KeySpec> T engineGetKeySpec(Key key, Class<T> keySpec) throws InvalidKeySpecException { if(StringUtils.equals(keySpec.getClass().getName(), HMacMd5KeySpec.class.getName())){ return (T)new HMacMd5KeySpec(key.getEncoded()); } throw new InvalidKeySpecException("只支持HMacMd5KeySpec"); } @Override protected Key engineTranslateKey(Key key) throws InvalidKeyException { byte[] keyData = key.getEncoded(); return new HMacMd5Key(new HMacMd5KeySpec(keyData)); } }通过上面代码,我们可以看到KeyFactorySpi这个接口主要是提供Key的生成和转换功能。我们这个代码本身不用考虑Key的那么多兼容性,毕竟应用场景只是行业内,所以在代码中看到异常情况处理基本是抛错提示。
签名算法是通过一个SignatureSpi类的子类HMacMd5SignSpi实现的。如下图:
在这个类中,通过engineInitSign()、engineSign()等方法实现了签名过程;通过engineInitVerify()和engineVerify()等方法实现类验证过程。
算法可参考之前Utils类的算法,这里只提供代码。
package net.wangds.tcec.tcec102d4.messagedigest.hmacmd5; import net.wangds.utils.HexUtils; import net.wangds.utils.Md5Utils; import org.apache.commons.lang3.StringUtils; import java.nio.ByteBuffer; import java.security.*; /** * HMAC-MD5签名算法实现. * <p></p> * * @author wangds 2020/10/20 12:51. */ public class HMacMd5SignSpi extends SignatureSpi { private static final byte opad = 0x5c; private static final byte ipad = 0x36; private static final byte KEN_LEN = 64; private Key key; private ByteBuffer buf = ByteBuffer.allocate(KEN_LEN); @Override protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException { this.key = new HMacMd5KeyFactory().engineTranslateKey(publicKey); } @Override protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException { this.key = new HMacMd5KeyFactory().engineTranslateKey(privateKey); } @Override protected void engineUpdate(byte b) { buf.rewind(); buf.put(b); buf.flip(); } @Override protected void engineUpdate(byte[] b, int off, int len) { if(buf.capacity()<len){ buf = ByteBuffer.allocate(len); } buf.rewind(); buf.put(b, off, len); buf.flip(); } @Override protected byte[] engineSign() { return calcSigin(); } private byte[] calcSigin() { byte[] data = new byte[buf.limit()]; byte[] k = key.getEncoded(); int keyLen = k.length; byte[] str = new byte[keyLen]; buf.rewind(); buf.get(data); byte[] data1 = new byte[keyLen+ data.length]; System.arraycopy(generateIstr(k, str), 0, data1, 0, keyLen); System.arraycopy(data, 0, data1, keyLen, data.length); byte[] md5One = Md5Utils.md5Bytes(data1); byte[] data2 = new byte[keyLen+md5One.length]; System.arraycopy(generateOstr(k, str), 0, data2, 0, keyLen); System.arraycopy(md5One, 0, data2, keyLen, md5One.length); return Md5Utils.md5Bytes(data2); } @Override protected boolean engineVerify(byte[] inSign) { byte[] calcSign = this.calcSigin(); String hexCalcSign = HexUtils.bytes2HexString(calcSign); String hexInSign = HexUtils.bytes2HexString(inSign); return StringUtils.equals(hexCalcSign, hexInSign); } @Override protected void engineSetParameter(String param, Object value) throws InvalidParameterException { //skip } @Override protected Object engineGetParameter(String param) throws InvalidParameterException { return null; } /** * 生成istr数据. * @param key 签名密钥. * <p>64字节长.</p> * @param out 输出数组. * @return 结果. */ public static byte[] generateIstr(byte[] key, byte[] out){ for(int i = 0; i< KEN_LEN; i++){ out[i]=(byte)(key[i]^ipad); } return out; } /** * 生成ostr数据. * @param key 签名密钥. * <p>64字节长.</p> * @param out 输出数组. * @return 结果. */ private static byte[] generateOstr(byte[] key, byte[] out) { for(int i = 0; i< KEN_LEN; i++){ out[i]=(byte)(key[i]^opad); } return out; } }Provider类是算法注册的接口。通过Provider,将算法注册到JCE中。
如下:
package net.wangds.tcec.tcec102d4.messagedigest.hmacmd5; import java.security.Provider; import java.security.Security; /** * . * <p></p> * * @author wangds 2020/10/21 10:52. */ public final class HMacMd5Provider extends Provider { static { Security.addProvider(new HMacMd5Provider()); } /** * Constructs a provider with the specified name, version number, * and information. * */ protected HMacMd5Provider() { super("HMacMd5", 1.0f, "HMAC-MD5 Provider v1.0"); put("KeyFactory.HMacMd5", "net.wangds.tcec.tcec102d4.messagedigest.hmacmd5.HMacMd5KeyFactory"); put("Signature.HMacMd5", "net.wangds.tcec.tcec102d4.messagedigest.hmacmd5.HMacMd5SignSpi"); } }Provider类在使用时有两种方式,一种是通过修改JRE配置文件实现的;另外一种就是和JDBC中数据库驱动类似的方式,通过反射接口东财注入的。
动态注入的代码片段如下:
static { try { Class.forName("net.wangds.tcec.tcec102d4.messagedigest.hmacmd5.HMacMd5Provider"); } catch (ClassNotFoundException e) { LogHelper.error(e); } }代码如下:
/** * 测试直接使用jce接口的方式调用. */ @Test public void test1(){ try { Class.forName("net.wangds.tcec.tcec102d4.messagedigest.hmacmd5.HMacMd5Provider"); } catch (ClassNotFoundException e) { LogHelper.error(e); } try { Provider prov = Security.getProvider("HMacMd5"); LogHelper.dev("prov:"+prov); KeyFactory fac = KeyFactory.getInstance("HMacMd5"); LogHelper.error("fac:"+fac); Signature sign = Signature.getInstance("HMacMd5"); LogHelper.error("sign:"+sign); try { KeySpec keySpec = HMacMd5KeySpec.of(key); PrivateKey pk = fac.generatePrivate(keySpec); sign.initSign(pk); sign.update(HMacMd5Utils.assembleData(opId, data, ts, seq, StandardCharsets.UTF_8)); byte[] res = sign.sign(); byte[] bsSign = new byte[16]; System.arraycopy(res, res.length-16, bsSign , 0,16); LogHelper.dev("sign length:"+res.length); String hex = HexUtils.bytes2HexString(bsSign); TestCase.assertEquals(want, hex); PublicKey pub = fac.generatePublic(keySpec); sign.initVerify(pub); sign.update(HMacMd5Utils.assembleData(opId, data, ts, seq, StandardCharsets.UTF_8)); TestCase.assertTrue(sign.verify(res)); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (InvalidKeySpecException | SignatureException e) { e.printStackTrace(); } } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } }注意这里也只测试了加密过程。
这个接口通过调用JCE风格的实现,便于以后算法的升级和变换。
代码如下:
package net.wangds.cnpg.cpmn.utils; import net.wangds.log.helper.LogHelper; import net.wangds.tcec.tcec102d4.messagedigest.hmacmd5.HMacMd5KeySpec; import net.wangds.utils.HexUtils; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.security.*; import java.security.spec.InvalidKeySpecException; import java.security.spec.KeySpec; import java.util.function.BiFunction; /** * . * <p></p> * * @author 王东石 2020/10/21 11:46. */ public final class HMacMd5Utils2 extends AbstractHMacMd5Utils { static { try { Class.forName("net.wangds.tcec.tcec102d4.messagedigest.hmacmd5.HMacMd5Provider"); } catch (ClassNotFoundException e) { LogHelper.error(e); } } /** * 生成签名. * @param key 签名密钥. * <p>e.g.: 1234567890abcdef</p> * @param operatorID 运营商Id. * <p>e.g.: 123456789</p> * @param data 数据. * <p>e.g.: il7B0BSEjFdzpyKzfOFpvg/Se1CP802RItKYFPfSLRxJ3jf0bVl9hvYOEktPAYW2nd7S8MBcyHYyacHKbISq5iTmDzG+ivnR+SZJv3USNTYVMz9rCQVSxd0cLlqsJauko79NnwQJbzDTyLooYoIwz75qBOH2/xOMirpeEqRJrF/EQjWekJmGk9RtboXePu2rka+Xm51syBPhiXJAq0GfbfaFu9tNqs/e2Vjja/ltE1M0lqvxfXQ6da6HrThsm5id4ClZFIi0acRfrsPLRixS/IQYtksxghvJwbqOsbIsITail9Ayy4tKcogeEZiOO+4Ed264NSKmk7l3wKwJLAFjCFogBx8GE3OBz4pqcAn/ydA=</p> * @param timestamp 时间戳. * <p>e.g.: 20160729142400</p> * @param seq 序列. * <p>e.g.: 0001</p> * @param charset 字符集. * @return 签名. * <p>e.g.: 745166E8C43C84D37FFEC0F529C4136F(Hex格式)</p> */ public static byte[] sign(String key, String operatorID, String data, String timestamp, String seq, Charset charset){ return sign(key, assembleData(operatorID,data, timestamp, seq, charset)); } /** * 生成签名Hex字符串. * @param key 签名密钥. * <p>e.g.: 1234567890abcdef</p> * @param operatorID 运营商Id. * <p>e.g.: 123456789</p> * @param data 数据. * <p>e.g.: il7B0BSEjFdzpyKzfOFpvg/Se1CP802RItKYFPfSLRxJ3jf0bVl9hvYOEktPAYW2nd7S8MBcyHYyacHKbISq5iTmDzG+ivnR+SZJv3USNTYVMz9rCQVSxd0cLlqsJauko79NnwQJbzDTyLooYoIwz75qBOH2/xOMirpeEqRJrF/EQjWekJmGk9RtboXePu2rka+Xm51syBPhiXJAq0GfbfaFu9tNqs/e2Vjja/ltE1M0lqvxfXQ6da6HrThsm5id4ClZFIi0acRfrsPLRixS/IQYtksxghvJwbqOsbIsITail9Ayy4tKcogeEZiOO+4Ed264NSKmk7l3wKwJLAFjCFogBx8GE3OBz4pqcAn/ydA=</p> * @param timestamp 时间戳. * <p>e.g.: 20160729142400</p> * @param seq 序列. * <p>e.g.: 0001</p> * @param charset 字符集. * @return 签名. * <p>根据规范要求,字母大写。e.g.: 745166E8C43C84D37FFEC0F529C4136F</p> */ public static String signHex(String key, String operatorID, String data, String timestamp, String seq, Charset charset){ return HexUtils.bytes2HexString(sign(key, operatorID, data, timestamp, seq, charset)).toUpperCase(); } /** * 生成签名. * <p>默认字符集:UTF-8</p> * @param key 签名密钥. * <p>e.g.: 1234567890abcdef</p> * @param operatorID 运营商Id. * <p>e.g.: 123456789</p> * @param data 数据. * <p>e.g.: il7B0BSEjFdzpyKzfOFpvg/Se1CP802RItKYFPfSLRxJ3jf0bVl9hvYOEktPAYW2nd7S8MBcyHYyacHKbISq5iTmDzG+ivnR+SZJv3USNTYVMz9rCQVSxd0cLlqsJauko79NnwQJbzDTyLooYoIwz75qBOH2/xOMirpeEqRJrF/EQjWekJmGk9RtboXePu2rka+Xm51syBPhiXJAq0GfbfaFu9tNqs/e2Vjja/ltE1M0lqvxfXQ6da6HrThsm5id4ClZFIi0acRfrsPLRixS/IQYtksxghvJwbqOsbIsITail9Ayy4tKcogeEZiOO+4Ed264NSKmk7l3wKwJLAFjCFogBx8GE3OBz4pqcAn/ydA=</p> * @param timestamp 时间戳. * <p>e.g.: 20160729142400</p> * @param seq 序列. * <p>e.g.: 0001</p> * @return 签名. * <p>e.g.: 745166E8C43C84D37FFEC0F529C4136F(Hex格式)</p> */ public static byte[] sign(String key, String operatorID, String data, String timestamp, String seq){ return sign(key, assembleData(operatorID,data, timestamp, seq, StandardCharsets.UTF_8)); } /** * 生成签名Hex字符串. * <p>默认字符集:UTF-8</p> * @param key 签名密钥. * <p>e.g.: 1234567890abcdef</p> * @param operatorID 运营商Id. * <p>e.g.: 123456789</p> * @param data 数据. * <p>e.g.: il7B0BSEjFdzpyKzfOFpvg/Se1CP802RItKYFPfSLRxJ3jf0bVl9hvYOEktPAYW2nd7S8MBcyHYyacHKbISq5iTmDzG+ivnR+SZJv3USNTYVMz9rCQVSxd0cLlqsJauko79NnwQJbzDTyLooYoIwz75qBOH2/xOMirpeEqRJrF/EQjWekJmGk9RtboXePu2rka+Xm51syBPhiXJAq0GfbfaFu9tNqs/e2Vjja/ltE1M0lqvxfXQ6da6HrThsm5id4ClZFIi0acRfrsPLRixS/IQYtksxghvJwbqOsbIsITail9Ayy4tKcogeEZiOO+4Ed264NSKmk7l3wKwJLAFjCFogBx8GE3OBz4pqcAn/ydA=</p> * @param timestamp 时间戳. * <p>e.g.: 20160729142400</p> * @param seq 序列. * <p>e.g.: 0001</p> * @return 签名. * <p>e.g.: 745166E8C43C84D37FFEC0F529C4136F</p> */ public static String signHex(String key, String operatorID, String data, String timestamp, String seq){ return HexUtils.bytes2HexString(sign(key, operatorID, data, timestamp, seq)).toUpperCase(); } /** * 生成签名. * @param key 签名密钥. * @param data 数据. * @return 签名. * <p>e.g.: 745166E8C43C84D37FFEC0F529C4136F(Hex格式)</p> */ public static byte[] sign(String key, byte[] data){ return withSign((fac, sign)->{ try { KeySpec keySpec = HMacMd5KeySpec.of(key); PrivateKey pk = fac.generatePrivate(keySpec); sign.initSign(pk); sign.update(data); return sign.sign(); } catch (InvalidKeyException|InvalidKeySpecException | SignatureException e) { throw new RuntimeException(e); } }); } /** * 生成签名Hex字符串. * @param key 签名密钥. * @param data 数据. * @return 签名. * <p>e.g.: 745166E8C43C84D37FFEC0F529C4136F</p> */ public static String signHex(String key, byte[] data){ return HexUtils.bytes2HexString(sign(key, data)).toUpperCase(); } /** * 验证Hex格式签名. * @param key 签名密钥. * <p>e.g.: 1234567890abcdef</p> * @param sign 签名. * <p>e.g.:745166E8C43C84D37FFEC0F529C4136F</p> * @param operatorID 运营商Id. * <p>e.g.: 123456789</p> * @param data 数据. * <p>e.g.: il7B0BSEjFdzpyKzfOFpvg/Se1CP802RItKYFPfSLRxJ3jf0bVl9hvYOEktPAYW2nd7S8MBcyHYyacHKbISq5iTmDzG+ivnR+SZJv3USNTYVMz9rCQVSxd0cLlqsJauko79NnwQJbzDTyLooYoIwz75qBOH2/xOMirpeEqRJrF/EQjWekJmGk9RtboXePu2rka+Xm51syBPhiXJAq0GfbfaFu9tNqs/e2Vjja/ltE1M0lqvxfXQ6da6HrThsm5id4ClZFIi0acRfrsPLRixS/IQYtksxghvJwbqOsbIsITail9Ayy4tKcogeEZiOO+4Ed264NSKmk7l3wKwJLAFjCFogBx8GE3OBz4pqcAn/ydA=</p> * @param timestamp 时间戳. * <p>e.g.: 20160729142400</p> * @param seq 序列. * <p>e.g.: 0001</p> * @return 验证结果. */ public static boolean verifyHex(String key, String sign, String operatorID, String data, String timestamp, String seq){ return verify(key, HexUtils.hexString2Bytes(sign), operatorID, data, timestamp, seq); } /** * 验证签名. * @param key 签名密钥. * <p>e.g.: 1234567890abcdef</p> * @param sign 签名. * <p>e.g.:745166E8C43C84D37FFEC0F529C4136F(Hex格式)</p> * @param operatorID 运营商Id. * <p>e.g.: 123456789</p> * @param data 数据. * <p>e.g.: il7B0BSEjFdzpyKzfOFpvg/Se1CP802RItKYFPfSLRxJ3jf0bVl9hvYOEktPAYW2nd7S8MBcyHYyacHKbISq5iTmDzG+ivnR+SZJv3USNTYVMz9rCQVSxd0cLlqsJauko79NnwQJbzDTyLooYoIwz75qBOH2/xOMirpeEqRJrF/EQjWekJmGk9RtboXePu2rka+Xm51syBPhiXJAq0GfbfaFu9tNqs/e2Vjja/ltE1M0lqvxfXQ6da6HrThsm5id4ClZFIi0acRfrsPLRixS/IQYtksxghvJwbqOsbIsITail9Ayy4tKcogeEZiOO+4Ed264NSKmk7l3wKwJLAFjCFogBx8GE3OBz4pqcAn/ydA=</p> * @param timestamp 时间戳. * <p>e.g.: 20160729142400</p> * @param seq 序列. * <p>e.g.: 0001</p> * @return 验证结果. */ public static boolean verify(String key, byte[] sign, String operatorID, String data, String timestamp, String seq){ return verify(key, sign, assembleData(operatorID,data, timestamp, seq, StandardCharsets.UTF_8)); } /** * 验证Hex格式签名. * @param key 签名密钥. * <p>e.g.: 1234567890abcdef</p> * @param sign 签名. * <p>e.g.:745166E8C43C84D37FFEC0F529C4136F(Hex格式)</p> * @param operatorID 运营商Id. * <p>e.g.: 123456789</p> * @param data 数据. * <p>e.g.: il7B0BSEjFdzpyKzfOFpvg/Se1CP802RItKYFPfSLRxJ3jf0bVl9hvYOEktPAYW2nd7S8MBcyHYyacHKbISq5iTmDzG+ivnR+SZJv3USNTYVMz9rCQVSxd0cLlqsJauko79NnwQJbzDTyLooYoIwz75qBOH2/xOMirpeEqRJrF/EQjWekJmGk9RtboXePu2rka+Xm51syBPhiXJAq0GfbfaFu9tNqs/e2Vjja/ltE1M0lqvxfXQ6da6HrThsm5id4ClZFIi0acRfrsPLRixS/IQYtksxghvJwbqOsbIsITail9Ayy4tKcogeEZiOO+4Ed264NSKmk7l3wKwJLAFjCFogBx8GE3OBz4pqcAn/ydA=</p> * @param timestamp 时间戳. * <p>e.g.: 20160729142400</p> * @param seq 序列. * <p>e.g.: 0001</p> * @param charset 字符集. * * @return 验证结果. */ public static boolean verifyHex(String key, String sign, String operatorID, String data, String timestamp, String seq, Charset charset){ return verify(key, HexUtils.hexString2Bytes(sign), operatorID,data, timestamp, seq, charset); } /** * 验证签名. * @param key 签名密钥. * <p>e.g.: 1234567890abcdef</p> * @param sign 签名. * <p>e.g.:745166E8C43C84D37FFEC0F529C4136F(Hex格式)</p> * @param operatorID 运营商Id. * <p>e.g.: 123456789</p> * @param data 数据. * <p>e.g.: il7B0BSEjFdzpyKzfOFpvg/Se1CP802RItKYFPfSLRxJ3jf0bVl9hvYOEktPAYW2nd7S8MBcyHYyacHKbISq5iTmDzG+ivnR+SZJv3USNTYVMz9rCQVSxd0cLlqsJauko79NnwQJbzDTyLooYoIwz75qBOH2/xOMirpeEqRJrF/EQjWekJmGk9RtboXePu2rka+Xm51syBPhiXJAq0GfbfaFu9tNqs/e2Vjja/ltE1M0lqvxfXQ6da6HrThsm5id4ClZFIi0acRfrsPLRixS/IQYtksxghvJwbqOsbIsITail9Ayy4tKcogeEZiOO+4Ed264NSKmk7l3wKwJLAFjCFogBx8GE3OBz4pqcAn/ydA=</p> * @param timestamp 时间戳. * <p>e.g.: 20160729142400</p> * @param seq 序列. * <p>e.g.: 0001</p> * @param charset 字符集. * @return 验证结果. */ public static boolean verify(String key, byte[] sign, String operatorID, String data, String timestamp, String seq, Charset charset){ return verify(key, sign, assembleData(operatorID,data, timestamp, seq, charset)); } /** * 验证数据包. * @param key 密钥. * @param signData 签名. * @param data 数据. * @return 验证是否成功. */ public static boolean verify(String key, byte[] signData, byte[] data){ return withSign((fac, sign)->{ try { KeySpec keySpec = HMacMd5KeySpec.of(key); PublicKey pub = fac.generatePublic(keySpec); sign.initVerify(pub); sign.update(data); return sign.verify(signData); } catch (InvalidKeyException|InvalidKeySpecException | SignatureException e) { throw new RuntimeException(e); } }); } /** * 验证数据包. * @param key 密钥. * @param signData 签名. * @param data 数据. * @return 验证是否成功. */ public static boolean verifyHex(String key, String signData, byte[] data){ return verify(key, HexUtils.hexString2Bytes(signData), data); } /** * 在密钥工厂和签名算法确定的条件下,执行回到函数. * @param callback 要执行的回调函数. * @param <T> 回调函数返回值类型. * @return 回调函数返回值. */ private static <T> T withSign(BiFunction<KeyFactory, Signature, T> callback){ try { KeyFactory fac = KeyFactory.getInstance("HMacMd5"); Signature sign = Signature.getInstance("HMacMd5"); return callback.apply(fac, sign); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } } }测试过程代码如下:
@Test public void test3(){ TestCase.assertEquals(want, HMacMd5Utils2.signHex(key, opId, data, ts, seq)); TestCase.assertEquals(want, HMacMd5Utils2.signHex(key, opId, data, ts, seq, StandardCharsets.UTF_8)); TestCase.assertTrue(HMacMd5Utils2.verifyHex(key, want, opId, data, ts, seq)); TestCase.assertTrue(HMacMd5Utils2.verifyHex(key, want, opId, data, ts, seq, StandardCharsets.UTF_8)); }END