SM3加密算法

sm3用于替代MD5/SHA-1/SHA-2等国际算法,适用于数字签名和验证、消息认证码的生成与验证以及随机数的生成,可以满足电子认证服务系统等应用需求,于2010年12月17日发布。它是在SHA-256基础上改进实现的一种算法,采用Merkle-Damgard结构,消息分组长度为512bit,输出的摘要值长度为256bit。
SM3主要用于数据库密码加密。

这里使用SM3加盐完成存储密码功能

  1. 新增用户时,将明文密码通过SM3加密后再加盐(随机生成)后形成密文存储在数据库中,同时我们也要将盐存在用户表的一个字段中(用于登录时密码的校验比对)。用户修改时不需要将盐进行更新存储。
  2. 登录认证时,在前端通过一个固定的公钥将账号和密码使用sm2进行加密后再传输到后端,后端通过一个固定的私钥将账号及密码进行解密,解密后的密码通过sm3加盐进行加密后与数据库中的密文进行比对认证。(这里使用RSA什么的都可以,认证方面的代码就不给出了,这边主要是关于sm3的)

导入相关依赖

<dependency>
     <groupId>org.bouncycastle</groupId>
     <artifactId>bcprov-jdk15on</artifactId>
     <version>1.57</version>
 </dependency>
 <dependency>
     <groupId>org.bouncycastle</groupId>
     <artifactId>bcprov-ext-jdk15on</artifactId>
     <version>1.57</version>
 </dependency>

新建SM3加密类(Sm3crypto)

import cn.stylefeng.guns.core.util.Sm3Utils;
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.util.encoders.Hex;
 
import java.io.IOException;
import java.security.SecureRandom;
import java.util.Base64;
 
//SM3加密
public class Sm3crypto {
 
 
    public static byte[] getSalt(){
        /*
         * 随机生成128位的随机数
         */
        SecureRandom random = new SecureRandom();
        byte bytes1[] = new byte[16];
        random.nextBytes(bytes1);
        return bytes1;
    }
 
    public static byte[] pwdSaltedHashValue(byte[] bytes1, String passwdString) {
 
        //sm3加密密码
        try {
            passwdString = Sm3Utils.encodeSM3(passwdString);
        } catch (IOException e) {
            e.printStackTrace();
        }
 
        /*
         * 加盐:即随机数和口令组合
         */
        byte passwdbyte[]= arraycat(bytes1,passwdString.getBytes());
        //SM3计算
        SM3Digest mdDigest=new SM3Digest();
        mdDigest.update(passwdbyte,0,passwdbyte.length);
        byte[] result=new byte[mdDigest.getDigestSize()];
        mdDigest.doFinal(result, 0);
        return result;
    }
    /*
     * 拼接buf1和buf2数组
     */
    public static byte[] arraycat(byte[] buf1,byte[] buf2)
    {
 
        byte[] bufret=null;
        int len1=0;
        int len2=0;
        if(buf1!=null)
            len1=buf1.length;
        if(buf2!=null)
            len2=buf2.length;
        if(len1+len2>0)
            bufret=new byte[len1+len2];
        if(len1>0)
            System.arraycopy(buf1,0,bufret,0,len1);
        if(len2>0)
            System.arraycopy(buf2,0,bufret,len1,len2);
        return bufret;
    }
}

新建国密SM3工具类(Sm3Utils)

import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.util.Arrays;
 
import java.io.ByteArrayOutputStream;
import java.io.IOException;
 
/**
 * 国密SM3,消息摘要(MD5)
 *
 * @author Luke
 */
@Slf4j
public class Sm3Utils {
 
    private static char[] chars = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
    public static final byte[] IV = {0x73, (byte) 0x80, 0x16, 0x6f, 0x49, 0x14, (byte) 0xb2, (byte) 0xb9, 0x17, 0x24, 0x42,
            (byte) 0xd7, (byte) 0xda, (byte) 0x8a, 0x06, 0x00, (byte) 0xa9, 0x6f, 0x30, (byte) 0xbc, (byte) 0x16, 0x31,
            0x38, (byte) 0xaa, (byte) 0xe3, (byte) 0x8d, (byte) 0xee, 0x4d, (byte) 0xb0, (byte) 0xfb, 0x0e, 0x4e};
    private static final Integer TJ_15 = Integer.valueOf("79cc4519", 16);
    private static final Integer TJ_63 = Integer.valueOf("7a879d8a", 16);
    private static final byte[] FirstPadding = {(byte) 0x80};
    private static final byte[] ZeroPadding = {(byte) 0x00};
 
    private static int T(int j) {
        if (j >= 0 && j <= 15) {
            return TJ_15.intValue();
        } else if (j >= 16 && j <= 63) {
            return TJ_63.intValue();
        } else {
            throw new RuntimeException("data invalid");
        }
    }
 
