/**
* 安全证书生成DTO
*
* @author luce
* @date 2021年08月30日 15:17
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CertificateCreate {
/**
* 国家代码
*/
private String countryCode;
/**
* 省份或州
*/
private String province;
/**
* 地区
*/
private String district;
/**
* 组织
*/
private String organization;
/**
* 通用名称
*/
private String commonName;
/**
* 证书存储路径
*/
private String certificateStoragePath;
/**
* 密钥存储路径
*/
private String secretKeyStoragePath;
}
/**
* 密文文件
*
* @author luce
* @date 2021年08月31日 14:20
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Ciphertext {
/**
* 签文
*/
private String sigFile;
/**
* 加密密钥
*/
private String encryptionKey;
/**
* 加密密文
*/
private String encryptionFile;
/**
* 发送时间
*/
private String time;
}
@RestController
public class CipherController {
@Autowired
private CipherService cipherService;
/**
* 生成证书和密钥文件
*
* @param create 参数类
* @return com.hb56.common.result.RestInfo>
* @author luce
* @date 2021/9/1 10:12
*/
@PostMapping("/cipher/certificatepath")
public RestInfo<?> createCertificate(@RequestBody CertificateCreate create) throws Exception {
return RestUtil.setSuccessMsg("生成成功!", cipherService.createCertificate(create));
}
/**
* 读取证书
*
* @return com.hb56.common.result.RestInfo>
* @author luce
* @date 2021/9/1 10:12
*/
@GetMapping("/cipher/certificate")
public RestInfo<?> getCert() throws Exception {
return RestUtil.setSuccessMsg("读取成功!", cipherService.getCert());
}
/**
* 生成密文:path
*
* @param str 报文
* @param certFilePath 接收方证书文件路径
* @param privateKeyFilePath 发送方密钥文件路径
* @return com.hb56.common.result.RestInfo>
* @author luce
* @date 2021/9/1 10:17
*/
@PostMapping("/cipher/encryptedpath")
public RestInfo<?> encrypted(String str, @RequestHeader("certFilePath") String certFilePath, @RequestHeader("privateKeyFilePath") String privateKeyFilePath) throws Exception {
BCECPublicKey receivePublicKey = getPublicKeyByPath(certFilePath);
BCECPrivateKey sendPrivateKey = getPrivateKeyByPath(privateKeyFilePath);
return RestUtil.setSuccessMsg("生成成功!", cipherService.createCiphertext(str, receivePublicKey, sendPrivateKey));
}
/**
* 生成密文
* @author luce
* @date 2021/9/3 10:23
* @param map str:报文/cert:接收方证书
* @return com.hb56.common.result.RestInfo>
*/
@PostMapping("/cipher/encrypted")
public RestInfo<?> encrypted(@RequestBody Map<String, String> map) throws Exception {
BCECPublicKey receivePublicKey = getPublicKey(map.get("cert"));
BCECPrivateKey sendPrivateKey = cipherService.getPrivateKey();
return RestUtil.setSuccessMsg("生成成功!", cipherService.createCiphertext(map.get("str"), receivePublicKey, sendPrivateKey));
}
/**
* 解读密文:path
*
* @param ciphertext 密文
* @param certFilePath 发送方证书文件路径
* @param privateKeyFilePath 接收方密钥文件路径
* @return com.hb56.common.result.RestInfo>
* @author luce
* @date 2021/9/1 10:18
*/
@PostMapping("/cipher/unscrambledpath")
public RestInfo<?> unscrambled(@RequestBody Ciphertext ciphertext, @RequestHeader("certFilePath") String certFilePath, @RequestHeader("privateKeyFilePath") String privateKeyFilePath) throws Exception {
BCECPublicKey sendPublicKey = getPublicKeyByPath(certFilePath);
BCECPrivateKey receivePrivateKey = getPrivateKeyByPath(privateKeyFilePath);
return RestUtil.setSuccessMsg("读取成功!", cipherService.unscrambledCiphertext(ciphertext, sendPublicKey, receivePrivateKey));
}
/**
* 解读密文
*
* @param ciphertext 密文
* @return com.hb56.common.result.RestInfo>
* @author luce
* @date 2021/9/1 10:18
*/
@PostMapping("/cipher/unscrambled")
public RestInfo<?> unscrambled(@RequestBody Ciphertext ciphertext) throws Exception {
BCECPublicKey sendPublicKey = getPublicKey(ciphertext.getCert());
BCECPrivateKey receivePrivateKey = cipherService.getPrivateKey();
return RestUtil.setSuccessMsg("读取成功!", cipherService.unscrambledCiphertext(ciphertext, sendPublicKey, receivePrivateKey));
}
}
public interface CipherService {
/**
* 生成证书和密钥
*
* @param create
* @return 保存路径 -String
* @author luce
* @date 2021/9/1 10:00
*/
String createCertificate(CertificateCreate create) throws Exception;
/**
* 生成加密文件
*
* @param str 报文
* @param publicKey 接收方公钥
* @param privateKey 发送方私钥
* @return com.hb56.cipherclient.dto.Ciphertext
* @author luce
* @date 2021/9/3 8:45
*/
Ciphertext createCiphertext(String str, BCECPublicKey publicKey, BCECPrivateKey privateKey) throws Exception;
/**
* 解读密文
* @param ciphertext 密文
* @param publicKey 发送方公钥
* @param privateKey 接收方私钥
* @return 报文 -String
* @author luce
* @date 2021/9/3 9:53
*/
String unscrambledCiphertext(Ciphertext ciphertext, BCECPublicKey publicKey, BCECPrivateKey privateKey) throws Exception;
/**
* 读取证书
*
* @return java.lang.String
* @author luce
* @date 2021/9/3 10:09
*/
Object getCert() throws Exception;
/**
* 读取私钥
*
* @return org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey
* @author luce
* @date 2021/9/3 13:23
*/
BCECPrivateKey getPrivateKey() throws Exception;
这层代码会比较多,不过都有注解
@Slf4j
@Service
public class CipherServiceImp implements CipherService {
/**
* 生成证书和密钥
*
* @param create
* @return 保存路径 -String
* @author luce
* @date 2021/8/31 14:56
*/
@Override
public String createCertificate(CertificateCreate create) throws Exception {
//创建密钥
KeyPair keyPair = SM2Util.generateKeyPair();
Map<String, String> names = new HashMap<>();
// 国家代码
names.put(BCStyle.C.getId(), create.getCountryCode());
// 省份或州
names.put(BCStyle.ST.getId(), create.getProvince());
// 地区
names.put(BCStyle.L.getId(), create.getDistrict());
// 组织
names.put(BCStyle.O.getId(), create.getOrganization());
// 通用名称
names.put(BCStyle.CN.getId(), create.getCommonName());
X500Name x500Name = CommonUtil.buildX500Name(names);
SM2X509CertMaker sm2X509CertMaker = new SM2X509CertMaker(keyPair, 525600000, x500Name, new RandomSNAllocator());
PKCS10CertificationRequest pkcs10CertificationRequest = CommonUtil.createCSR(x500Name, new SM2PublicKey((BCECPublicKey) keyPair.getPublic()), keyPair.getPrivate(), SIGN_ALGO_SM3WITHSM2);
X509Certificate x509Certificate = sm2X509CertMaker.makeRootCACert(pkcs10CertificationRequest.getEncoded());
String certStoragePath = "";
String secretKeyStoragePath = "";
if (StringUtils.hasText(create.getCertificateStoragePath())) {
certStoragePath = create.getCertificateStoragePath() + "/safe/certificate.pem";
} else {
certStoragePath = acquireJarPath() + "safe/certificate.pem";
}
if (StringUtils.hasText(create.getSecretKeyStoragePath())) {
secretKeyStoragePath = create.getCertificateStoragePath() + "/safe/private.key";
} else {
secretKeyStoragePath = acquireJarPath() + "safe/private.key";
}
//保存证书文件
createCert(x509Certificate, certStoragePath);
//保存密钥文件
createKeyPair(keyPair, secretKeyStoragePath);
return certStoragePath + " and " + secretKeyStoragePath;
}
/**
* 生成密文文件
*
* @param str 明文
* @param publicKey 接收方公钥
* @param privateKey 发送方私钥
* @return com.hb56.cipherclient.dto.Ciphertext
* @author luce
* @date 2021/9/2 15:46
*/
@Override
public Ciphertext createCiphertext(String str, BCECPublicKey publicKey, BCECPrivateKey privateKey) throws Exception {
Security.addProvider(new BouncyCastleProvider());
String localDateTime = LocalDateTime.now().toString();
//将报文转码 Base64
String encodeStr = Base64Encoder.encode(str.getBytes(StandardCharsets.UTF_8));
//将时间转码 Base64
String encodeTime = Base64Encoder.encode(localDateTime.getBytes(StandardCharsets.UTF_8));
//生成随机密钥
byte[] key = SM4Util.generateKey();
//加签:用发送方的私钥
String sigFile = sign(encodeStr, privateKey, encodeTime);
//加密:用随机 SM4key
String encodeCiphertext = encryptedDocument(key, encodeStr);
//加密随机密钥:用接收方的公钥
byte[] encryptsKey = SM2Util.encrypt(publicKey, key);
String encodeKey = Base64Encoder.encode(encryptsKey);
return new Ciphertext(sigFile, encodeKey, encodeCiphertext, localDateTime, null);
}
/**
* 解读密文
*
* @param ciphertext 密文
* @param publicKey 发送方公钥
* @param privateKey 接收方私钥
* @return 报文 -String
* @author luce
* @date 2021/9/3 9:53
*/
@Override
public String unscrambledCiphertext(Ciphertext ciphertext, BCECPublicKey publicKey, BCECPrivateKey privateKey) throws Exception {
Security.addProvider(new BouncyCastleProvider());
//解密
String encodeStr = decodeDocument(ciphertext, privateKey);
System.out.println("解密后得到的报文:" + encodeStr);
//在报文中加入发送时间一起验签
String signStr = encodeStr + Base64Encoder.encode(ciphertext.getTime().getBytes(StandardCharsets.UTF_8));
System.out.println("验签内容:" + signStr);
//验签
boolean result = SM2Util.verify(publicKey, signStr.getBytes(StandardCharsets.UTF_8), Base64Decoder.decode(ciphertext.getSignText()));
if (!result) {
throw new RuntimeException("验签失败!");
}
return new String(Base64Decoder.decode(encodeStr), StandardCharsets.UTF_8);
}
/**
* 读取证书
*
* @return java.lang.String
* @author luce
* @date 2021/9/3 10:09
*/
@Override
public String getCert() {
File file = new File(acquireJarPath() + "safe/certificate.pem");
long filelength = file.length();
byte[] filecontent = new byte[(int) filelength];
try {
FileInputStream in = new FileInputStream(file);
in.read(filecontent);
in.close();
} catch (IOException e) {
e.printStackTrace();
}
return new String(filecontent);
}
/**
* 读取私钥
*
* @return org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey
* @author luce
* @date 2021/9/3 13:23
*/
@Override
public BCECPrivateKey getPrivateKey() throws Exception {
String privateKey = FileUtil.readString(acquireJarPath() + "safe/private.key", CharsetUtil.CHARSET_UTF_8);
return BCECUtil.convertPKCS8ToECPrivateKey(BCECUtil.convertECPrivateKeyPEMToPKCS8(privateKey));
}
/**
* 获取jar包中的绝对路径
* @author luce
* @date 2021/9/7 15:24
* @return java.lang.String
*/
private String acquireJarPath() {
String path = this.getClass().getProtectionDomain().getCodeSource().getLocation().getFile();
try {
path = java.net.URLDecoder.decode(path, "UTF-8");
} catch (java.io.UnsupportedEncodingException ex) {
log.error(ex.getLocalizedMessage());
}
java.io.File jarFile = new java.io.File(path);
String jarFilepath = jarFile.getAbsolutePath();
int end = jarFilepath.indexOf("target\\");
String result = jarFilepath.substring(0, end + 7);
return result;
}
}
/**
* 工具类
*
* @author luce
* @date 2021年09月01日 13:40
*/
@Configuration
public class CipherTool {
public static final String BEGIN_CERT = "-----BEGIN CERTIFICATE-----";
public static final String END_CERT = "-----END CERTIFICATE-----";
/**
* 生成证书文件
*
* @param x509Certificate 证书
* @param certStoragePath 存储路径
* @author luce
* @date 2021/8/31 15:27
*/
public static void createCert(X509Certificate x509Certificate, String certStoragePath) throws CertificateEncodingException {
FileUtil.del(certStoragePath);
//在指定目录生成证书文件
FileUtil.newFile(certStoragePath);
//证书标题
FileUtil.appendUtf8String(BEGIN_CERT, certStoragePath);
//分割线
FileUtil.appendUtf8String(System.getProperty("line.separator"), certStoragePath);
//转Base64码的证书
FileUtil.appendUtf8String(Base64Encoder.encode(x509Certificate.getEncoded()), certStoragePath);
//分割线
FileUtil.appendUtf8String(System.getProperty("line.separator"), certStoragePath);
//尾文
FileUtil.appendUtf8String(END_CERT, certStoragePath);
}
/**
* 生成密钥文件
*
* @param keyPair 密钥对
* @param secretKeyStoragePath 存储路径
* @author luce
* @date 2021/8/31 15:30
*/
public static void createKeyPair(KeyPair keyPair, String secretKeyStoragePath) throws InvalidKeyException, IOException {
FileUtil.del(secretKeyStoragePath);
//生成私钥
ECPrivateKeyParameters bcecPrivateKey = (ECPrivateKeyParameters) ECUtil.generatePrivateKeyParameter(keyPair.getPrivate());
//生成公钥
ECPublicKeyParameters bcecPublicKey = (ECPublicKeyParameters) ECUtil.generatePublicKeyParameter(keyPair.getPublic());
//在指定目录生成密钥文件
FileUtil.newFile(secretKeyStoragePath);
//转PEM格式密钥
FileUtil.appendUtf8String(BCECUtil.convertECPrivateKeyPKCS8ToPEM(BCECUtil.convertECPrivateKeyToPKCS8(bcecPrivateKey, bcecPublicKey)), secretKeyStoragePath);
}
/**
* 从证书 Path 读取公钥
*
* @param certFilePath 证书文件路径
* @return org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey
* @author luce
* @date 2021/9/1 14:53
*/
public static BCECPublicKey getPublicKeyByPath(String certFilePath) throws Exception {
X509Certificate sendCert = SM2CertUtil.getX509Certificate(certFilePath);
return SM2CertUtil.getBCECPublicKey(sendCert);
}
/**
* 读取公钥
*
* @param receiveCert 证书
* @return org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey
* @author luce
* @date 2021/9/1 14:53
*/
public static BCECPublicKey getPublicKey(String receiveCert) throws Exception {
Security.addProvider(new BouncyCastleProvider());
X509Certificate sendCert = SM2CertUtil.getX509Certificate(new ByteArrayInputStream(receiveCert.getBytes(StandardCharsets.UTF_8)));
return SM2CertUtil.getBCECPublicKey(sendCert);
}
/**
* 从 Path 读取私钥
*
* @return org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey
* @author luce
* @date 2021/9/1 14:53
*/
public static BCECPrivateKey getPrivateKeyByPath(String privateKeyFilePath) throws Exception {
String privateKey = FileUtil.readString(privateKeyFilePath, CharsetUtil.CHARSET_UTF_8);
return BCECUtil.convertPKCS8ToECPrivateKey(BCECUtil.convertECPrivateKeyPEMToPKCS8(privateKey));
}
/**
* 加签:用发送方的私钥进行加签
*
* @param encodeStr Base64码报文
* @param sendPrivateKey 发送方私钥
* @param encodeTime 发送时间
* @return Base64格式签文 -String
* @author luce
* @date 2021/8/31 14:37
*/
public static String sign(String encodeStr, BCECPrivateKey sendPrivateKey, String encodeTime) throws CryptoException {
String signStr = encodeStr + encodeTime;
byte[] encodeSigFile = SM2Util.sign(sendPrivateKey, signStr.getBytes(StandardCharsets.UTF_8));
return Base64Encoder.encode(encodeSigFile);
}
/**
* 加密报文
*
* @param key SM4随机密钥
* @param encodeStr Base64格式 报文
* @return 密文 -String
* @author luce
* @date 2021/8/31 16:00
*/
public static String encryptedDocument(byte[] key, String encodeStr) throws Exception {
byte[] ciphertext = SM4Util.encrypt_ECB_Padding(key, encodeStr.getBytes(StandardCharsets.UTF_8));
return Base64Encoder.encode(ciphertext);
}
/**
* 解密报文
*
* @param ciphertext 加密文件
* @param bcecPrivateKey 接收方密钥
* @return 报文 -byte[]
* @author luce
* @date 2021/8/31 16:48
*/
public static String decodeDocument(Ciphertext ciphertext, BCECPrivateKey bcecPrivateKey) throws Exception {
//解码密文
byte[] decodeCiphertext = Base64Decoder.decode(ciphertext.getEncryptionFile());
//解密随机密钥
byte[] key = SM2Util.decrypt(SM2Engine.Mode.C1C3C2, bcecPrivateKey, Base64Decoder.decode(ciphertext.getEncryptionKey()));
//解密密文
byte[] encodeStr = SM4Util.decrypt_ECB_Padding(key, decodeCiphertext);
return new String(encodeStr, StandardCharsets.UTF_8);
}
}