Android 国密SM3-基于BC库(BouncyCastle)实现

813 阅读3分钟

国密SM3是一种由中国国家密码局发布的加密哈希函数,是国家标准 GB/T 32918.1-2016《信息安全技术 公钥基础设施 公钥密码算法 第1部分:SM3密码杂凑算法》规定的加密算法。SM3基于SHA-256算法改进而成,是一种哈希函数,常用于文件摘要、签名验证、消息认证码等方面的安全性要求较高的场合。

相较于其他哈希函数,SM3具有一些优势,包括以下几点:

  1. 速度快:SM3算法速度较快,在当前主流平台上计算速度快于SHA-256;

  2. 安全性强:目前尚未发现明显的漏洞,具备良好的安全性;

  3. 支持国产芯片加速:很多国产芯片支持SM3加速功能,可以进一步提高计算速度。

基于BC库,在Android上实现SM3

jar包配置参考文章

实现效果

工具类

/**
 * @description: SM3工具类
 * @Time 2023/8/14 9:11
 * @Author gz
 */
public class SM3Util {
    private static String TAG = SM3Util.class.getSimpleName();

    static {
        double version = Security.getProvider(BouncyCastleProvider.PROVIDER_NAME).getVersion();
        Log.i(TAG, "generateKey1: " + version);
        Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME);
        if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
            Log.i(TAG, "运行环境没有BouncyCastleProvider");
            Security.addProvider(new BouncyCastleProvider());
        }
        version = Security.getProvider(BouncyCastleProvider.PROVIDER_NAME).getVersion();
        Log.i(TAG, "generateKey2: " + version);
    }
    /**
     * sm3算法加密
     *
     * @param paramStr 待加密字符串
     * @return 返回加密后,固定长度=32的16进制字符串
     * @explain
     */
    public static byte[] encrypt(String paramStr) {
        byte[] resultHash = new byte[0];
        try {
            // 将字符串转换成byte数组
            byte[] srcData = paramStr.getBytes(ENCODING);
            // 调用hash()
            resultHash = hash(srcData);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return resultHash;
    }

    /**
     * 返回长度=32的byte数组
     *
     * @param srcData
     * @return
     * @explain 生成对应的hash值
     */
    public static byte[] hash(byte[] srcData) {
        SM3Digest digest = new SM3Digest();
        digest.update(srcData, 0, srcData.length);
        byte[] hash = new byte[digest.getDigestSize()];
        digest.doFinal(hash, 0);
        return hash;
    }

    /**
     * sm3算法加密
     *
     * @param paramStr 待加密字符串
     * @param key      密钥
     * @return 返回加密后,固定长度=32的16进制字符串
     * @explain
     */
    public static byte[] encryptByKey(String paramStr, String key) {
        // 将返回的hash值转换成16进制字符串
        byte[] resultHash = new byte[0];
        try {
            // 将字符串转换成byte数组
            byte[] srcData = paramStr.getBytes(ENCODING);
            // 调用hash()
            resultHash = hashMac(srcData, key.getBytes(ENCODING));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return resultHash;
    }

    /**
     * 通过密钥进行加密
     *
     * @param key     密钥
     * @param srcData 被加密的byte数组
     * @return
     * @explain 指定密钥进行加密
     */
    public static byte[] hashMac(byte[] key, byte[] srcData) {
        KeyParameter keyParameter = new KeyParameter(key);
        SM3Digest digest = new SM3Digest();
        HMac mac = new HMac(digest);
        mac.init(keyParameter);
        mac.update(srcData, 0, srcData.length);
        byte[] result = new byte[mac.getMacSize()];
        mac.doFinal(result, 0);
        return result;
    }


    /**
     * 判断源数据与加密数据是否一致
     *
     * @param srcStr       原字符串
     * @param sm3HexString 16进制字符串
     * @return 校验结果
     * @explain 通过验证原数组和生成的hash数组是否为同一数组,验证2者是否为同一数据
     */
    public static boolean verify(String srcStr, String sm3HexString) {
        boolean flag = false;
        try {
            byte[] srcData = srcStr.getBytes(ENCODING);
//            byte[] sm3Hash = ByteUtils.fromHexString(sm3HexString);
            byte[] sm3Hash = Hex.decode(sm3HexString);
            byte[] newHash = hash(srcData);
            if (Arrays.equals(newHash, sm3Hash))
                flag = true;
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return flag;
    }

    /**
     * 有密钥的加密,判断源数据与加密数据是否一致
     *
     * @param srcStr       原字符串
     * @param sm3HexString 16进制字符串
     * @param key          用户自定义密钥
     * @return 校验结果
     * @explain 通过验证原数组和生成的hash数组是否为同一数组,验证2者是否为同一数据
     */
    public static boolean verifyByKey(String srcStr, String sm3HexString, String key) {
        boolean flag = false;
        try {
            byte[] srcData = srcStr.getBytes(ENCODING);
//            byte[] sm3Hash = ByteUtils.fromHexString(sm3HexString);
            byte[] sm3Hash = Hex.decode(sm3HexString);
            byte[] newHash = hashMac(srcData, key.getBytes(ENCODING));
            if (Arrays.equals(newHash, sm3Hash))
                flag = true;
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return flag;
    }
}

工具类调用

public void onClick(View v) {
        switch (v.getId()) {
            case R.id.cal_sm3:
                content = mBinding.inputText.getText().toString();
                if (TextUtils.isEmpty(content)) {
                    Toast.makeText(this, "请输入要计算的内容", Toast.LENGTH_SHORT).show();
                    break;
                }
                byte[] bytes = SM3Util.encrypt(content);
//                hexResult = ByteUtils.toHexString(bytes);
                hexResult = Hex.toHexString(bytes);
                result = CommonUtils.changeFormat(bytes);
                Log.e(TAG, "onClick2: " + result);
                viewModel.createResult.set(hexResult);
                break;
            case R.id.verify_result:
                String str = mBinding.calSm3Result.getText().toString();
                if (TextUtils.isEmpty(str)) {
                    Toast.makeText(this, "请先计算", Toast.LENGTH_SHORT).show();
                    break;
                }
                boolean result = SM3Util.verify(content, str);
                viewModel.verifyResult.set("验证结果:\n" + result);
                Log.e(TAG, "result: " + result);
                break;
            case R.id.cal_sm3_key:
                key = mBinding.inputKey.getText().toString();
                if (TextUtils.isEmpty(key)) {
                    Toast.makeText(this, "请先输入密钥", Toast.LENGTH_SHORT).show();
                    break;
                }
                byte[] bytes2 = SM3Util.encryptByKey(content, key);
//                hexResultKey = ByteUtils.toHexString(bytes2);
                hexResultKey = Hex.toHexString(bytes2);
                resultKey = CommonUtils.changeFormat(bytes2);
                Log.e(TAG, "onClick: " + resultKey);
                viewModel.createKeyResult.set(hexResultKey);
                break;
            case R.id.verify_key_result:
                String str2 = mBinding.calSm3KeyResult.getText().toString();
                if (TextUtils.isEmpty(str2)) {
                    Toast.makeText(this, "请先计算", Toast.LENGTH_SHORT).show();
                    break;
                }
                boolean resultKey = SM3Util.verifyByKey(content, str2, key);
                viewModel.verifyKeyResult.set("验证结果:\n" + resultKey);
                Log.e(TAG, "result: " + resultKey);
                break;
        }
    }
```
```