package com.sunyard.chsm.sdf; import com.sunyard.chsm.enums.AlgMode; import com.sunyard.chsm.enums.KeyAlg; import com.sunyard.chsm.enums.KeyCategory; import com.sunyard.chsm.enums.Padding; import com.sunyard.chsm.sdf.model.EccKey; import com.sunyard.chsm.sdf.util.LangUtils; import com.sunyard.chsm.utils.gm.BCSM2Utils; import com.sunyard.chsm.utils.gm.BCSM3Utils; import com.sunyard.chsm.utils.gm.BCSM4Utils; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey; import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; import org.bouncycastle.util.BigIntegers; import org.springframework.stereotype.Service; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import java.security.*; /** * 基于 BC 库的国密软算法实现 * 当前实现类的接口返回的对称密钥和私钥认为是明文,在上层 Service 进行使用和存储时的加密和解密运算。 *

* 主密钥: * - 生成。 软随机数。项目启动时不存在则自动生成。 * - 存储。 存储在 SC_PARAM_CONF 表, KEY 为 mk 的字段,值为固定的 48 字节,以 HEX 格式 96 字节存储。 * 前 16 字节为主密钥值,后 32 字节为主密钥值使用 SM3 计算的校验值。 * - 同步。基于数据库进行导入、导出、和备份、恢复。多台应用服务器连接同一个数据库,无需同步。 * - 使用。顶层密钥,用于保护其他存于数据的密钥。 * * @author liulu 、Cheney * @since 2024/10/23 */ @Slf4j @Service public class BCSdfApiService extends AbstractSdfApiService { public BCSdfApiService() { super(); } @Override public byte[] generateRandom(int len) { byte[] res = new byte[len]; new SecureRandom().nextBytes(res); return res; } /** * 生成对称算法密钥 * * @param alg 算法,只支持对称算法 * @param keyLen 密钥长度(bit),只针对密钥长度可变的算法。 * 禁止传 null * @return */ @Override public byte[] genSymKey(KeyAlg alg, Integer keyLen) { switch (alg) { case SM4: { if (null == keyLen || keyLen != BCSM4Utils.DEFAULT_KEY_SIZE) { throw new IllegalArgumentException("Invalid key length: " + keyLen); } try { return BCSM4Utils.generateKey(); } catch (NoSuchAlgorithmException | NoSuchProviderException e) { throw new RuntimeException("算法实现错误", e); } } default: { throw new IllegalArgumentException("Unsupported algorithm: " + alg); } } } @Override public byte[] symEncrypt(KeyAlg alg, AlgMode mode, Padding padding, byte[] key, byte[] data) { return symCalc(Cipher.ENCRYPT_MODE, alg, mode, padding, key, data); } @Override public byte[] symDecrypt(KeyAlg alg, AlgMode mode, Padding padding, byte[] key, byte[] data) { return symCalc(Cipher.DECRYPT_MODE, alg, mode, padding, key, data); } /** * 对称加解密的统一实现 * * @param alg 算法,只支持对称算法 * @param key 密钥值,明文 * @param data 加密时明文数据,解密时为密文数据 */ private byte[] symCalc(int cipherMode, KeyAlg alg, AlgMode mode, Padding padding, byte[] key, byte[] data) { if (alg.getCategory() != KeyCategory.SYM_KEY) { throw new IllegalArgumentException("Must SYM_KEY, unsupported algorithm: " + alg); } // 算法 String algName = null; if (alg == KeyAlg.SM4) { algName = "SM4/"; } else { throw new IllegalArgumentException("Unsupported algorithm: " + alg); } // 算法轮模式 algName += mode.getCode() + "/"; // 填充模式 algName += padding.getCode(); Cipher cipher = null; try { cipher = BCSM4Utils.generateECBCipher(algName, cipherMode, key); return cipher.doFinal(data); } catch (IllegalBlockSizeException | BadPaddingException | NoSuchAlgorithmException | NoSuchProviderException | NoSuchPaddingException | InvalidKeyException e) { throw new RuntimeException("算法执行错误", e); } } @SneakyThrows @Override public EccKey genKeyPairEcc() { // 生成密钥对 KeyPair keyPair = BCSM2Utils.generateKeyPair(); BCECPublicKey pubKey = (BCECPublicKey) keyPair.getPublic(); BCECPrivateKey priKey = (BCECPrivateKey) keyPair.getPrivate(); byte[] x = pubKey.getQ().getXCoord().getEncoded(); byte[] y = pubKey.getQ().getYCoord().getEncoded(); byte[] d = BigIntegers.asUnsignedByteArray(32, priKey.getD()); return new EccKey(LangUtils.merge(x, y), d); } @Override public byte[] calculateMAC(byte[] symKey, byte[] pucIv, byte[] pucData) { return new byte[0]; } @Override public byte[] hmac(byte[] key, byte[] srcData) { return BCSM3Utils.hmac(key, srcData); } @Override public byte[] hash(byte[] pucData) { return BCSM3Utils.hash(pucData); } @Override public byte[] encryptByTMK(byte[] data) { return data; } @Override public byte[] decryptByTMK(byte[] data) { return data; } }