数字信封加密和解密
This commit is contained in:
parent
17bc552dc5
commit
da77d42f89
@ -43,5 +43,18 @@ public interface AppCertMapper extends BaseMapper<AppCert> {
|
||||
return certs.iterator().next();
|
||||
}
|
||||
|
||||
default AppCert selectEncBySubject(String dn) {
|
||||
Assert.hasText(dn, "证书序列号不能为空");
|
||||
String subject = Subject.fromDN(dn).getDN();
|
||||
List<AppCert> certs = selectList(new LambdaQueryWrapper<AppCert>()
|
||||
.eq(AppCert::getSubject, subject)
|
||||
.eq(AppCert::getCertType, KeyUsage.ENCRYPT_DECRYPT.getCode())
|
||||
);
|
||||
if (CollectionUtils.isEmpty(certs)) {
|
||||
return null;
|
||||
}
|
||||
return certs.iterator().next();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,20 @@
|
||||
package com.sunyard.chsm.param;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
@Data
|
||||
public class AsymEnvelopeSealReq {
|
||||
|
||||
/**
|
||||
* 证书主题
|
||||
*/
|
||||
@NotBlank(message = "证书主题不能为空")
|
||||
private String subject;
|
||||
|
||||
// 明文,使用Base64编码
|
||||
@NotBlank(message = "明文不能为空")
|
||||
private String plainData;
|
||||
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package com.sunyard.chsm.param;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class AsymEnvelopeSealResp {
|
||||
|
||||
// 数字信封。使用Base64编码
|
||||
private String envelopeData;
|
||||
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package com.sunyard.chsm.param;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
@Data
|
||||
public class AsymEnvelopeUnsealReq {
|
||||
|
||||
/**
|
||||
* 加密证书的主题
|
||||
*/
|
||||
// @NotBlank(message = "加密证书不能为空")
|
||||
// private String subject;
|
||||
|
||||
// 数字信封。使用Base64编码
|
||||
@NotBlank(message = "数字信封不能为空")
|
||||
private String envelopeData;
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package com.sunyard.chsm.param;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class AsymEnvelopeUnsealResp {
|
||||
|
||||
|
||||
// 明文,使用Base64编码
|
||||
private String plainData;
|
||||
|
||||
}
|
@ -14,10 +14,10 @@ public class AsymVerifyP7Req {
|
||||
// private String subject;
|
||||
|
||||
// 原文,使用Base64编码
|
||||
@NotBlank(message = "明文不能为空")
|
||||
private String plainData;
|
||||
|
||||
// 签名值, 使用Base64编码
|
||||
@NotBlank(message = "签名值不能为空")
|
||||
private String signData;
|
||||
|
||||
}
|
||||
|
@ -25,12 +25,6 @@
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.sunyard.chsm</groupId>
|
||||
<artifactId>chsm-web-manage</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.dm</groupId>
|
||||
<artifactId>DmJdbcDriver</artifactId>
|
||||
|
@ -3,17 +3,7 @@ package com.sunyard.chsm.controller;
|
||||
import com.sunyard.chsm.auth.AuthCode;
|
||||
import com.sunyard.chsm.constant.AuthCodeConst;
|
||||
import com.sunyard.chsm.model.R;
|
||||
import com.sunyard.chsm.param.AsymDecryptReq;
|
||||
import com.sunyard.chsm.param.AsymDecryptResp;
|
||||
import com.sunyard.chsm.param.AsymEncryptReq;
|
||||
import com.sunyard.chsm.param.AsymEncryptResp;
|
||||
import com.sunyard.chsm.param.AsymSignP7Req;
|
||||
import com.sunyard.chsm.param.AsymSignP7Resp;
|
||||
import com.sunyard.chsm.param.AsymSignRawReq;
|
||||
import com.sunyard.chsm.param.AsymSignRawResp;
|
||||
import com.sunyard.chsm.param.AsymVerifyP7Req;
|
||||
import com.sunyard.chsm.param.AsymVerifyRawReq;
|
||||
import com.sunyard.chsm.param.VerifyResp;
|
||||
import com.sunyard.chsm.param.*;
|
||||
import com.sunyard.chsm.service.AsymKeyService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
@ -55,7 +45,7 @@ public class AsymKeyController {
|
||||
* @param req
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/encrypt")
|
||||
@PostMapping("/decrypt")
|
||||
@AuthCode(AuthCodeConst.asym_enc)
|
||||
public R<AsymDecryptResp> decrypt(@Valid @RequestBody AsymDecryptReq req) {
|
||||
AsymDecryptResp resp = asymKeyService.decrypt(req);
|
||||
@ -140,7 +130,31 @@ public class AsymKeyController {
|
||||
return R.data(resp);
|
||||
}
|
||||
|
||||
/**
|
||||
* P7数字信封加封
|
||||
*
|
||||
* @param req
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/envelope/seal")
|
||||
@AuthCode(AuthCodeConst.envelope_seal)
|
||||
public R<AsymEnvelopeSealResp> envelopeSeal(@Valid @RequestBody AsymEnvelopeSealReq req) {
|
||||
AsymEnvelopeSealResp resp = asymKeyService.envelopeSeal(req);
|
||||
return R.data(resp);
|
||||
}
|
||||
|
||||
/**
|
||||
* P7数字信封解封
|
||||
*
|
||||
* @param req
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/envelope/unseal")
|
||||
@AuthCode(AuthCodeConst.envelope_unseal)
|
||||
public R<AsymEnvelopeUnsealResp> envelopeUnseal(@Valid @RequestBody AsymEnvelopeUnsealReq req) {
|
||||
AsymEnvelopeUnsealResp resp = asymKeyService.envelopeUnseal(req);
|
||||
return R.data(resp);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
package com.sunyard.chsm.controller;
|
||||
|
||||
|
||||
import com.sunyard.chsm.constant.AuditLogConst;
|
||||
import com.sunyard.chsm.model.R;
|
||||
import com.sunyard.chsm.model.dto.CertDTO;
|
||||
import com.sunyard.chsm.param.*;
|
||||
import com.sunyard.chsm.param.CertExinfoResp;
|
||||
import com.sunyard.chsm.param.CertInfoResp;
|
||||
import com.sunyard.chsm.service.AppCertService;
|
||||
import com.sunyard.chsm.service.CertService;
|
||||
import com.sunyard.ssp.common.annotation.AuditControllerLog;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
|
||||
@ -84,7 +84,6 @@ public class CertController {
|
||||
* @return void
|
||||
*/
|
||||
@DeleteMapping
|
||||
@AuditControllerLog(description = "删除证书", operateType = AuditLogConst.DELETE)
|
||||
public R<Void> delete(Long id) {
|
||||
appCertService.delete(id);
|
||||
return R.ok();
|
||||
|
@ -5,11 +5,7 @@ import com.sunyard.chsm.enums.AlgMode;
|
||||
import com.sunyard.chsm.enums.Padding;
|
||||
import com.sunyard.chsm.sdf.SdfApiService;
|
||||
import com.sunyard.chsm.sdf.context.AlgId;
|
||||
import com.sunyard.chsm.sdf.model.EccCipher;
|
||||
import com.sunyard.chsm.sdf.model.EccKey;
|
||||
import com.sunyard.chsm.sdf.model.EccPriKey;
|
||||
import com.sunyard.chsm.sdf.model.EccPubKey;
|
||||
import com.sunyard.chsm.sdf.model.EccSignature;
|
||||
import com.sunyard.chsm.sdf.model.*;
|
||||
import com.sunyard.chsm.sdf.util.PaddingUtil;
|
||||
import com.sunyard.chsm.utils.CodecUtils;
|
||||
import com.sunyard.chsm.utils.gm.BCECUtils;
|
||||
@ -57,13 +53,6 @@ public class LoadBalancedSdfApiService implements SdfApiService {
|
||||
Assert.notNull(iv, "CBC模式下, iv不能为空");
|
||||
Assert.isTrue(iv.length >= key.length, "CBC模式下, iv长度不能低于key的长度");
|
||||
}
|
||||
log.info("symEncrypt: alg:{}, padding: {}, key: {}, iv:{}, plainData:{}",
|
||||
alg.name(),
|
||||
padding.getCode(),
|
||||
CodecUtils.encodeHex(key),
|
||||
CodecUtils.encodeHex(iv),
|
||||
CodecUtils.encodeHex(data)
|
||||
);
|
||||
byte[] paddingData = PaddingUtil.padding(padding, data);
|
||||
return apply(s -> {
|
||||
String keyHandle = s.getSdfApiAdapter().importKey(s.getSessionHandle(), key);
|
||||
|
@ -1,10 +1,7 @@
|
||||
package com.sunyard.chsm.service;
|
||||
|
||||
import com.sunyard.chsm.auth.UserContext;
|
||||
import com.sunyard.chsm.enums.KeyAlg;
|
||||
import com.sunyard.chsm.enums.KeyCategory;
|
||||
import com.sunyard.chsm.enums.KeyStatus;
|
||||
import com.sunyard.chsm.enums.KeyUsage;
|
||||
import com.sunyard.chsm.enums.*;
|
||||
import com.sunyard.chsm.mapper.AppCertMapper;
|
||||
import com.sunyard.chsm.mapper.KeyInfoMapper;
|
||||
import com.sunyard.chsm.mapper.SpKeyRecordMapper;
|
||||
@ -13,20 +10,16 @@ import com.sunyard.chsm.model.entity.KeyInfo;
|
||||
import com.sunyard.chsm.model.entity.KeyRecord;
|
||||
import com.sunyard.chsm.param.*;
|
||||
import com.sunyard.chsm.sdf.SdfApiService;
|
||||
import com.sunyard.chsm.sdf.context.AlgId;
|
||||
import com.sunyard.chsm.sdf.model.EccCipher;
|
||||
import com.sunyard.chsm.sdf.model.EccSignature;
|
||||
import com.sunyard.chsm.sdf.util.LangUtils;
|
||||
import com.sunyard.chsm.utils.CodecUtils;
|
||||
import com.sunyard.chsm.utils.gm.BCECUtils;
|
||||
import com.sunyard.chsm.utils.gm.BCSM2Utils;
|
||||
import com.sunyard.chsm.utils.gm.BCSM4Utils;
|
||||
import com.sunyard.chsm.utils.gm.cert.BCSM2CertUtils;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
|
||||
import org.bouncycastle.asn1.DERNull;
|
||||
import org.bouncycastle.asn1.DEROctetString;
|
||||
import org.bouncycastle.asn1.DERSet;
|
||||
import org.bouncycastle.asn1.*;
|
||||
import org.bouncycastle.asn1.cms.*;
|
||||
import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
|
||||
import org.bouncycastle.asn1.x500.X500Name;
|
||||
@ -34,26 +27,20 @@ import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
|
||||
import org.bouncycastle.cert.X509CertificateHolder;
|
||||
import org.bouncycastle.cert.jcajce.JcaCertStore;
|
||||
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
|
||||
import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
|
||||
import org.bouncycastle.cms.*;
|
||||
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
|
||||
import org.bouncycastle.cms.jcajce.JcaSignerInfoVerifierBuilder;
|
||||
import org.bouncycastle.cms.jcajce.JceKeyTransEnvelopedRecipient;
|
||||
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
|
||||
import org.bouncycastle.jcajce.io.CipherInputStream;
|
||||
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
|
||||
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.bouncycastle.operator.InputDecryptor;
|
||||
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
|
||||
import org.bouncycastle.operator.*;
|
||||
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
|
||||
import org.bouncycastle.util.Store;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.io.InputStream;
|
||||
import java.security.Key;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.security.cert.CertificateEncodingException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
@ -213,7 +200,7 @@ public class AsymKeyService {
|
||||
byte[] plainData = CodecUtils.decodeBase64(req.getPlainData());
|
||||
AppCert appCert = appCertMapper.selectSignBySubject(req.getSubject());
|
||||
Assert.notNull(appCert, "证书主题对应的证书不存在");
|
||||
Assert.isTrue(Objects.equals(appCert.getApplicationId(), UserContext.getCurrentAppId()), "您无权使用此密钥ID");
|
||||
Assert.isTrue(Objects.equals(appCert.getApplicationId(), UserContext.getCurrentAppId()), "您无权使用此证书");
|
||||
byte[] encPri = CodecUtils.decodeHex(appCert.getEncPriKey());
|
||||
byte[] pri = sdfApiService.decryptByTMK(encPri);
|
||||
|
||||
@ -241,17 +228,34 @@ public class AsymKeyService {
|
||||
|
||||
private byte[] p7Sign(byte[] pri, String cert, byte[] plainData, boolean encapsulate) {
|
||||
try {
|
||||
BCECPrivateKey privateKey = BCECUtils.createPrivateKey(pri);
|
||||
X509Certificate x509Cert = BCSM2CertUtils.getX509Cert(cert);
|
||||
// 构造签名内容
|
||||
CMSTypedData cmsData = new CMSProcessableByteArray(plainData);
|
||||
|
||||
ContentSigner signer = new ContentSigner() {
|
||||
private final ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
|
||||
@Override
|
||||
public AlgorithmIdentifier getAlgorithmIdentifier() {
|
||||
return new AlgorithmIdentifier(GMObjectIdentifiers.sm2sign_with_sm3);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream getOutputStream() {
|
||||
return stream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getSignature() {
|
||||
EccSignature signature = sdfApiService.externalSignWithIdECC(pri, stream.toByteArray(), null);
|
||||
return signature.getDerSignBytes();
|
||||
}
|
||||
};
|
||||
|
||||
// 生成签名者信息
|
||||
SignerInfoGenerator signerInfoGenerator = new JcaSignerInfoGeneratorBuilder(
|
||||
new JcaDigestCalculatorProviderBuilder().setProvider(BouncyCastleProvider.PROVIDER_NAME).build()
|
||||
).build(
|
||||
new JcaContentSignerBuilder("SM3withSM2").setProvider(BouncyCastleProvider.PROVIDER_NAME).build(privateKey),
|
||||
x509Cert
|
||||
);
|
||||
).build(signer, x509Cert);
|
||||
// 构建 CMS Signed Data
|
||||
CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
|
||||
generator.addSignerInfoGenerator(signerInfoGenerator);
|
||||
@ -264,7 +268,7 @@ public class AsymKeyService {
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean p7Verify(byte[] signedDataBytes, byte[] originalData) throws Exception {
|
||||
public boolean p7Verify(byte[] signedDataBytes, byte[] originalData) throws Exception {
|
||||
|
||||
CMSSignedData signedData;
|
||||
if (originalData == null || originalData.length == 0) {
|
||||
@ -286,24 +290,86 @@ public class AsymKeyService {
|
||||
X509Certificate cert = new JcaX509CertificateConverter()
|
||||
.setProvider(BouncyCastleProvider.PROVIDER_NAME)
|
||||
.getCertificate(certHolder);
|
||||
if (signer.verify(new JcaSignerInfoVerifierBuilder(
|
||||
new JcaDigestCalculatorProviderBuilder().setProvider(BouncyCastleProvider.PROVIDER_NAME).build()).build(cert))) {
|
||||
CMSSignatureAlgorithmNameGenerator sigAlgNameGen = new DefaultCMSSignatureAlgorithmNameGenerator();
|
||||
SignatureAlgorithmIdentifierFinder sigAlgIDFinder = new DefaultSignatureAlgorithmIdentifierFinder();
|
||||
DigestCalculatorProvider digestProvider = new JcaDigestCalculatorProviderBuilder().setProvider(BouncyCastleProvider.PROVIDER_NAME).build();
|
||||
SignerInformationVerifier verifier = new SignerInformationVerifier(sigAlgNameGen, sigAlgIDFinder, build(cert), digestProvider);
|
||||
|
||||
if (signer.verify(verifier)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public ContentVerifierProvider build(X509Certificate certificate)
|
||||
throws OperatorCreationException {
|
||||
X509CertificateHolder certHolder;
|
||||
byte[] xy;
|
||||
try {
|
||||
certHolder = new JcaX509CertificateHolder(certificate);
|
||||
BCECPublicKey publicKey = (BCECPublicKey) certificate.getPublicKey();
|
||||
xy = LangUtils.merge(publicKey.getQ().getXCoord().getEncoded(), publicKey.getQ().getYCoord().getEncoded());
|
||||
} catch (CertificateEncodingException e) {
|
||||
throw new OperatorCreationException("cannot process certificate: " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
return new ContentVerifierProvider() {
|
||||
public boolean hasAssociatedCertificate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public X509CertificateHolder getAssociatedCertificate() {
|
||||
return certHolder;
|
||||
}
|
||||
|
||||
public ContentVerifier get(AlgorithmIdentifier algorithm) {
|
||||
return new ContentVerifier() {
|
||||
private final ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
|
||||
@Override
|
||||
public AlgorithmIdentifier getAlgorithmIdentifier() {
|
||||
return algorithm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream getOutputStream() {
|
||||
return stream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verify(byte[] expected) {
|
||||
return sdfApiService.externalVerifyWithIdECC(xy, stream.toByteArray(), expected, null);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private byte[] makeEnvelopeData(byte[] srcMsg, byte[] certData) {
|
||||
X509Certificate cert = BCSM2CertUtils.getX509Certificate(certData);
|
||||
public AsymEnvelopeSealResp envelopeSeal(AsymEnvelopeSealReq req) {
|
||||
byte[] plainData = CodecUtils.decodeBase64(req.getPlainData());
|
||||
AppCert appCert = appCertMapper.selectEncBySubject(req.getSubject());
|
||||
Assert.notNull(appCert, "证书主题对应的证书不存在");
|
||||
Assert.isTrue(Objects.equals(appCert.getApplicationId(), UserContext.getCurrentAppId()), "您无权使用此证书");
|
||||
|
||||
byte[] envelopeData = makeEnvelopeData(plainData, appCert.getCertText());
|
||||
AsymEnvelopeSealResp resp = new AsymEnvelopeSealResp();
|
||||
resp.setEnvelopeData(CodecUtils.encodeBase64(envelopeData));
|
||||
return resp;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private byte[] makeEnvelopeData(byte[] srcMsg, String certText) {
|
||||
X509Certificate cert = BCSM2CertUtils.getX509Cert(certText);
|
||||
AlgorithmIdentifier symAlg = new AlgorithmIdentifier(GMObjectIdentifiers.sm_scheme.branch("104")); // sm4
|
||||
AlgorithmIdentifier asymAlg = new AlgorithmIdentifier(GMObjectIdentifiers.sm2encrypt, DERNull.INSTANCE);
|
||||
|
||||
byte[] symKey = sdfApiService.generateRandom(16);
|
||||
|
||||
BCECPublicKey publicKey = (BCECPublicKey) cert.getPublicKey();
|
||||
byte[] encryptKey = BCSM2Utils.encrypt(publicKey, symKey);
|
||||
byte[] xy = LangUtils.merge(publicKey.getQ().getXCoord().getEncoded(), publicKey.getQ().getYCoord().getEncoded());
|
||||
EccCipher cipher = sdfApiService.externalEncryptECC(xy, symKey);
|
||||
byte[] encryptKey = cipher.getC1C3C2Bytes();
|
||||
|
||||
X500Name x500Name = X500Name.getInstance(cert.getIssuerX500Principal().getEncoded());
|
||||
KeyTransRecipientInfo keyTransRecipientInfo = new KeyTransRecipientInfo(
|
||||
@ -311,64 +377,47 @@ public class AsymKeyService {
|
||||
asymAlg,
|
||||
new DEROctetString(encryptKey)
|
||||
);
|
||||
|
||||
byte[] encContent = BCSM4Utils.encrypt_ECB_Padding(symKey, srcMsg);
|
||||
RecipientInfo recipientInfo = new RecipientInfo(keyTransRecipientInfo);
|
||||
|
||||
byte[] encContent = sdfApiService.symEncrypt(AlgId.SGD_SM4_ECB, Padding.PCKS7Padding, symKey, null, srcMsg);
|
||||
EncryptedContentInfo encContentInfo = new EncryptedContentInfo(
|
||||
new ASN1ObjectIdentifier("1.2.156.10197.6.1.4.2.1"),
|
||||
symAlg,
|
||||
new DEROctetString(encContent)
|
||||
);
|
||||
EnvelopedData envelopedData = new EnvelopedData(null,
|
||||
|
||||
EnvelopedData contentInfo = new EnvelopedData(null,
|
||||
new DERSet(recipientInfo),
|
||||
encContentInfo,
|
||||
(org.bouncycastle.asn1.ASN1Set) null);
|
||||
|
||||
ContentInfo contentInfo = new ContentInfo(new ASN1ObjectIdentifier("1.2.156.10197.6.1.4.2.3"), envelopedData);
|
||||
return contentInfo.getEncoded("DER");
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private byte[] decryptEnvelopeData(byte[] envelopeData, byte[] priKeyD) {
|
||||
ECPrivateKeyParameters priKeyParameters = BCECUtils.createECPrivateKeyParameters(priKeyD);
|
||||
final BCECPrivateKey priKey = new BCECPrivateKey("EC", priKeyParameters, BouncyCastleProvider.CONFIGURATION);
|
||||
|
||||
public AsymEnvelopeUnsealResp envelopeUnseal(AsymEnvelopeUnsealReq req) {
|
||||
byte[] envelopeData = CodecUtils.decodeBase64(req.getEnvelopeData());
|
||||
// 解密数字信封
|
||||
CMSEnvelopedData ed = new CMSEnvelopedData(envelopeData);
|
||||
RecipientInformationStore recipientInfos = ed.getRecipientInfos();
|
||||
Collection<RecipientInformation> recipients = recipientInfos.getRecipients();
|
||||
RecipientInformation next = recipients.iterator().next();
|
||||
EnvelopedData ed = EnvelopedData.getInstance(envelopeData);
|
||||
ASN1Set infos = ed.getRecipientInfos();
|
||||
RecipientInfo recipientInfo = RecipientInfo.getInstance(infos.getObjectAt(0));
|
||||
KeyTransRecipientInfo transRecipientInfo = KeyTransRecipientInfo.getInstance(recipientInfo.getInfo());
|
||||
IssuerAndSerialNumber serialNumber = IssuerAndSerialNumber.getInstance(transRecipientInfo.getRecipientIdentifier().getId());
|
||||
String sn = serialNumber.getSerialNumber().getValue().toString();
|
||||
AppCert appCert = appCertMapper.selectBySN(sn);
|
||||
Assert.notNull(appCert, "此信封对应的证书不存在");
|
||||
Assert.isTrue(Objects.equals(appCert.getApplicationId(), UserContext.getCurrentAppId()), "您无权使用此信封的解密证书");
|
||||
byte[] pri = sdfApiService.decryptByTMK(CodecUtils.decodeHex(appCert.getEncPriKey()));
|
||||
byte[] encryptedKey = transRecipientInfo.getEncryptedKey().getOctets();
|
||||
byte[] symKey = sdfApiService.externalDecryptECC(pri, encryptedKey);
|
||||
|
||||
return next.getContent(new JceKeyTransEnvelopedRecipient(priKey) {
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public RecipientOperator getRecipientOperator(AlgorithmIdentifier keyEncAlg,
|
||||
AlgorithmIdentifier contentEncryptionAlgorithm,
|
||||
byte[] encryptedContentKey) {
|
||||
String keyEncId = keyEncAlg.getAlgorithm().getId();
|
||||
if (Objects.equals(GMObjectIdentifiers.sm2encrypt.getId(), keyEncId)) {
|
||||
byte[] keyBytes = BCSM2Utils.decrypt(priKeyParameters, encryptedContentKey);
|
||||
Cipher cipher = Cipher.getInstance("SM4/ECB/PKCS7Padding", BouncyCastleProvider.PROVIDER_NAME);
|
||||
Key sm4Key = new SecretKeySpec(keyBytes, "SM4");
|
||||
cipher.init(Cipher.DECRYPT_MODE, sm4Key);
|
||||
return new RecipientOperator(new InputDecryptor() {
|
||||
@Override
|
||||
public AlgorithmIdentifier getAlgorithmIdentifier() {
|
||||
return contentEncryptionAlgorithm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream(InputStream dataIn) {
|
||||
return new CipherInputStream(dataIn, cipher);
|
||||
}
|
||||
});
|
||||
}
|
||||
return super.getRecipientOperator(keyEncAlg, contentEncryptionAlgorithm, encryptedContentKey);
|
||||
}
|
||||
}.setProvider(BouncyCastleProvider.PROVIDER_NAME));
|
||||
EncryptedContentInfo info = ed.getEncryptedContentInfo();
|
||||
byte[] octets = info.getEncryptedContent().getOctets();
|
||||
byte[] plain = sdfApiService.symDecrypt(AlgId.SGD_SM4_ECB, Padding.PCKS7Padding, symKey, null, octets);
|
||||
AsymEnvelopeUnsealResp resp = new AsymEnvelopeUnsealResp();
|
||||
resp.setPlainData(CodecUtils.encodeBase64(plain));
|
||||
return resp;
|
||||
}
|
||||
|
||||
|
||||
private KeyInfo checkKey(Long keyId, KeyUsage usage) {
|
||||
|
||||
KeyInfo keyInfo = keyInfoMapper.selectById(keyId);
|
||||
@ -385,4 +434,5 @@ public class AsymKeyService {
|
||||
return keyInfo;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
57
chsm-web-server/src/test/java/api/AsymKeyTest.java
Normal file
57
chsm-web-server/src/test/java/api/AsymKeyTest.java
Normal file
@ -0,0 +1,57 @@
|
||||
package api;
|
||||
|
||||
import com.sunyard.chsm.param.*;
|
||||
import com.sunyard.chsm.utils.CodecUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* @author liulu
|
||||
* @since 2024/12/24
|
||||
*/
|
||||
@Slf4j
|
||||
public class AsymKeyTest extends BaseTest {
|
||||
|
||||
private static Long keyId;
|
||||
private static final byte[] plain = "hjsu234127qikqwndqqw13412as324".getBytes();
|
||||
private static final Long certKeyId = 1871443220005818369L;
|
||||
private static final String dn = "CN=cert-test,O=SYD,L=HZ,ST=ZJ,C=CN";
|
||||
|
||||
|
||||
@Test
|
||||
public void testAttach() {
|
||||
AsymSignP7Req signP7Req = new AsymSignP7Req();
|
||||
signP7Req.setSubject(dn);
|
||||
signP7Req.setPlainData(CodecUtils.encodeBase64(plain));
|
||||
AsymSignP7Resp signP7Resp = execute("/asym/sign/P7Attach", signP7Req, AsymSignP7Resp.class);
|
||||
log.info("signP7: {}", signP7Resp.getSignData());
|
||||
|
||||
AsymVerifyP7Req verifyP7Req = new AsymVerifyP7Req();
|
||||
verifyP7Req.setSignData(signP7Resp.getSignData());
|
||||
VerifyResp verifyResp = execute("/asym/verify/P7Attach", verifyP7Req, VerifyResp.class);
|
||||
log.info("verifyResp: {}", verifyResp.getVerified());
|
||||
|
||||
Assertions.assertTrue(verifyResp.getVerified());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEnvelopedData() {
|
||||
AsymEnvelopeSealReq sealReq = new AsymEnvelopeSealReq();
|
||||
sealReq.setSubject(dn);
|
||||
sealReq.setPlainData(CodecUtils.encodeBase64(plain));
|
||||
AsymEnvelopeSealResp sealResp = execute("/asym/envelope/seal", sealReq, AsymEnvelopeSealResp.class);
|
||||
log.info("signP7: {}", sealResp.getEnvelopeData());
|
||||
|
||||
AsymEnvelopeUnsealReq unsealReq = new AsymEnvelopeUnsealReq();
|
||||
unsealReq.setEnvelopeData(sealResp.getEnvelopeData());
|
||||
AsymEnvelopeUnsealResp unsealResp = execute("/asym/envelope/unseal", unsealReq, AsymEnvelopeUnsealResp.class);
|
||||
log.info("verifyResp: {}", unsealResp.getPlainData());
|
||||
log.info("verifyResp: {}", new String(CodecUtils.decodeBase64(unsealResp.getPlainData())));
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user