非对称签名验签
This commit is contained in:
@@ -7,8 +7,11 @@ 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.service.AsymKeyService;
|
||||
@@ -85,5 +88,59 @@ public class AsymKeyController {
|
||||
return R.data(resp);
|
||||
}
|
||||
|
||||
/**
|
||||
* P7 Attach签名
|
||||
*
|
||||
* @param req
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/sign/P7Attach")
|
||||
@AuthCode(AuthCodeConst.sign_P7Attach)
|
||||
public R<AsymSignP7Resp> signP7Attach(@Valid @RequestBody AsymSignP7Req req) {
|
||||
AsymSignP7Resp resp = asymKeyService.signP7Attach(req);
|
||||
return R.data(resp);
|
||||
}
|
||||
|
||||
/**
|
||||
* P7 Attach验签
|
||||
*
|
||||
* @param req
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/verify/P7Attach")
|
||||
@AuthCode(AuthCodeConst.verify_P7Attach)
|
||||
public R<VerifyResp> verifyP7Attach(@Valid @RequestBody AsymVerifyP7Req req) {
|
||||
VerifyResp resp = asymKeyService.verifyP7Attach(req);
|
||||
return R.data(resp);
|
||||
}
|
||||
|
||||
/**
|
||||
* P7 Detach签名
|
||||
*
|
||||
* @param req
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/sign/P7Detach")
|
||||
@AuthCode(AuthCodeConst.sign_P7Detach)
|
||||
public R<AsymSignP7Resp> signP7Detach(@Valid @RequestBody AsymSignP7Req req) {
|
||||
AsymSignP7Resp resp = asymKeyService.signP7Detach(req);
|
||||
return R.data(resp);
|
||||
}
|
||||
|
||||
/**
|
||||
* P7 Detach验签
|
||||
*
|
||||
* @param req
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/verify/P7Detach")
|
||||
@AuthCode(AuthCodeConst.verify_P7Detach)
|
||||
public R<VerifyResp> verifyP7Detach(@Valid @RequestBody AsymVerifyP7Req req) {
|
||||
VerifyResp resp = asymKeyService.verifyP7Detach(req);
|
||||
return R.data(resp);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -5,37 +5,67 @@ 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.mapper.AppCertMapper;
|
||||
import com.sunyard.chsm.mapper.KeyInfoMapper;
|
||||
import com.sunyard.chsm.mapper.SpKeyRecordMapper;
|
||||
import com.sunyard.chsm.model.entity.AppCert;
|
||||
import com.sunyard.chsm.model.entity.KeyInfo;
|
||||
import com.sunyard.chsm.model.entity.KeyRecord;
|
||||
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.sdf.SdfApiService;
|
||||
import com.sunyard.chsm.sdf.model.EccCipher;
|
||||
import com.sunyard.chsm.sdf.model.EccSignature;
|
||||
import com.sunyard.chsm.utils.CodecUtils;
|
||||
import com.sunyard.chsm.utils.gm.BCECUtils;
|
||||
import com.sunyard.chsm.utils.gm.cert.BCSM2CertUtils;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.bouncycastle.cert.X509CertificateHolder;
|
||||
import org.bouncycastle.cert.jcajce.JcaCertStore;
|
||||
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
|
||||
import org.bouncycastle.cms.CMSProcessableByteArray;
|
||||
import org.bouncycastle.cms.CMSSignedData;
|
||||
import org.bouncycastle.cms.CMSSignedDataGenerator;
|
||||
import org.bouncycastle.cms.CMSTypedData;
|
||||
import org.bouncycastle.cms.SignerInfoGenerator;
|
||||
import org.bouncycastle.cms.SignerInformation;
|
||||
import org.bouncycastle.cms.SignerInformationStore;
|
||||
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
|
||||
import org.bouncycastle.cms.jcajce.JcaSignerInfoVerifierBuilder;
|
||||
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
|
||||
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
|
||||
import org.bouncycastle.util.Store;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author liulu
|
||||
* @since 2024/12/20
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class AsymKeyService {
|
||||
|
||||
private final AppCertMapper appCertMapper;
|
||||
private final KeyInfoMapper keyInfoMapper;
|
||||
private final SpKeyRecordMapper spKeyRecordMapper;
|
||||
private final SdfApiService sdfApiService;
|
||||
@@ -147,6 +177,118 @@ public class AsymKeyService {
|
||||
return resp;
|
||||
}
|
||||
|
||||
public AsymSignP7Resp signP7Attach(AsymSignP7Req req) {
|
||||
byte[] plainData = CodecUtils.decodeBase64(req.getPlainData());
|
||||
AppCert appCert = appCertMapper.selectSignBySubject(req.getSubject());
|
||||
Assert.notNull(appCert, "证书主题对应的证书不存在");
|
||||
Assert.isTrue(Objects.equals(appCert.getApplicationId(), UserContext.getCurrentAppId()), "您无权使用此密钥ID");
|
||||
byte[] encPri = CodecUtils.decodeHex(appCert.getEncPriKey());
|
||||
byte[] pri = sdfApiService.decryptByTMK(encPri);
|
||||
|
||||
byte[] asymSignP7Resp = p7Sign(pri, appCert.getCertText(), plainData, true);
|
||||
AsymSignP7Resp resp = new AsymSignP7Resp();
|
||||
resp.setSignData(CodecUtils.encodeBase64(asymSignP7Resp));
|
||||
return resp;
|
||||
}
|
||||
|
||||
public VerifyResp verifyP7Attach(AsymVerifyP7Req req) {
|
||||
byte[] signData = CodecUtils.decodeBase64(req.getSignData());
|
||||
boolean verify;
|
||||
try {
|
||||
verify = p7Verify(signData, null);
|
||||
|
||||
} catch (Exception e) {
|
||||
verify = false;
|
||||
}
|
||||
VerifyResp resp = new VerifyResp();
|
||||
resp.setVerified(verify);
|
||||
return resp;
|
||||
}
|
||||
|
||||
public AsymSignP7Resp signP7Detach(AsymSignP7Req req) {
|
||||
byte[] plainData = CodecUtils.decodeBase64(req.getPlainData());
|
||||
AppCert appCert = appCertMapper.selectSignBySubject(req.getSubject());
|
||||
Assert.notNull(appCert, "证书主题对应的证书不存在");
|
||||
Assert.isTrue(Objects.equals(appCert.getApplicationId(), UserContext.getCurrentAppId()), "您无权使用此密钥ID");
|
||||
byte[] encPri = CodecUtils.decodeHex(appCert.getEncPriKey());
|
||||
byte[] pri = sdfApiService.decryptByTMK(encPri);
|
||||
|
||||
byte[] asymSignP7Resp = p7Sign(pri, appCert.getCertText(), plainData, false);
|
||||
AsymSignP7Resp resp = new AsymSignP7Resp();
|
||||
resp.setSignData(CodecUtils.encodeBase64(asymSignP7Resp));
|
||||
return resp;
|
||||
}
|
||||
|
||||
public VerifyResp verifyP7Detach(AsymVerifyP7Req req) {
|
||||
Assert.hasText(req.getPlainData(), "P7Detach验签原文不能为空");
|
||||
byte[] signData = CodecUtils.decodeBase64(req.getSignData());
|
||||
byte[] plainData = CodecUtils.decodeBase64(req.getPlainData());
|
||||
boolean verify;
|
||||
try {
|
||||
verify = p7Verify(signData, plainData);
|
||||
|
||||
} catch (Exception e) {
|
||||
verify = false;
|
||||
}
|
||||
VerifyResp resp = new VerifyResp();
|
||||
resp.setVerified(verify);
|
||||
return resp;
|
||||
}
|
||||
|
||||
private static 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);
|
||||
// 生成签名者信息
|
||||
SignerInfoGenerator signerInfoGenerator = new JcaSignerInfoGeneratorBuilder(
|
||||
new JcaDigestCalculatorProviderBuilder().setProvider(BouncyCastleProvider.PROVIDER_NAME).build()
|
||||
).build(
|
||||
new JcaContentSignerBuilder("SM3withSM2").setProvider(BouncyCastleProvider.PROVIDER_NAME).build(privateKey),
|
||||
x509Cert
|
||||
);
|
||||
// 构建 CMS Signed Data
|
||||
CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
|
||||
generator.addSignerInfoGenerator(signerInfoGenerator);
|
||||
generator.addCertificates(new JcaCertStore(Collections.singletonList(x509Cert)));
|
||||
CMSSignedData signedData = generator.generate(cmsData, encapsulate);
|
||||
return signedData.getEncoded();
|
||||
} catch (Exception ex) {
|
||||
log.warn("", ex);
|
||||
throw new IllegalArgumentException("P7Attach 签名异常");
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean p7Verify(byte[] signedDataBytes, byte[] originalData) throws Exception {
|
||||
|
||||
CMSSignedData signedData;
|
||||
if (originalData == null || originalData.length == 0) {
|
||||
signedData = new CMSSignedData(signedDataBytes);
|
||||
} else {
|
||||
CMSTypedData originalContent = new CMSProcessableByteArray(originalData);
|
||||
signedData = new CMSSignedData(originalContent, signedDataBytes);
|
||||
}
|
||||
|
||||
Store<X509CertificateHolder> certStore = signedData.getCertificates();
|
||||
SignerInformationStore signers = signedData.getSignerInfos();
|
||||
|
||||
for (SignerInformation signer : signers.getSigners()) {
|
||||
Collection<X509CertificateHolder> matches = certStore.getMatches(signer.getSID());
|
||||
if (matches.isEmpty()) {
|
||||
throw new IllegalArgumentException("No matching certificate found for signer");
|
||||
}
|
||||
X509CertificateHolder certHolder = matches.iterator().next(); // 这里进行类型安全的提取
|
||||
X509Certificate cert = new JcaX509CertificateConverter()
|
||||
.setProvider(BouncyCastleProvider.PROVIDER_NAME)
|
||||
.getCertificate(certHolder);
|
||||
if (signer.verify(new JcaSignerInfoVerifierBuilder(
|
||||
new JcaDigestCalculatorProviderBuilder().setProvider(BouncyCastleProvider.PROVIDER_NAME).build()).build(cert))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private KeyInfo checkKey(Long keyId, KeyUsage usage) {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user