    private static Integer FF(Integer x, Integer y, Integer z, int j) {
        if (j >= 0 && j <= 15) {
            return Integer.valueOf(x.intValue() ^ y.intValue() ^ z.intValue());
        } else if (j >= 16 && j <= 63) {
            return Integer.valueOf(
                    (x.intValue() & y.intValue()) | (x.intValue() & z.intValue()) | (y.intValue() & z.intValue()));
        } else {
            throw new RuntimeException("data invalid");
        }
    }
 
    private static Integer GG(Integer x, Integer y, Integer z, int j) {
        if (j >= 0 && j <= 15) {
            return Integer.valueOf(x.intValue() ^ y.intValue() ^ z.intValue());
        } else if (j >= 16 && j <= 63) {
            return Integer.valueOf((x.intValue() & y.intValue()) | (~x.intValue() & z.intValue()));
        } else {
            throw new RuntimeException("data invalid");
        }
    }
 
    private static Integer P0(Integer x) {
        return Integer
                .valueOf(x.intValue() ^ Integer.rotateLeft(x.intValue(), 9) ^ Integer.rotateLeft(x.intValue(), 17));
    }
 
    private static Integer P1(Integer x) {
        return Integer.valueOf(x.intValue() ^ Integer.rotateLeft(x.intValue(), 15) ^ Integer.rotateLeft(x.intValue(), 23));
    }
 
    private static byte[] padding(byte[] source) throws IOException {
        if (source.length >= 0x2000000000000000L) {
            throw new RuntimeException("src data invalid.");
        }
        long l = source.length * 8;
        long k = 448 - (l + 1) % 512;
        if (k < 0) {
            k = k + 512;
        }
        if (log.isDebugEnabled()) {
            log.debug("k = " + k);
        }
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();) {
            baos.write(source);
            baos.write(FirstPadding);
            long i = k - 7;
            while (i > 0) {
                baos.write(ZeroPadding);
                i -= 8;
            }
            baos.write(long2bytes(l));
            if (log.isDebugEnabled()) {
                log.debug("paded size = " + baos.size());
            }
            return baos.toByteArray();
        }
    }
 
    private static byte[] long2bytes(long l) {
        byte[] bytes = new byte[8];
        for (int i = 0; i < 8; i++) {
            bytes[i] = (byte) (l >>> ((7 - i) * 8));
        }
        return bytes;
    }
 
    public static String encodeSM3(String source) throws IOException {
        byte[] b = encodeSM3(source.getBytes());
        return byteToHexString(b);
    }
 
    public static byte[] encodeSM3(byte[] source) throws IOException {
        byte[] m1 = padding(source);
        int n = m1.length / (512 / 8);
        if (log.isDebugEnabled()) {
            log.debug("n = " + n);
        }
        byte[] b;
        byte[] vi = IV.clone();
        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;
    }
 
    private static byte[] CF(byte[] vi, byte[] bi) throws IOException {
        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 = new int[68];
        int[] w1 = new int[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;
    }
 
    private static int toInteger(byte[] source, int index) {
        StringBuilder valueStr = new StringBuilder("");
        for (int i = 0; i < 4; i++) {
            valueStr.append(chars[(byte) ((source[index * 4 + i] & 0xF0) >> 4)]);
            valueStr.append(chars[(byte) (source[index * 4 + i] & 0x0F)]);
        }
        return Long.valueOf(valueStr.toString(), 16).intValue();
 
    }
 
    private static byte[] toByteArray(int a, int b, int c, int d, int e, int f, int g, int h) throws IOException {
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream(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();
        }
    }
 
    private static byte[] toByteArray(int i) {
        byte[] byteArray = new byte[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;
    }
 
    private static String byteToHexString(byte[] bytes)
    {
        StringBuilder resultHexString = new StringBuilder();
        String tempStr;
        for (byte b: bytes) {
            //这里需要对b与0xff做位与运算,
            //若b为负数,强制转换将高位位扩展,导致错误,
            //故需要高位清零
            tempStr = Integer.toHexString(b & 0xff);
            //若转换后的十六进制数字只有一位,
            //则在前补"0"
            if (tempStr.length() == 1) {
                resultHexString.append(0).append(tempStr);
            } else {
                resultHexString.append(tempStr);
            }
        }
        return resultHexString.toString();
    }
 
    private Sm3Utils() {
    }
}

加密代码

用户表(SysUser),密码字段(password),放盐的字段(salt)

        byte[] mySalt = Sm3crypto.getSalt();
        //将盐用base64转化为字符串存到数据库中
        sysUser.setSalt(Base64.getEncoder().encodeToString(mySalt));
        //用密码sm3加密后再加盐,形成新的密码
        sysUser.setPassword(Hex.toHexString(Sm3crypto.pwdSaltedHashValue(mySalt, sysUser.getPassword())));
        this.save(sysUser);

本文代码转载至 SM3加密算法 想看完整版点击这个。

你可能感兴趣的:(加密算法,java)