SM3密码杂凑算法是中国国家密码管理局2010年公布的中国商用密码杂凑算法标准。具体算法标准原始文本参见参考文献[1]。该算法于2012年发布为密码行业标准(GM/T 0004-2012),2016年发布为国家密码杂凑算法标准(GB/T 32905-2016)。
SM3适用于商用密码应用中的数字签名和验证,是在[SHA-256]基础上改进实现的一种算法,其安全性和SHA-256相当。SM3和MD5的迭代过程类似,也采用Merkle-Damgard结构。消息分组长度为512位,摘要值长度为256位。
整个算法的执行过程可以概括成四个步骤:消息填充、消息扩展、迭代压缩、输出结果。
消息填充
SM3的消息扩展步骤是以512位的数据分组作为输入的。因此,我们需要在一开始就把数据长度填充至512位的倍数。数据填充规则和MD5一样,具体步骤如下:
1、先填充一个“1”,后面加上k个“0”。其中k是满足(n+1+k) mod 512 = 448的最小正整数。
2、追加64位的数据长度(bit为单位,大端序存放1。观察算法标准原文附录A运算示例可以推知。)
消息扩展
SM3的迭代压缩步骤没有直接使用数据分组进行运算,而是使用这个步骤产生的132个消息字。(一个消息字的长度为32位/4个字节/8个16j进制数字)概括来说,先将一个512位数据分组划分为16个消息字,并且作为生成的132个消息字的前16个。再用这16个消息字递推生成剩余的116个消息字。
迭代压缩
SM3的迭代过程和MD5类似,也是Merkle-Damgard结构。但和MD5不同的是,SM3使用消息扩展得到的消息字进行运算。这个迭代过程可以用这幅图表示:
初值IV被放在A、B、C、D、E、F、G、H八个32位变量中,其具体数值参见参考文献[1]。整个算法中最核心、也最复杂的地方就在于压缩函数。压缩函数将这八个变量进行64轮相同的计算,一轮的计算过程如下图所示:
图中不同的数据流向用不同颜色的箭头表示。
最后,再将计算完成的A、B、C、D、E、F、G、H和原来的A、B、C、D、E、F、G、H分别进行异或,就是压缩函数的输出。这个输出再作为下一次调用压缩函数时的初值。依次类推,直到用完最后一组132个消息字为止。
输出结果
将得到的A、B、C、D、E、F、G、H八个变量拼接输出,就是SM3算法的输出。
JAVA实现SM3加密与校验
- 导入Maven依赖
<!-- https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on --><dependency><groupId>org.bouncycastle</groupId><artifactId>bcprov-jdk15on</artifactId><version>1.70</version></dependency>
- SM3Utils
privatestaticfinalString ENCODING ="UTF-8";/**
* 加密
*
* @param src 明文
* @param key 密钥
* @return
* @throws Exception
*/publicstaticStringencrypt(String src,String key)throwsException{returnByteUtils.toHexString(getEncryptByKey(src, key));}/**
* SM3加密方式之: 根据自定义密钥进行加密,返回加密后长度为32位的16进制字符串
*
* @param src 源数据
* @param key 密钥
* @return 加密后长度为32位的16进制字符串
* @throws Exception
*/publicstaticbyte[]getEncryptByKey(String src,String key)throwsException{byte[] srcByte = src.getBytes(ENCODING);byte[] keyByte = key.getBytes(ENCODING);KeyParameter keyParameter =newKeyParameter(keyByte);SM3Digest sm3 =newSM3Digest();HMac hMac =newHMac(sm3);
hMac.init(keyParameter);
hMac.update(srcByte,0, srcByte.length);byte[] result =newbyte[hMac.getMacSize()];
hMac.doFinal(result,0);return result;}/**
* 利用源数据+密钥校验与密文是否一致
*
* @param src 源数据
* @param key 密钥
* @param sm3HexStr 密文
* @return
* @throws Exception
*/publicstaticbooleanverify(String src,String key,String sm3HexStr)throwsException{byte[] sm3HashCode =ByteUtils.fromHexString(sm3HexStr);byte[] newHashCode =getEncryptByKey(src, key);returnArrays.equals(newHashCode, sm3HashCode);}/**
* SM3加密方式之:不提供密钥的方式 SM3加密,返回加密后长度为64位的16进制字符串
*
* @param src 明文
* @return 加密后长度为64位的16进制字符串
*/publicstaticStringencrypt(String src){returnByteUtils.toHexString(getEncryptBySrcByte(src.getBytes()));}/**
* 返回长度为32位的加密后的byte数组
*
* @param srcByte
* @return
*/publicstaticbyte[]getEncryptBySrcByte(byte[] srcByte){SM3Digest sm3 =newSM3Digest();
sm3.update(srcByte,0, srcByte.length);byte[] encryptByte =newbyte[sm3.getDigestSize()];
sm3.doFinal(encryptByte,0);return encryptByte;}/**
* 校验源数据与加密数据是否一致
*
* @param src 源数据
* @param sm3HexStr 16进制的加密数据
* @return
* @throws Exception
*/publicstaticbooleanverify(String src,String sm3HexStr)throwsException{byte[] sm3HashCode =ByteUtils.fromHexString(sm3HexStr);byte[] newHashCode =getEncryptBySrcByte(src.getBytes(ENCODING));returnArrays.equals(newHashCode, sm3HashCode);}publicstaticvoidmain(String[] args)throwsException{String srcStr ="今天天气很晴朗";String key ="zjqzjq";// ******************************自定义密钥加密及校验*****************************************String hexStrByKey =SM3Utils.encrypt(srcStr, key);System.out.println("带密钥加密后的密文:"+ hexStrByKey);System.out.println("明文(带密钥)与密文校验结果:"+SM3Utils.verify(srcStr, key, hexStrByKey));// ******************************无密钥的加密及校验******************************************String hexStrNoKey =SM3Utils.encrypt(srcStr);System.out.println("不带密钥加密后的密文:"+ hexStrNoKey);System.out.println("明文(不带密钥)与密文校验结果:"+SM3Utils.verify(srcStr, hexStrNoKey));}
注:SM3算法的实现
importjava.io.ByteArrayOutputStream;importjava.io.File;importjava.io.FileInputStream;importjava.io.FileNotFoundException;importjava.io.IOException;importjava.io.InputStream;importjava.math.BigInteger;importjava.util.Arrays;importltd.snowland.utils.NumberTool;importltd.snowland.utils.StreamTool;/**
* SM3杂凑算法实现
*
* @author Potato
*
*/publicclass SM3 {privatestaticchar[] hexDigits ={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};privatestaticfinalString ivHexStr ="7380166f 4914b2b9 172442d7 da8a0600 a96f30bc 163138aa e38dee4d b0fb0e4e";privatestaticfinalBigInteger IV =newBigInteger(ivHexStr.replaceAll(" ",""),16);privatestaticfinalIntegerTj15=Integer.valueOf("79cc4519",16);privatestaticfinalIntegerTj63=Integer.valueOf("7a879d8a",16);privatestaticfinalbyte[]FirstPadding={(byte)0x80};privatestaticfinalbyte[]ZeroPadding={(byte)0x00};privatestaticintT(int j){if(j >=0&& j <=15){returnTj15.intValue();}elseif(j >=16&& j <=63){returnTj63.intValue();}else{thrownewRuntimeException("data invalid");}}privatestaticIntegerFF(Integer x,Integer y,Integer z,int j){if(j >=0&& j <=15){returnInteger.valueOf(x.intValue()^ y.intValue()^ z.intValue());}elseif(j >=16&& j <=63){returnInteger.valueOf((x.intValue()& y.intValue())|(x.intValue()& z.intValue())|(y.intValue()& z.intValue()));}else{thrownewRuntimeException("data invalid");}}privatestaticIntegerGG(Integer x,Integer y,Integer z,int j){if(j >=0&& j <=15){returnInteger.valueOf(x.intValue()^ y.intValue()^ z.intValue());}elseif(j >=16&& j <=63){returnInteger.valueOf((x.intValue()& y.intValue())|(~x.intValue()& z.intValue()));}else{thrownewRuntimeException("data invalid");}}privatestaticIntegerP0(Integer x){returnInteger.valueOf(x.intValue()^Integer.rotateLeft(x.intValue(),9)^Integer.rotateLeft(x.intValue(),17));}privatestaticIntegerP1(Integer x){returnInteger.valueOf(x.intValue()^Integer.rotateLeft(x.intValue(),15)^Integer.rotateLeft(x.intValue(),23));}privatestaticbyte[]padding(byte[] source)throwsIOException{if(source.length >=0x2000000000000000l){thrownewRuntimeException("src data invalid.");}long l = source.length *8;long k =448-(l +1)%512;if(k <0){
k = k +512;}ByteArrayOutputStream baos =newByteArrayOutputStream();
baos.write(source);
baos.write(FirstPadding);long i = k -7;while(i >0){
baos.write(ZeroPadding);
i -=8;}
baos.write(long2bytes(l));return baos.toByteArray();}privatestaticbyte[]long2bytes(long l){byte[] bytes =newbyte[8];for(int i =0; i <8; i++){
bytes[i]=(byte)(l >>>((7- i)*8));}return bytes;}publicstaticbyte[]hash(byte[] source)throwsIOException{byte[] m1 =padding(source);int n = m1.length /(512/8);byte[] b;byte[] vi = IV.toByteArray();byte[] vi1 =null;for(int i =0; i < n; i++){
b =Arrays.copyOfRange(m1, i *64,(i +1)*64);
vi1 =CF(vi, b);
vi = vi1;}return vi1;}publicstaticbyte[]hash(String source)throwsException{returnhash(source.getBytes());}publicstaticbyte[]hash(File file)throwsException{if(file.exists()){InputStream inStream =newFileInputStream(file);returnhash(StreamTool.readInputStream2ByteArray(inStream));}else{thrownewFileNotFoundException();}}privatestaticbyte[]CF(byte[] vi,byte[] bi)throwsIOException{int a, b, c, d, e, f, g, h;
a =toInteger(vi,0);
b =toInteger(vi,1);
c =toInteger(vi,2);
d =toInteger(vi,3);
e =toInteger(vi,4);
f =toInteger(vi,5);
g =toInteger(vi,6);
h =toInteger(vi,7);int[] w =newint[68];int[] w1 =newint[64];for(int i =0; i <16; i++){
w[i]=toInteger(bi, i);}for(int j =16; j <68; j++){
w[j]=P1(w[j -16]^ w[j -9]^Integer.rotateLeft(w[j -3],15))^Integer.rotateLeft(w[j -13],7)^ w[j -6];}for(int j =0; j <64; j++){
w1[j]= w[j]^ w[j +4];}int ss1, ss2, tt1, tt2;for(int j =0; j <64; j++){
ss1 =Integer.rotateLeft(Integer.rotateLeft(a,12)+ e +Integer.rotateLeft(T(j), j),7);
ss2 = ss1 ^Integer.rotateLeft(a,12);
tt1 =FF(a, b, c, j)+ d + ss2 + w1[j];
tt2 =GG(e, f, g, j)+ h + ss1 + w[j];
d = c;
c =Integer.rotateLeft(b,9);
b = a;
a = tt1;
h = g;
g =Integer.rotateLeft(f,19);
f = e;
e =P0(tt2);}byte[] v =toByteArray(a, b, c, d, e, f, g, h);for(int i =0; i < v.length; i++){
v[i]=(byte)(v[i]^ vi[i]);}return v;}privatestaticinttoInteger(byte[] source,int index){StringBuilder valueStr =newStringBuilder("");for(int i =0; i <4; i++){
valueStr.append(hexDigits[(byte)((source[index *4+ i]&0xF0)>>4)]);
valueStr.append(hexDigits[(byte)(source[index *4+ i]&0x0F)]);}returnLong.valueOf(valueStr.toString(),16).intValue();}privatestaticbyte[]toByteArray(int a,int b,int c,int d,int e,int f,int g,int h)throwsIOException{ByteArrayOutputStream baos =newByteArrayOutputStream(32);
baos.write(toByteArray(a));
baos.write(toByteArray(b));
baos.write(toByteArray(c));
baos.write(toByteArray(d));
baos.write(toByteArray(e));
baos.write(toByteArray(f));
baos.write(toByteArray(g));
baos.write(toByteArray(h));return baos.toByteArray();}publicstaticbyte[]toByteArray(int i){byte[] byteArray =newbyte[4];
byteArray[0]=(byte)(i >>>24);
byteArray[1]=(byte)((i &0xFFFFFF)>>>16);
byteArray[2]=(byte)((i &0xFFFF)>>>8);
byteArray[3]=(byte)(i &0xFF);return byteArray;}privatestaticStringbyteToHexString(byte b){int n = b;if(n <0)
n =256+ n;int d1 = n /16;int d2 = n %16;return""+ hexDigits[d1]+ hexDigits[d2];}publicstaticStringbyteArrayToHexString(byte[] b){StringBuffer resultSb =newStringBuffer();for(int i =0; i < b.length; i++){
resultSb.append(byteToHexString(b[i]));}return resultSb.toString();}publicstaticStringhashHex(byte[] b){try{return SM3.byteArrayToHexString(SM3.hash(b));}catch(IOException e){
e.printStackTrace();}returnnull;}publicstaticStringhashHex(String str){returnhashHex(str.getBytes());}publicstaticvoidmain(String[] args)throwsIOException{System.out.println(SM3.byteArrayToHexString(SM3.hash("abc".getBytes())));}}
版权归原作者 猿猴一号(lxd) 所有, 如有侵权,请联系我们删除。