密评涉及密改的运维端经常会有动态令牌做身份鉴别,对应的标准为:
GM/T 0021-2012《动态口令密码应用技术规范》
GB/T 38556-2020《信息安全技术 动态口令密码应用技术规范》
写了最基础的SM3/SM4 两种国标口令生成,数据全部验证通过,给小伙伴们抛砖引玉。
package org.liuy.pki;
import org.bouncycastle.util.encoders.Hex;
/**
* 国密动态令牌实现
* 参考标准:《GB/38556-2020 信息安全技术动态口令密码应用技术规范》
* 参考网址:
* https://www.wangan.com/docs/2484
*
*
* @author liuy
*
*/
public class GMOTP {
/**
* 左补位
* @param c 补位的字符
* @param length 需要的长度
* @param target
* @return
*/
public static String flushLeft(String c, int length, String target) {
String cs = "";
if(target.length() < length){
for(int i = 0; i < length - target.length(); i++){
cs = c + cs;
}
target = cs + target;
}
return target;
}
/**
* 字节数组转int 大端模式
*/
public static int byteArrayToIntBigEndian(byte[] bytes) {
// byte数组中序号大的在右边
// long values = 0;
// for (int i = 0; i < 4; i++) {
// values <<= Byte.SIZE;
// values |= (bytes[i] & 0xff);
// }
int res = ((bytes[0] & 0xff) << 24) | ((bytes[1] & 0xff) << 16) |
((bytes[2] & 0xff) << 8) | (bytes[3] & 0xff);
return res;
}
/**
* 截位运算
* OD = Truncate(S)
* @param sm3Hash
* @return
*/
private static String truncate(byte[] sm3Hash){
//每组4个字节,32字节长度总共分为8组
int g=sm3Hash.length/4;
byte[] sByte = new byte[4];
long OD_sum=0;
for(int i=0;i<g;i++){
System.arraycopy(sm3Hash,i*4,sByte,0,4);
long bI=byteArrayToIntBigEndian(sByte);
OD_sum+=bI;
}
//转为无符号整形
OD_sum=OD_sum & 0xFFFFFFFFL;
long mod=(long) Math.pow(2,32);
long OD=OD_sum % mod;
String ODStr=String.valueOf(OD);
return ODStr.substring(ODStr.length()-6,ODStr.length());
}
/**
* 计算 ID
* @param K
* @param T
* @param C
* @param Q
* @return
*/
private static String genID(int T,int C,String Q){
//T : 时间因子 (8字节,整型)
String T_HEX=Integer.toHexString(T);
T_HEX=flushLeft("0",16,T_HEX);
//C : 事件因子 (4字节, 整型)
String C_HEX=Integer.toHexString(C);
C_HEX=flushLeft("0",8,C_HEX);
//Q : 挑战因子 (最小为4字节,使用ASCII码表示)
String Q_Hex="";
for(int i=0;i<Q.length();i++)
{
String hexStr = Integer.toHexString(Q.charAt(i));
Q_Hex+=hexStr;
}
//ID= {T|C|Q}
String ID=T_HEX+ C_HEX+Q_Hex;
return ID;
}
/**
* 生成SM3动态口令
* @param K 种子密钥 (不少于128位, 16进制)
* @param T 时间因子 (8字节,整型)
* @param C 事件因子 (4字节, 整型)
* @param Q 挑战因子 (最小为4字节,使用ASCII码表示)
* @return
*/
public static String genSM3GMOTP(String K,int T,int C,String Q){
//ID= {T|C|Q}
String ID=genID(T,C,Q);
//SM3 S = F(K|ID)
String S=K+ID;
byte[] srcByte=Hex.decode(S);
byte[] sm3Hash =SM3Tools.encrypt(srcByte);
return truncate(sm3Hash);
}
/**
* 生成SM4动态口令
* @param K 种子密钥 (不少于128位, 16进制)
* @param T 时间因子 (8字节,整型)
* @param C 事件因子 (4字节, 整型)
* @param Q 挑战因子 (最小为4字节,使用ASCII码表示)
* @return
*/
public static String genSM4GMOTP(String K,int T,int C,String Q) {
//ID= {T|C|Q}
String ID=genID(T,C,Q);
//SM4 S = F(K,ID)
String encDataHex=SM4Tools.encryptECB(K, ID);
byte[] sByte = new byte[16];
System.arraycopy(Hex.decode(encDataHex),0,sByte,0,16);
return truncate(sByte);
}
}
测试类:
package test;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
import org.liuy.pki.GMOTP;
public class TESTOTP {
public static void testSM3OTP() throws FileNotFoundException {
//动态令牌相关取值
String K;
int T;
int C;
String Q ;
String P;
File myObj = new File("sm3VF.txt");
Scanner myReader = new Scanner(myObj);
while (myReader.hasNextLine()) {
String data = myReader.nextLine();
String[] sList=data.split("\\s+");
K=sList[0];
T=Integer.valueOf(sList[1]);
C=Integer.valueOf(sList[2]);
Q=sList[3];
P=sList[4];
//System.out.println(K+":"+T+":"+C+":"+Q+":"+P);
String otp=GMOTP.genSM3GMOTP(K, T, C, Q);
if(!P.endsWith(otp))
{
System.out.println(sList[5]+" 错误口令:"+otp);
return;
}
}
myReader.close();
System.out.println("SM3国标数据验证正确");
}
public static void testSM4OTP() throws Exception {
//动态令牌相关取值
String K;
int T;
int C;
String Q ;
String P;
File myObj = new File("sm4VF.txt");
Scanner myReader = new Scanner(myObj);
while (myReader.hasNextLine()) {
String data = myReader.nextLine();
String[] sList=data.split("\\s+");
K=sList[0];
T=Integer.valueOf(sList[1]);
C=Integer.valueOf(sList[2]);
Q=sList[3];
P=sList[4];
String otp=GMOTP.genSM4GMOTP(K, T, C, Q);
if(!P.endsWith(otp))
{
System.out.println(sList[5]+" 错误口令:"+otp);
return;
}
}
myReader.close();
System.out.println("SM4国标数据验证正确");
}
public static void main(String[] args) throws Exception {
testSM3OTP();
testSM4OTP();
}
}
国标SM3验证数据(sm3VF.txt):
1234567890abcdef1234567890abcdef 1313998979 1234 5678 814095
1234567890abcdefabcdef1234567890 1313998995 5621 3698 959691
1234567890abcdef0987654321abcdef 1313999014 5621 3698 063014
1234567890abcdefabcdef0987654321 1313999047 2053 6984 302593
87524138025adcfe2584376195abfedc 1313999067 2058 3024 657337
87524138025adcfeabfedc2584376195 1313999098 2056 2018 345821
adcfe87524138025abfedc2584376195 1313999131 2358 1036 629660
58ade3698fe280cb6925010dd236caef 1313999155 2547 2058 479821
58ade365201d80cbdd236caef6925010 1313999174 6031 2058 893826
65201d80cb58ade3dd236caef6925010 1313999189 6580 1047 607614
国标SM4验证数据(sm4VF.txt):
1234567890abcdef1234567890abcdef 1340783053 1234 5678 446720
1234567890abcdefabcdef1234567890 1340783416 5621 3698 049845
1234567890abcdef0987654321abcdef 1340783476 2584 2105 717777
87524138025adcfeabfedc2584376195 1340783509 2053 6984 037000
87524138025adcfe2584376195abfedc 1340783588 2058 3024 502206
1234567890abcdefabcdef0987654321 1340783624 2056 2018 692843
adcfe87524138025abfedc2584376195 1340783652 2358 1036 902690
58ade3698fe280cb6925010dd236caef 1340783729 2547 2058 499811
58ade365201d80cbdd236caef6925010 1340783771 6031 2058 565180
65201d80cb58ade3dd236caef6925010 1340783815 6580 1047 724654
参考文献:
https://www.wangan.com/docs/2484
https://www.jianshu.com/p/e1722328695e