龙空技术网

国产加密运用:使用SM3加盐存储密码,并且使用SM2进行登录认证

蜗牛学技术 35

前言:

此时咱们对“passwd查看密码”大约比较注重,大家都需要了解一些“passwd查看密码”的相关知识。那么小编同时在网上网罗了一些关于“passwd查看密码””的相关知识,希望兄弟们能喜欢,兄弟们一起来了解一下吧!

1.简要

本文结合个人实际代码,使用SM3加盐存储密码,并且使用SM2进行登录认证。主要有以下两个点需要了解:

1.新增用户时,将明文密码通过SM3加密后再加盐(随机生成)后形成密文存储在数据库中,同时我们也要将盐存在用户表的一个字段中(用于登录时密码的校验比对)。用户修改时不需要将盐进行更新存储。

2.登录认证时,在前端通过一个固定的公钥将账号和密码使用sm2进行加密后再传输到后端,后端通过一个固定的私钥将账号及密码进行解密,解密后的密码通过sm3加盐进行加密后与数据库中的密文进行比对认证。

3.以下所放的代码均为关键代码,并非全部代码,但足以供大家参考实现功能。

2.开发环境及工具

java,idea,mybatis-plus,spring boot,vue

3.后台密码加密部分

后台接收到密码明文,进行加密并保存。

3.1加密代码

用户表(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);
3.2 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;    }   }
3.3国密SM3工具类(Sm3Utils)
import lombok.extern.slf4j.Slf4j;import org.bouncycastle.util.Arrays; import java.io.ByteArrayOutputStream;import java.io.IOException; /** * 国密SM3,消息摘要(MD5) * * @author Luke */@Slf4jpublic 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() {    }}
3.4国密相关依赖包
<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>
4.登录认证部分4.1前端部分关键代码

1.npm安装国密:

npm install --save sm-crypto

2.vue中引用国密:

import SMCRYPTO from "sm-crypto";

3.在data中定义一下sm2:

data () {    return {      .      .      .      sm2: SMCRYPTO.sm2,      pubKey: "自己的SM2公钥"    }  },

4.登录时将账号密码使用sm2加密后传输到后台

          //账号密码使用sm2加密          loginParams.account = this.sm2.doEncrypt(values.account, this.pubKey, 1);          loginParams.password = this.sm2.doEncrypt(values.password, this.pubKey, 1);
4.2后端login登录认证的关键代码

接收到前端的账号密码后进行处理,要将账号和密码利用SM2私钥进行解密,解密后的密码需要用sm3加密再加盐进行处理后与数据库的密码密文进行比对:

        //账号解密        account = SM2Utils.decrypt(Sm2crypto.priKey,account,1);         SysUser sysUser = sysUserService.getUserByCount(account);         //用户不存在,账号或密码错误        if (ObjectUtil.isEmpty(sysUser)) {            LogManager.me().executeLoginLog(account, LogSuccessStatusEnum.FAIL.getCode(), AuthExceptionEnum.ACCOUNT_PWD_ERROR.getMessage());            throw new AuthException(AuthExceptionEnum.ACCOUNT_PWD_ERROR);        }           String passwordBcrypt = sysUser.getPassword();         //密码解密        password = SM2Utils.decrypt(Sm2crypto.priKey,password,1);         //获取用户表的盐        byte[] mySalt = Base64.getDecoder().decode(sysUser.getSalt());        //用密码sm3加密后再加盐,形成新的密码        password = Hex.toHexString(Sm3crypto.pwdSaltedHashValue(mySalt,password));         //验证账号密码是否正确        if (ObjectUtil.isEmpty(passwordBcrypt) || !password.equals(passwordBcrypt)) {             LogManager.me().executeLoginLog(sysUser.getAccount(),         LogSuccessStatusEnum.FAIL.getCode(), AuthExceptionEnum.ACCOUNT_PWD_ERROR.getMessage());            throw new AuthException(AuthExceptionEnum.ACCOUNT_PWD_ERROR);        }
