首先介绍下什么是国密算法
国密算法是中国自主制定的加密算法系列,主要由中华人民共和国国家密码管理局负责制定并发布。根据不同的应用场景,主要有:SM1、SM2、SM3、SM4、SM7、SM9、ZUC,开源算法并使用的有SM2\SM3\SM4三种。
- SM1:一种对称加密算法,也被称为国家商用密码算法中的“密钥交换”,是一种安全强度与AES相当的分组密码算法。SM1基于密钥交换技术实现;该算法不公开,调用该算法时,需要通过加密芯片的接口进行调用。
- SM2:非对称加密算法,基于椭圆曲线,SM2算法可以用于公开密钥加密、数字签名、随机数生成等多种用途;
- SM3:一种哈希函数,SM3可以计算出固定的160比特摘要,支持SHA-384、SHA-512算法的安全特性;
- SM4:分组密码算法,用于数据传输和存储过程中的加密;
- SM7对称密码是一种分组密码算法,分组长度为128比特,密钥长度为128比特。SM7适用于非接触式IC卡,应用包括身份识别类应用(门禁卡、工作证、参赛证),票务类应用(大型赛事门票、展会门票),支付与通卡类应用(积分消费卡、校园一卡通、企业一卡通等)。
- SM9标识密码算法
为了降低公开密钥系统中密钥和证书管理的复杂性,以色列科学家、RSA算法发明人之一Adi Shamir在1984年提出了标识密码(Identity-Based Cryptography)的理念。标识密码将用户的标识(如邮件地址、手机号码、QQ号码等)作为公钥,省略了交换数字证书和公钥过程,使得安全系统变得易于部署和管理,非常适合端对端离线安全通讯、云端数据加密、基于属性加密、基于策略加密的各种场合。2008年标识密码算法正式获得国家密码管理局颁发的商密算法型号:SM9(商密九号算法),为我国标识密码技术的应用奠定了坚实的基础。
SM9算法不需要申请数字证书,适用于互联网应用的各种新兴应用的安全保障。如基于云技术的密码服务、电子邮件安全、智能终端保护、物联网安全、云存储安全等等。这些安全应用可采用手机号码或邮件地址作为公钥,实现数据加密、身份认证、通话加密、通道加密等安全应用,并具有使用方便,易于部署的特点,从而开启了普及密码算法的大门。 - ZUC祖冲之算法
祖冲之序列密码算法是中国自主研究的流密码算法,是运用于移动通信4G网络中的国际标准密码算法,该算法包括祖冲之算法(ZUC)、加密算法(128-EEA3)和完整性算法(128-EIA3)三个部分。目前已有对ZUC算法的优化实现,有专门针对128-EEA3和128-EIA3的硬件实现与优化。
SM2是一种非对称加密算法,也被称为国密算法或商密算法,是中国国家密码管理局(State Cryptography Administration,SCA)发布的一套国家密码标准中的一部分。与传统的非对称加密算法(如RSA、DSA等)相比,SM2在保证安全性的前提下,具有较高的性能和较小的密钥尺寸,适用于数字签名、密钥交换和公钥加密等场景。
SM2算法的一些重要特点和特性:
- 非对称加密: SM2是一种非对称加密算法,意味着它使用一对相关的密钥:公钥和私钥。公钥用于加密数据和验证数字签名,而私钥用于解密数据和生成数字签名。
- 椭圆曲线密码学: SM2基于椭圆曲线密码学(Elliptic Curve Cryptography,ECC),这是一种比传统的RSA等算法更高效的非对称加密方法。ECC使用的椭圆曲线数学问题被认为在相对较短的密钥长度下提供了与传统算法相当的安全性。
- 数字签名: SM2支持数字签名,允许数据的发送者使用其私钥对数据进行签名,接收者可以使用发送者的公钥验证签名的有效性。这有助于确保数据的完整性和身份验证。
- 密钥交换: SM2还支持密钥交换,使得两个通信方可以通过交换公钥来协商一个共享的密钥,用于后续的对称加密通信。这有助于保护通信内容的保密性。
- 公钥加密: SM2可以用于公钥加密,允许数据的发送者使用接收者的公钥加密数据,只有接收者可以使用其私钥解密。
- 性能和密钥尺寸: SM2相对于传统的非对称加密算法,如RSA,具有更小的密钥尺寸,从而提供了更高的性能。这对于移动设备和资源受限的环境非常有利。
- 安全性: SM2的安全性经过了广泛的分析和测试,并且在国际密码学社区中得到了关注。然而,就像所有加密算法一样,随着时间的推移和攻击技术的发展,算法的安全性可能会受到影响。因此,使用SM2时需要关注最新的安全建议和实践。
使用BC库实现SM2
BC库jar包下载地址(三个都可以,这里使用的第一个15to18):
Android中自带有BouncyCastleProvider,导入新的jar后,需要先把默认的provider移除掉,然后在重新加载新的provider,可以打印版本号查看对应库的版本是否一致。
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("sys", "运行环境没有BouncyCastleProvider");
Security.addProvider(new BouncyCastleProvider());
}
version = Security.getProvider(BouncyCastleProvider.PROVIDER_NAME).getVersion();
Log.i(TAG, "generateKey2: " + version);
}
实现效果
SM2使用流程
- 密钥生成阶段:
- 生成一对密钥:公钥和私钥。通常情况下,使用者会生成自己的密钥对。
- 数字签名阶段,也就是验签(如果需要的话) :
- 数据发送者:
使用自己的私钥对要发送的数据进行签名,生成数字签名。
- 数据接收者:
使用发送者的公钥对收到的数据和数字签名进行验证,确保数据的完整性和发送者的身份。
- 密钥交换阶段(如果需要的话) :
- 通信双方:
各自生成密钥对,并将自己的公钥传递给对方。使用对方的公钥计算共享密钥,该共享密钥将用于后续的对称加密通信。
- 公钥加密和解密阶段:
- 数据发送者:
使用接收者的公钥对要发送的数据进行加密。
- 数据接收者:
使用自己的私钥对收到的加密数据进行解密,恢复原始数据。
总的来说,就是以下四步,按照自己的需要进行选择
- 生成密钥对:使用者生成自己的公钥和私钥。
- 数字签名-验签(可选):发送者使用私钥对数据进行签名,接收者使用公钥验证签名。
- 密钥交换(可选):通信双方生成密钥对并交换公钥,计算共享密钥。
- 公钥加密和解密:发送者使用接收者的公钥加密数据,接收者使用私钥解密数据。
在实际应用中,需要注意:
- 密钥管理: 密钥的安全管理至关重要。私钥必须严格保密,而公钥可以自由分享。
- 算法参数: 使用正确的算法参数是保证安全性的重要一步,确保在算法使用中采用正确的曲线参数。
- 安全性注意事项: 尽量遵循安全的实践,比如避免使用弱密码、定期更换密钥等。
- 实现的正确性: 使用安全且经过验证的实现来执行SM2操作,以避免实现错误导致的安全风险。
工具类
/**
* @description: SM2工具类
* @Time 2023/8/15 9:15
* @Author gz
*/
public class SM2Util {
private static final String sm2p256v1 = "sm2p256v1";
private static String TAG = SM2Util.class.getSimpleName();
/**
* 生成公私钥对
* @param compressedPubKey 是否压缩公钥
* @return SMKeyPair 密钥对
*/
public static SMKeyPair genKeyPair(boolean compressedPubKey) {
//获取密钥对
AsymmetricCipherKeyPair asymmetricCipherKeyPair = genKeyGenerate();
//提取公钥点
ECPoint ecPoint = ((ECPublicKeyParameters) asymmetricCipherKeyPair.getPublic()).getQ();
//公钥前面的02或者03表示是压缩公钥,04表示未压缩公钥,04的时候,可以去掉前面的04
byte[] bytes = ecPoint.getEncoded(compressedPubKey);
String pubKey = Hex.toHexString(bytes);
String base64 = Base64.encodeToString(bytes, Base64.DEFAULT);
Log.i(TAG, "genKeyPair: pubKey:" + pubKey + ",base64:" + base64);
BigInteger privateKey = ((ECPrivateKeyParameters) asymmetricCipherKeyPair.getPrivate()).getD();
String priKey = privateKey.toString(16);
SMKeyPair keyPair = new SMKeyPair(priKey, pubKey);
return keyPair;
}
/**
* 生成SM2公私钥对
* @return AsymmetricCipherKeyPair
*/
private static AsymmetricCipherKeyPair genKeyGenerate() {
//获取一条SM2曲线参数
X9ECParameters sm2ECParameters = GMNamedCurves.getByName(sm2p256v1);
//构造ECC算法参数,曲线方程、椭圆曲线G点、大整数N
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.i(TAG, "生成公私钥对时出现异常: " + e);
e.printStackTrace();
}
//3.生成密钥对
AsymmetricCipherKeyPair asymmetricCipherKeyPair = keyPairGenerator.generateKeyPair();
return asymmetricCipherKeyPair;
}
/**
* 私钥签名
* @param privateKey 私钥
* @param content 待签名内容
* @return
*/
public static String privateKeySign(String privateKey, String content) throws CryptoException {
//待签名内容转为字节数组
byte[] message = Hex.decode(content);
//获取一条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);
//创建签名实例
SM2Signer sm2Signer = new SM2Signer();
//初始化签名实例,带上ID,国密的要求,ID默认值:1234567812345678
try {
sm2Signer.init(true, new ParametersWithID(new ParametersWithRandom(privateKeyParameters, SecureRandom.getInstance("SHA1PRNG")), Strings.toByteArray("1234567812345678")));
} catch (NoSuchAlgorithmException e) {
Log.e(TAG, "sign: ", e);
}
sm2Signer.update(message, 0, message.length);
//生成签名,签名分为两部分r和s,分别对应索引0和1的数组
byte[] signBytes = sm2Signer.generateSignature();
String sign = Hex.toHexString(signBytes);
return sign;
}
/**
* 验证签名
* @param publicKey 公钥
* @param content 待签名内容
* @param sign 签名值
* @return boolean true/false
*/
public static boolean signVerify(String publicKey, String content, String sign) {
boolean verify;
try {
//待签名内容
byte[] message = Hex.decode(content);
byte[] signData = Hex.decode(sign);
// 获取一条SM2曲线参数
X9ECParameters sm2ECParameters = GMNamedCurves.getByName(sm2p256v1);
// 构造domain参数
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);
//创建签名实例
SM2Signer sm2Signer = new SM2Signer();
ParametersWithID parametersWithID = new ParametersWithID(publicKeyParameters, Strings.toByteArray("1234567812345678"));
sm2Signer.init(false, parametersWithID);
sm2Signer.update(message, 0, message.length);
//验证签名结果
verify = sm2Signer.verifySignature(signData);
} catch (Exception e) {
verify = false;
Log.e(TAG, "verify: ", e.fillInStackTrace());
}
return verify;
}
/**
* SM2加密算法
*
* @param publicKey 公钥
* @param data 数据
* @return
*/
public static String encryptSM2(String publicKey, String data) {
// 获取一条SM2曲线参数
X9ECParameters sm2ECParameters = GMNamedCurves.getByName(sm2p256v1);
// 构造domain参数
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);
SM2Engine sm2Engine = new SM2Engine();
sm2Engine.init(true, new ParametersWithRandom(publicKeyParameters, new SecureRandom()));
byte[] arrayOfBytes = null;
try {
byte[] in = data.getBytes("utf-8");
arrayOfBytes = sm2Engine.processBlock(in, 0, in.length);
} catch (Exception e) {
Log.e(TAG, "SM2加密时出现异常: ", e);
}
Log.i(TAG, "encrypt: " + Base64.encodeToString(arrayOfBytes, Base64.DEFAULT));
return Hex.toHexString(arrayOfBytes);
}
/**
* SM2解密算法
*
* @param privateKey 私钥
* @param cipherData 密文数据
* @return
*/
public static String decryptSM2(String privateKey, String cipherData) {
String result = "解密失败";
try {
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);
SM2Engine sm2Engine = new SM2Engine();
sm2Engine.init(false, privateKeyParameters);
byte[] arrayOfBytes = sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length);
return new String(arrayOfBytes, "utf-8");
} catch (Exception e) {
Log.e(TAG, "SM2解密时出现异常: ", e);
}
return result;
}
/**
* 将未压缩公钥压缩成压缩公钥
* 公钥前面的02或者03表示是压缩公钥,04表示未压缩公钥, 04的时候,可以去掉前面的04
*
* @param pubKey 未压缩公钥(16进制,不要带头部04)
* @return
*/
public static String compressPubKey(String pubKey) {
pubKey = "04" + pubKey; //将未压缩公钥加上未压缩标识.
// 获取一条SM2曲线参数
X9ECParameters sm2ECParameters = GMNamedCurves.getByName(sm2p256v1);
//提取公钥点
ECPoint pukPoint = sm2ECParameters.getCurve().decodePoint(Hex.decode(pubKey));
String compressPubKey = Hex.toHexString(pukPoint.getEncoded(Boolean.TRUE));
return compressPubKey;
}
/**
* 将压缩的公钥解压为非压缩公钥
*
* @param compressKey 压缩公钥
* @return
*/
public static String unCompressPubKey(String compressKey) {
// 获取一条SM2曲线参数
X9ECParameters sm2ECParameters = GMNamedCurves.getByName(sm2p256v1);
//提取公钥点
ECPoint pukPoint = sm2ECParameters.getCurve().decodePoint(Hex.decode(compressKey));
String pubKey = Hex.toHexString(pukPoint.getEncoded(Boolean.FALSE));
//去掉前面的04 (04的时候,可以去掉前面的04)
pubKey = pubKey.substring(2);
return pubKey;
}
/**
* 将R或者S修正为固定字节数
*
* @param rs
* @return
*/
private static byte[] modifyRSFixedBytes(byte[] rs) {
int length = rs.length;
int fixedLength = 32;
byte[] result = new byte[fixedLength];
if (length < 32) {
System.arraycopy(rs, 0, result, fixedLength - length, length);
} else {
System.arraycopy(rs, length - fixedLength, result, 0, fixedLength);
}
return result;
}
}
工具类代码调用
public void onClick(View v) {
switch (v.getId()) {
case R.id.client_sm2_key_create:
clientSM2Pair = genKeyPair(false);
Log.i(TAG, "clientSM2Pair:" + clientSM2Pair);
viewModel.clientSM2KeyInfo.set("客户端密钥对信息:\n" + clientSM2Pair.toString());
viewModel.checkSignText.set("验签信息\n私钥:" + clientSM2Pair.getPrivateKey()
+ "\n" + "明文:" + checkSignString);
viewModel.clientPublicKey.set("客户端的公钥:\n" + clientSM2Pair.getPublicKey());
break;
case R.id.sm2_client_encrypt:
content = mBinding.inputText.getText().toString();
if (TextUtils.isEmpty(content)) {
Toast.makeText(this, "请输入要加密的文本", Toast.LENGTH_SHORT).show();
break;
}
if (serverSM2Pair == null) {
Toast.makeText(this, "请生成服务端密钥对", Toast.LENGTH_SHORT).show();
break;
}
encryptData = encryptSM2(serverSM2Pair.getPublicKey(), content);
System.out.println("客户端发送给服务端数据加密结果:\n" + encryptData);
viewModel.clientEncryptInfo.set("客户端发送给服务端数据加密结果:\n" + encryptData);
viewModel.serverPublicKey.set("使用服务端的公钥:\n" + serverSM2Pair.getPublicKey());
break;
case R.id.sm2_encrypt_test:
content = mBinding.inputText.getText().toString();
if (TextUtils.isEmpty(content)) {
Toast.makeText(this, "请输入要加密的文本", Toast.LENGTH_SHORT).show();
break;
}
if (clientSM2Pair == null) {
Toast.makeText(this, "请生成客户端端密钥对", Toast.LENGTH_SHORT).show();
break;
}
encryptDataTest = encryptSM2(clientSM2Pair.getPublicKey(), content);
System.out.println("客户端test加密结果:\n" + encryptDataTest);
viewModel.clientEncryptInfoTest.set(encryptDataTest);
break;
case R.id.sm2_decrypt:
String sm2En = mBinding.sm2EncryptTestInfo.getText().toString();
if (TextUtils.isEmpty(sm2En)) {
Toast.makeText(this, "请先加密", Toast.LENGTH_SHORT).show();
break;
}
String temp = decryptSM2(clientSM2Pair.getPrivateKey(), sm2En);
viewModel.clientDecryptInfoTest.set("客户端测试解密结果:\n" + temp);
System.out.println("客户端测试解密结果" + temp);
break;
case R.id.server_m2_key_create:
serverSM2Pair = genKeyPair(false);
Log.i(TAG, "serverSM2Pair:" + serverSM2Pair);
viewModel.serverSM2KeyInfo.set("服务端密钥信息:\n" + serverSM2Pair.toString());
break;
case R.id.sm2_server_decrypt:
if (TextUtils.isEmpty(encryptData)) {
Toast.makeText(this, "没有加密数据,请先加密", Toast.LENGTH_SHORT).show();
break;
}
String decryptData = decryptSM2(serverSM2Pair.getPrivateKey(), encryptData);
viewModel.serverDecryptInfo.set("服务端解密结果:\n" + decryptData);
System.out.println("解密结果" + decryptData);
break;
case R.id.generate_sign:
//客户端私钥进行签名,发送公钥和明文给服务端,服务端进行验签
try {
checkSign = privateKeySign(clientSM2Pair.getPrivateKey(), Hex.toHexString(checkSignString.getBytes()));
} catch (CryptoException e) {
e.printStackTrace();
}
Log.i(TAG, "生成签名:" + checkSign);
viewModel.generateSign.set(checkSign);
break;
case R.id.check_sign_but:
String str = mBinding.generateSignInfo.getText().toString();
if (TextUtils.isEmpty(str)) {
Toast.makeText(this, "请先签名", Toast.LENGTH_SHORT).show();
break;
}
boolean verify = signVerify(clientSM2Pair.getPublicKey(), Hex.toHexString(checkSignString.getBytes()), str);
Log.i(TAG, "验签结果:" + verify);
viewModel.checkSignResult.set("验签结果:" + verify);
Deque<String> deque = new ArrayDeque<>();
deque.poll();
break;
}
}
工具类代码调用
public void onClick(View v) {
switch (v.getId()) {
case R.id.client_sm2_key_create:
clientSM2Pair = genKeyPair(false);
Log.i(TAG, "clientSM2Pair:" + clientSM2Pair);
viewModel.clientSM2KeyInfo.set("客户端密钥对信息:\n" + clientSM2Pair.toString());
viewModel.checkSignText.set("验签信息\n私钥:" + clientSM2Pair.getPrivateKey()
+ "\n" + "明文:" + checkSignString);
viewModel.clientPublicKey.set("客户端的公钥:\n" + clientSM2Pair.getPublicKey());
break;
case R.id.sm2_client_encrypt:
content = mBinding.inputText.getText().toString();
if (TextUtils.isEmpty(content)) {
Toast.makeText(this, "请输入要加密的文本", Toast.LENGTH_SHORT).show();
break;
}
if (serverSM2Pair == null) {
Toast.makeText(this, "请生成服务端密钥对", Toast.LENGTH_SHORT).show();
break;
}
encryptData = encryptSM2(serverSM2Pair.getPublicKey(), content);
System.out.println("客户端发送给服务端数据加密结果:\n" + encryptData);
viewModel.clientEncryptInfo.set("客户端发送给服务端数据加密结果:\n" + encryptData);
viewModel.serverPublicKey.set("使用服务端的公钥:\n" + serverSM2Pair.getPublicKey());
break;
case R.id.sm2_encrypt_test:
content = mBinding.inputText.getText().toString();
if (TextUtils.isEmpty(content)) {
Toast.makeText(this, "请输入要加密的文本", Toast.LENGTH_SHORT).show();
break;
}
if (clientSM2Pair == null) {
Toast.makeText(this, "请生成客户端端密钥对", Toast.LENGTH_SHORT).show();
break;
}
encryptDataTest = encryptSM2(clientSM2Pair.getPublicKey(), content);
System.out.println("客户端test加密结果:\n" + encryptDataTest);
viewModel.clientEncryptInfoTest.set(encryptDataTest);
break;
case R.id.sm2_decrypt:
String sm2En = mBinding.sm2EncryptTestInfo.getText().toString();
if (TextUtils.isEmpty(sm2En)) {
Toast.makeText(this, "请先加密", Toast.LENGTH_SHORT).show();
break;
}
String temp = decryptSM2(clientSM2Pair.getPrivateKey(), sm2En);
viewModel.clientDecryptInfoTest.set("客户端测试解密结果:\n" + temp);
System.out.println("客户端测试解密结果" + temp);
break;
case R.id.server_m2_key_create:
serverSM2Pair = genKeyPair(false);
Log.i(TAG, "serverSM2Pair:" + serverSM2Pair);
viewModel.serverSM2KeyInfo.set("服务端密钥信息:\n" + serverSM2Pair.toString());
break;
case R.id.sm2_server_decrypt:
if (TextUtils.isEmpty(encryptData)) {
Toast.makeText(this, "没有加密数据,请先加密", Toast.LENGTH_SHORT).show();
break;
}
String decryptData = decryptSM2(serverSM2Pair.getPrivateKey(), encryptData);
viewModel.serverDecryptInfo.set("服务端解密结果:\n" + decryptData);
System.out.println("解密结果" + decryptData);
break;
case R.id.generate_sign:
//客户端私钥进行签名,发送公钥和明文给服务端,服务端进行验签
try {
checkSign = privateKeySign(clientSM2Pair.getPrivateKey(), Hex.toHexString(checkSignString.getBytes()));
} catch (CryptoException e) {
e.printStackTrace();
}
Log.i(TAG, "生成签名:" + checkSign);
viewModel.generateSign.set(checkSign);
break;
case R.id.check_sign_but:
String str = mBinding.generateSignInfo.getText().toString();
if (TextUtils.isEmpty(str)) {
Toast.makeText(this, "请先签名", Toast.LENGTH_SHORT).show();
break;
}
boolean verify = signVerify(clientSM2Pair.getPublicKey(), Hex.toHexString(checkSignString.getBytes()), str);
Log.i(TAG, "验签结果:" + verify);
viewModel.checkSignResult.set("验签结果:" + verify);
Deque<String> deque = new ArrayDeque<>();
deque.poll();
break;
}
}