【Java加解密系列】- SM2加解密
上一篇部落格介紹了SM2演算法生成金鑰的過程,詳見-SM2生成金鑰。這篇部落格接著介紹SM2演算法如何用上篇部落格生成的金鑰進行加解密操作。
因為金鑰都是byte陣列,在進行加解密前,我們需要將金鑰轉換成BC庫的CipherParameters。程式碼如下:
/**
* 私鑰轉換為 {@link ECPrivateKeyParameters}
* @param key key
* @return
* @throws InvalidKeyException
*/
public static ECPrivateKeyParameters privateKeyToParams(String algorithm, byte[] key) throws InvalidKeyException, InvalidKeySpecException, NoSuchAlgorithmException {
Preconditions.checkNotNull(key, "key must be not null !");
PrivateKey privateKey = generatePrivateKey(algorithm, key);
return (ECPrivateKeyParameters) ECUtil.generatePrivateKeyParameter(privateKey);
}
/**
* 生成私鑰
* @param algorithm 演算法
* @param key key
* @return
*/
public static PrivateKey generatePrivateKey(String algorithm, byte[] key) throws NoSuchAlgorithmException, InvalidKeySpecException {
Preconditions.checkNotNull(algorithm, "algorithm must be not null !");
Preconditions.checkNotNull(key, "key must be not null !");
KeySpec keySpec = new PKCS8EncodedKeySpec(key);
algorithm = getAlgorithmAfterWith(algorithm);
return getKeyFactory(algorithm).generatePrivate(keySpec);
}
/**
* 公鑰轉換為 {@link ECPublicKeyParameters}
* @param key key
* @return
* @throws InvalidKeyException
*/
public static ECPublicKeyParameters publicKeyToParams(String algorithm, byte[] key) throws InvalidKeyException, InvalidKeySpecException, NoSuchAlgorithmException {
Preconditions.checkNotNull(key, "key must be not null !");
PublicKey publicKey = generatePublicKey(algorithm, key);
return (ECPublicKeyParameters) ECUtil.generatePublicKeyParameter(publicKey);
}
/**
* 生成公鑰
* @param algorithm 演算法
* @param key key
* @return
*/
public static PublicKey generatePublicKey(String algorithm, byte[] key) throws NoSuchAlgorithmException, InvalidKeySpecException {
Preconditions.checkNotNull(algorithm, "algorithm must be not null !");
Preconditions.checkNotNull(key, "key must be not null !");
KeySpec keySpec = new X509EncodedKeySpec(key);
algorithm = getAlgorithmAfterWith(algorithm);
return getKeyFactory(algorithm).generatePublic(keySpec);
}
/**
* 獲取用於金鑰生成的演算法<br>
* 獲取XXXwithXXX演算法的後半部分演算法,如果為ECDSA或SM2,返回演算法為EC
* @param algorithm XXXwithXXX演算法
* @return 演算法
*/
private static String getAlgorithmAfterWith(String algorithm) {
Preconditions.checkNotNull(algorithm, "algorithm must be not null !");
int indexOfWith = StringUtils.lastIndexOfIgnoreCase(algorithm, "with");
if (indexOfWith > 0) {
algorithm = StringUtils.substring(algorithm, indexOfWith + "with".length());
}
if ("ECDSA".equalsIgnoreCase(algorithm) || "SM2".equalsIgnoreCase(algorithm)) {
algorithm = "EC";
}
return algorithm;
}
/**
* 獲取{@link KeyFactory}
* @param algorithm 非對稱加密演算法
* @return {@link KeyFactory}
*/
private static KeyFactory getKeyFactory(String algorithm) throws NoSuchAlgorithmException {
final Provider provider = new BouncyCastleProvider();
return KeyFactory.getInstance(algorithm, provider);
}
SM2操作主要有四類:加密、解密、簽名和驗籤。程式碼如下:
/**
* 加密
* @param data 資料
* @param publicKey 公鑰
* @return 加密之後的資料
*/
public static byte[] encrypt(byte[] data, byte[] publicKey) throws Exception {
CipherParameters pubKeyParameters = new ParametersWithRandom(publicKeyToParams("SM2", publicKey));
SM2Engine engine = new SM2Engine(DIGEST);
engine.init(true, pubKeyParameters);
return engine.processBlock(data, 0, data.length);
}
/**
* 解密
* @param data 資料
* @param privateKey 私鑰
* @return 解密之後的資料
*/
public static byte[] decrypt(byte[] data, byte[] privateKey) throws Exception {
CipherParameters privateKeyParameters = privateKeyToParams("SM2", privateKey);
SM2Engine engine = new SM2Engine(DIGEST);
engine.init(false, privateKeyParameters);
byte[] byteDate = engine.processBlock(data, 0, data.length);
return byteDate;
}
/**
* 簽名
* @param data 資料
* @return 簽名
*/
public static byte[] sign(byte[] data, byte[] privateKey) throws Exception {
SM2Signer signer = new SM2Signer();
CipherParameters param = new ParametersWithRandom(privateKeyToParams("SM2", privateKey));
signer.init(true, param);
signer.update(data, 0, data.length);
return signer.generateSignature();
}
/**
* 用公鑰檢驗數字簽名的合法性
* @param data 資料
* @param sign 簽名
* @param publicKey 公鑰
* @return 是否驗證通過
*/
public static boolean verify(byte[] data, byte[] sign, byte[] publicKey) throws Exception {
SM2Signer signer = new SM2Signer();
CipherParameters param = publicKeyToParams("SM2", publicKey);
signer.init(false, param);
signer.update(data, 0, data.length);
return signer.verifySignature(sign);
}
跑個測試用例試下:
public static void main(String[] args) throws Exception {
KeyPair keyPair = generateSm2KeyPair();
//明文
String plaintext = "test";
//加密
String ciphertext = Base64Utils.encode(encrypt(plaintext.getBytes("utf-8"), keyPair.getPublic().getEncoded()));
//生成簽名
String signature = Base64Utils.encode(sign(plaintext.getBytes("utf-8"),keyPair.getPrivate().getEncoded()));
System.out.println("ciphertext: " + ciphertext);
System.out.println("signature: " + signature);
//解密
plaintext = new String(decrypt(Base64Utils.decode(ciphertext),keyPair.getPrivate().getEncoded()),"utf-8");
//驗籤
boolean result = verify(plaintext.getBytes("utf-8"),Base64Utils.decode(signature),keyPair.getPublic().getEncoded());
System.out.println("plaintext: " + plaintext);
System.out.println("verify result: " + result);
}
輸出結果如下:
ciphertext: BFH0SXC1bX7OL1i5I4GO/Ck8Lak6gfzrsEzhrk4GY2cKZOug73ThLBYSjbtnpC5z30CJuKmaAf/W+jlviRr9PT1dcBWyrO599UUC4XWW8as4XHej8nE/Rlr9TP5+wP+4AWaub6o=
signature: MEYCIQC7OldJ7B8JvP51zw8P2DfieG5iAj5rWEybqZ3bPG8D5wIhAIVXlOmMxk0t4cNm0oQ0HYIzJZ5JShGIWuVwZLZ/t9mQ
plaintext: test
verify result: true
從結果可以看出,可以進行正常的加解密、生成簽名以及驗籤操作。
相關文章
- Java RSA 分段加解密Java解密
- Java之DES加解密解析Java解密
- Java之RSA加解密解析Java解密
- 前端加解密前端解密
- Java DES 加解密("DES/EBC/NoPadding")Java解密padding
- 客戶端加解密客戶端解密
- 遊戲異或加解密遊戲解密
- 加解密演算法解密演算法
- 移動端加解密解密
- Python/Java AES 加解密方法互相轉換PythonJava解密
- 加解密演算法分析解密演算法
- 對稱EDS加解密方法解密
- NodeJS加解密之CryptoNodeJS解密
- C# RSA 分段加解密C#解密
- 3DES加解密-EncryptAndDecryptFile3D解密
- Openssl RSA基本加解密操作解密
- AES加解密多版本(GO、JAVA、Python)實現解密GoJavaPython
- Java DESede 加解密("DESede/ECB/PKCS5Padding")Java解密padding
- Java DES 加解密("DES/ECB/PKCS1Padding")Java解密padding
- Java DES 加解密("DES/CBC/PKCS5Padding")Java解密padding
- vue 核心加解密工具類 方法Vue解密
- AES加解密使用總結解密
- PHP 支援加解密的函式PHP解密函式
- C語言(檔案加解密)C語言解密
- .NET AES加解密(128位)解密
- jive對密碼的加解密?密碼解密
- C#.NET 字串加解密程式C#字串解密
- python各種加解密方法Python解密
- 安全閘道器 透明加解密解密
- .NET Core加解密實戰系列之——對稱加密演算法解密加密演算法
- JAVA加解密19-數字簽名演算法DSAJava解密演算法
- TEA、XTEA、XXTEA加解密過程解密
- 小規模DES手寫加解密解密
- VUE專案 AES加解密(小白版)Vue解密
- OpenSSL 使用AES對檔案加解密解密
- 前後端引數加解密方案後端解密
- CTF中常見的加解密(經典)解密
- Python AES - base64 加解密Python解密