4.2.1.SM2公私钥(Sm2crypto )
import cn.stylefeng.guns.core.sm2.SM2KeyPair;import cn.stylefeng.guns.core.sm2.SM2Utils;import org.springframework.stereotype.Component; import java.lang.reflect.Field;import java.util.Objects; @Componentpublic class Sm2crypto {     public static String pubKey = "自己的SM2公钥,与前端加密用的公钥一样";    public static String priKey = "自己的SM2私钥"; }
4.2.2SM2工具类(SM2Utils)
import lombok.extern.slf4j.Slf4j;import org.bouncycastle.asn1.gm.GMNamedCurves;import org.bouncycastle.asn1.x9.X9ECParameters;import org.bouncycastle.crypto.AsymmetricCipherKeyPair;import org.bouncycastle.crypto.generators.ECKeyPairGenerator;import org.bouncycastle.crypto.params.*;import org.bouncycastle.math.ec.ECPoint;import org.bouncycastle.util.encoders.Hex; import java.math.BigInteger;import java.security.NoSuchAlgorithmException;import java.security.SecureRandom; @Slf4jpublic class SM2Utils {      /**     * SM2加密算法     * @param publicKey 公钥     * @param data 待加密的数据     * @return 密文,BC库产生的密文带由04标识符,与非BC库对接时需要去掉开头的04     */    public static String encrypt(String publicKey, String data){        // 按国密排序标准加密        return encrypt(publicKey, data, SM2EngineExtend.CIPHERMODE_NORM);    }     /**     * SM2加密算法     * @param publicKey 公钥     * @param data 待加密的数据     * @param cipherMode 密文排列方式0-C1C2C3;1-C1C3C2;     * @return 密文,BC库产生的密文带由04标识符,与非BC库对接时需要去掉开头的04     */    public static String encrypt(String publicKey, String data, int cipherMode){        // 获取一条SM2曲线参数        X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");        // 构造ECC算法参数,曲线方程、椭圆曲线G点、大整数N        ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());        //提取公钥点        ECPoint pukPoint = sm2ECParameters.getCurve().decodePoint(Hex.decode(publicKey));        // 公钥前面的02或者03表示是压缩公钥,04表示未压缩公钥, 04的时候,可以去掉前面的04        ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, domainParameters);         SM2EngineExtend sm2Engine = new SM2EngineExtend();        // 设置sm2为加密模式        sm2Engine.init(true, cipherMode, new ParametersWithRandom(publicKeyParameters, new SecureRandom()));         byte[] arrayOfBytes = null;        try {            byte[] in = data.getBytes();            arrayOfBytes = sm2Engine.processBlock(in, 0, in.length);        } catch (Exception e) {            log.error("SM2加密时出现异常:{}", e.getMessage(), e);        }        return Hex.toHexString(arrayOfBytes);    }     /**     * 获取sm2密钥对     * BC库使用的公钥=64个字节+1个字节(04标志位),BC库使用的私钥=32个字节     * SM2秘钥的组成部分有 私钥D 、公钥X 、 公钥Y , 他们都可以用长度为64的16进制的HEX串表示,     * <br/>SM2公钥并不是直接由X+Y表示 , 而是额外添加了一个头,当启用压缩时:公钥=有头+公钥X ,即省略了公钥Y的部分     * @param compressed 是否压缩公钥(加密解密都使用BC库才能使用压缩)     * @return     */    public static SM2KeyPair getSm2Keys(boolean compressed){        //获取一条SM2曲线参数        X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");        //构造domain参数        ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());        //1.创建密钥生成器        ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();        //2.初始化生成器,带上随机数        try {            keyPairGenerator.init(new ECKeyGenerationParameters(domainParameters, SecureRandom.getInstance("SHA1PRNG")));        } catch (NoSuchAlgorithmException e) {            log.error("生成公私钥对时出现异常:", e);        }        //3.生成密钥对        AsymmetricCipherKeyPair asymmetricCipherKeyPair = keyPairGenerator.generateKeyPair();        ECPublicKeyParameters publicKeyParameters = (ECPublicKeyParameters)asymmetricCipherKeyPair.getPublic();        ECPoint ecPoint = publicKeyParameters.getQ();        // 把公钥放入map中,默认压缩公钥        // 公钥前面的02或者03表示是压缩公钥,04表示未压缩公钥,04的时候,可以去掉前面的04        String publicKey = Hex.toHexString(ecPoint.getEncoded(compressed));        ECPrivateKeyParameters privateKeyParameters = (ECPrivateKeyParameters) asymmetricCipherKeyPair.getPrivate();        BigInteger intPrivateKey = privateKeyParameters.getD();        // 把私钥放入map中        String privateKey = intPrivateKey.toString(16);        return new SM2KeyPair(publicKey, privateKey);    }     /**     * SM2解密算法     * @param privateKey    私钥     * @param cipherData    密文数据     * @return     */    public static String decrypt(String privateKey, String cipherData) {        // // 按国密排序标准解密        return decrypt(privateKey, cipherData, SM2EngineExtend.CIPHERMODE_NORM);    }     /**     * SM2解密算法     * @param privateKey    私钥     * @param cipherData    密文数据     * @param cipherMode 密文排列方式0-C1C2C3;1-C1C3C2;     * @return     */    public static String decrypt(String privateKey, String cipherData, int cipherMode) {        // 使用BC库加解密时密文以04开头,传入的密文前面没有04则补上        if (!cipherData.startsWith("04")){            cipherData = "04" + cipherData;        }        byte[] cipherDataByte = Hex.decode(cipherData);         //获取一条SM2曲线参数        X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");        //构造domain参数        ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());         BigInteger privateKeyD = new BigInteger(privateKey, 16);        ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, domainParameters);         SM2EngineExtend sm2Engine = new SM2EngineExtend();        // 设置sm2为解密模式        sm2Engine.init(false, cipherMode, privateKeyParameters);         String result = "";        try {            byte[] arrayOfBytes = sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length);            return new String(arrayOfBytes);        } catch (Exception e) {            log.error("SM2解密时出现异常:{}", e.getMessage(), e);        }        return result;     } }
4.2.3SM2EngineExtend类
 import org.bouncycastle.crypto.CipherParameters;import org.bouncycastle.crypto.Digest;import org.bouncycastle.crypto.InvalidCipherTextException;import org.bouncycastle.crypto.digests.SM3Digest;import org.bouncycastle.crypto.params.*;import org.bouncycastle.math.ec.ECConstants;import org.bouncycastle.math.ec.ECFieldElement;import org.bouncycastle.math.ec.ECPoint;import org.bouncycastle.util.Arrays;import org.bouncycastle.util.BigIntegers; import java.math.BigInteger;import java.security.SecureRandom; public class SM2EngineExtend {     private final Digest digest;     /**是否为加密模式*/    private boolean forEncryption;    private ECKeyParameters ecKey;    private ECDomainParameters ecParams;    private int curveLength;    private SecureRandom random;    /**密文排序方式*/    private int cipherMode;     /**BC库默认排序方式-C1C2C3*/    public static int CIPHERMODE_BC = 0;    /**国密标准排序方式-C1C3C2*/    public static int CIPHERMODE_NORM = 1;     public SM2EngineExtend() {        this(new SM3Digest());    }     public SM2EngineExtend(Digest digest) {        this.digest = digest;    }     /**     * 设置密文排序方式     * @param cipherMode     */    public void setCipherMode(int cipherMode){        this.cipherMode = cipherMode;    }     /**     * 默认初始化方法,使用国密排序标准     * @param forEncryption - 是否以加密模式初始化     * @param param - 曲线参数     */    public void init(boolean forEncryption, CipherParameters param) {        init(forEncryption, CIPHERMODE_NORM, param);    }     /**     * 默认初始化方法,使用国密排序标准     * @param forEncryption 是否以加密模式初始化     * @param cipherMode 加密数据排列模式:1-标准排序;0-BC默认排序     * @param param 曲线参数     */    public void init(boolean forEncryption, int cipherMode, CipherParameters param) {        this.forEncryption = forEncryption;        this.cipherMode = cipherMode;        if (forEncryption) {            ParametersWithRandom rParam = (ParametersWithRandom) param;             ecKey = (ECKeyParameters) rParam.getParameters();            ecParams = ecKey.getParameters();             ECPoint s = ((ECPublicKeyParameters) ecKey).getQ().multiply(ecParams.getH());            if (s.isInfinity()) {                throw new IllegalArgumentException("invalid key: [h]Q at infinity");            }             random = rParam.getRandom();        } else {            ecKey = (ECKeyParameters) param;            ecParams = ecKey.getParameters();        }         curveLength = (ecParams.getCurve().getFieldSize() + 7) / 8;    }     /**     * 加密或解密输入数据     * @param in     * @param inOff     * @param inLen     * @return     * @throws InvalidCipherTextException     */    public byte[] processBlock( byte[] in, int inOff, int inLen) throws InvalidCipherTextException {        if (forEncryption) {            // 加密            return encrypt(in, inOff, inLen);        } else {            return decrypt(in, inOff, inLen);        }    }     /**     * 加密实现,根据cipherMode输出指定排列的结果,默认按标准方式排列     * @param in     * @param inOff     * @param inLen     * @return     * @throws InvalidCipherTextException     */    private byte[] encrypt(byte[] in, int inOff, int inLen)            throws InvalidCipherTextException {        byte[] c2 = new byte[inLen];         System.arraycopy(in, inOff, c2, 0, c2.length);         byte[] c1;        ECPoint kPB;        do {            BigInteger k = nextK();             ECPoint c1P = ecParams.getG().multiply(k).normalize();             c1 = c1P.getEncoded(false);             kPB = ((ECPublicKeyParameters) ecKey).getQ().multiply(k).normalize();             kdf(digest, kPB, c2);        }        while (notEncrypted(c2, in, inOff));         byte[] c3 = new byte[digest.getDigestSize()];         addFieldElement(digest, kPB.getAffineXCoord());        digest.update(in, inOff, inLen);        addFieldElement(digest, kPB.getAffineYCoord());         digest.doFinal(c3, 0);        if (cipherMode == CIPHERMODE_NORM){            return Arrays.concatenate(c1, c3, c2);        }        return Arrays.concatenate(c1, c2, c3);    }     /**     * 解密实现,默认按标准排列方式解密,解密时解出c2部分原文并校验c3部分     * @param in     * @param inOff     * @param inLen     * @return     * @throws InvalidCipherTextException     */    private byte[] decrypt(byte[] in, int inOff, int inLen)            throws InvalidCipherTextException {        byte[] c1 = new byte[curveLength * 2 + 1];         System.arraycopy(in, inOff, c1, 0, c1.length);         ECPoint c1P = ecParams.getCurve().decodePoint(c1);         ECPoint s = c1P.multiply(ecParams.getH());        if (s.isInfinity()) {            throw new InvalidCipherTextException("[h]C1 at infinity");        }         c1P = c1P.multiply(((ECPrivateKeyParameters) ecKey).getD()).normalize();         byte[] c2 = new byte[inLen - c1.length - digest.getDigestSize()];        if (cipherMode == CIPHERMODE_BC) {            System.arraycopy(in, inOff + c1.length, c2, 0, c2.length);        }else{            // C1 C3 C2            System.arraycopy(in, inOff + c1.length + digest.getDigestSize(), c2, 0, c2.length);        }         kdf(digest, c1P, c2);         byte[] c3 = new byte[digest.getDigestSize()];         addFieldElement(digest, c1P.getAffineXCoord());        digest.update(c2, 0, c2.length);        addFieldElement(digest, c1P.getAffineYCoord());         digest.doFinal(c3, 0);         int check = 0;        // 检查密文输入值C3部分和由摘要生成的C3是否一致        if (cipherMode == CIPHERMODE_BC) {            for (int i = 0; i != c3.length; i++) {                check |= c3[i] ^ in[c1.length + c2.length + i];            }        }else{            for (int i = 0; i != c3.length; i++) {                check |= c3[i] ^ in[c1.length + i];            }        }         clearBlock(c1);        clearBlock(c3);         if (check != 0) {            clearBlock(c2);            throw new InvalidCipherTextException("invalid cipher text");        }         return c2;    }     private boolean notEncrypted(byte[] encData, byte[] in, int inOff) {        for (int i = 0; i != encData.length; i++) {            if (encData[i] != in[inOff]) {                return false;            }        }         return true;    }     private void kdf(Digest digest, ECPoint c1, byte[] encData) {        int ct = 1;        int v = digest.getDigestSize();         byte[] buf = new byte[digest.getDigestSize()];        int off = 0;         for (int i = 1; i <= ((encData.length + v - 1) / v); i++) {            addFieldElement(digest, c1.getAffineXCoord());            addFieldElement(digest, c1.getAffineYCoord());            digest.update((byte) (ct >> 24));            digest.update((byte) (ct >> 16));            digest.update((byte) (ct >> 8));            digest.update((byte) ct);             digest.doFinal(buf, 0);             if (off + buf.length < encData.length) {                xor(encData, buf, off, buf.length);            } else {                xor(encData, buf, off, encData.length - off);            }             off += buf.length;            ct++;        }    }     private void xor(byte[] data, byte[] kdfOut, int dOff, int dRemaining) {        for (int i = 0; i != dRemaining; i++) {            data[dOff + i] ^= kdfOut[i];        }    }     private BigInteger nextK() {        int qBitLength = ecParams.getN().bitLength();         BigInteger k;        do {            k = new BigInteger(qBitLength, random);        }        while (k.equals(ECConstants.ZERO) || k.compareTo(ecParams.getN()) >= 0);         return k;    }     private void addFieldElement(Digest digest, ECFieldElement v) {        byte[] p = BigIntegers.asUnsignedByteArray(curveLength, v.toBigInteger());         digest.update(p, 0, p.length);    }     /**     * clear possible sensitive data     */    private void clearBlock(            byte[] block) {        for (int i = 0; i != block.length; i++) {            block[i] = 0;        }    }  }  

来源:

标签: #passwd查看密码 #源码sm3算法java源码 #vue安装sha1加密算法 #java加盐之后怎么解密 #java 加盐