实现有差异

This commit is contained in:
cheney 2026-04-29 17:00:46 +08:00
parent 38909af664
commit 133363ef18
6 changed files with 428 additions and 1 deletions

View File

@ -15,6 +15,7 @@ public interface PcieNativeLibrary extends Library {
int SDF_CloseSession(Pointer hSessionHandle); int SDF_CloseSession(Pointer hSessionHandle);
int SDF_GetPrivateKeyAccessRight(Pointer hSessionHandle, int uiKeyIndex, byte[] pucPassword, int uiPwdLength); int SDF_GetPrivateKeyAccessRight(Pointer hSessionHandle, int uiKeyIndex, byte[] pucPassword, int uiPwdLength);
int SDF_ReleasePrivateKeyAccessRight(Pointer hSessionHandle, int uiKeyIndex); int SDF_ReleasePrivateKeyAccessRight(Pointer hSessionHandle, int uiKeyIndex);

View File

@ -58,7 +58,8 @@ import org.springframework.stereotype.Service;
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
public class AuthServiceImpl implements AuthService { public class
AuthServiceImpl implements AuthService {
private static final int MAX_FAILED_ATTEMPTS = 5; private static final int MAX_FAILED_ATTEMPTS = 5;
private static final int IDLE_TIMEOUT_MINUTES = 10; private static final int IDLE_TIMEOUT_MINUTES = 10;

View File

@ -0,0 +1,9 @@
package com.cisd.tms.modules.openapi.service;
public interface IOpenApiService {
public String rawSign(byte[] origBytes, String dn);
public boolean rawVerify(byte[] origBytes, String certStr, String dn);
public String dettachedSign(byte[] origBytes, String dn);
public String dettachedVerify(byte[] origBytes, String certStr);
public String dettachedVerifySimple(byte[] origBytes, String certStr);
}

View File

@ -0,0 +1,64 @@
package com.cisd.tms.modules.openapi.service.dto;
import java.security.cert.X509Certificate;
/**
* 通用证书类
*/
public class GenericCertificate {
private X509Certificate certificate;
private String dn;
private String issuer;
private String serialNumber;
public GenericCertificate(X509Certificate certificate) {
this.certificate = certificate;
if (certificate != null) {
this.dn = certificate.getSubjectDN().getName();
this.issuer = certificate.getIssuerDN().getName();
this.serialNumber = certificate.getSerialNumber().toString();
}
}
public X509Certificate getCertificate() {
return certificate;
}
public void setCertificate(X509Certificate certificate) {
this.certificate = certificate;
}
public String getDn() {
return dn;
}
public void setDn(String dn) {
this.dn = dn;
}
public String getIssuer() {
return issuer;
}
public void setIssuer(String issuer) {
this.issuer = issuer;
}
public String getSerialNumber() {
return serialNumber;
}
public void setSerialNumber(String serialNumber) {
this.serialNumber = serialNumber;
}
@Override
public String toString() {
return "GenericCertificate{" +
"dn='" + dn + '\'' +
", issuer='" + issuer + '\'' +
", serialNumber='" + serialNumber + '\'' +
'}';
}
}

View File

@ -0,0 +1,351 @@
package com.cisd.tms.modules.openapi.service.impl;
import com.cisd.tms.integration.crypto.pcie.service.JnaPcieCryptoService;
import com.cisd.tms.modules.openapi.service.IOpenApiService;
import com.cisd.tms.modules.openapi.service.dto.GenericCertificate;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
import lombok.RequiredArgsConstructor;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.util.encoders.Base64;
import java.security.KeyPair;
import java.security.cert.X509Certificate;
@RequiredArgsConstructor
public class OpenApiService implements IOpenApiService {
private JnaPcieCryptoService sdf;
@Override
public String rawSign(byte[] origBytes, String dn) {
try {
// 使用固定的密钥索引 0
int keyIndex = 0;
// 获取证书和公钥
X509Certificate cert = certManager.getCertificate(dn);
if (cert == null) {
throw new RuntimeException("Certificate not found for DN: " + dn);
}
// 获取公钥字节数组
byte[] publicKey = cert.getPublicKey().getEncoded();
// 使用 SDF 接口进行 SM3 哈希包含公钥和 ID
byte[] hash = new byte[32]; // SM3 哈希长度为 32 字节
IntByReference hashLength = new IntByReference();
// 使用空指针作为会话句柄SDFSoft 支持
Pointer sessionHandle = Pointer.NULL;
// 默认的 SM2 用户 ID
String userId = "1234567812345678";
// 初始化哈希包含公钥和用户 ID
int ret = sdf.SDF_HashInit(sessionHandle, 0, publicKey, userId, userId.length());
if (ret != 0) {
throw new RuntimeException("SDF_HashInit failed with code: " + ret);
}
// 更新哈希添加原始数据
ret = sdf.SDF_HashUpdate(sessionHandle, origBytes, origBytes.length);
if (ret != 0) {
throw new RuntimeException("SDF_HashUpdate failed with code: " + ret);
}
// 完成哈希
ret = sdf.SDF_HashFinal(sessionHandle, hash, hashLength);
if (ret != 0) {
throw new RuntimeException("SDF_HashFinal failed with code: " + ret);
}
// 使用 SDF 接口进行 SM2 签名
byte[] signature = new byte[64]; // SM2 签名长度为 64 字节
ret = sdf.SDF_InternalSign_ECC(sessionHandle, keyIndex, hash, hashLength.getValue(), signature);
if (ret != 0) {
throw new RuntimeException("SDF_InternalSign_ECC failed with code: " + ret);
}
// 返回 Base64 编码的签名
return new String(Base64.encode(signature));
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("Raw sign failed: " + e.getMessage(), e);
}
}
@Override
public boolean rawVerify(byte[] origBytes, String certStr, String dn) {
try {
// 获取证书
X509Certificate cert = certManager.getCertificate(dn);
if (cert == null) {
return false;
}
// 解码签名
byte[] signature = Base64.decode(certStr);
// 获取公钥并转换为 X.509 编码格式
byte[] publicKey = cert.getPublicKey().getEncoded();
// 使用 SDF 接口进行 SM3 哈希包含公钥和 ID
byte[] hash = new byte[32]; // SM3 哈希长度为 32 字节
IntByReference hashLength = new IntByReference();
// 使用空指针作为会话句柄SDFSoft 支持
Pointer sessionHandle = Pointer.NULL;
// 默认的 SM2 用户 ID与签名时保持一致
String userId = "1234567812345678";
// 初始化哈希包含公钥和用户 ID
int ret = sdf.SDF_HashInit(sessionHandle, 0, publicKey, userId, userId.length());
if (ret != 0) {
return false;
}
// 更新哈希添加原始数据
ret = sdf.SDF_HashUpdate(sessionHandle, origBytes, origBytes.length);
if (ret != 0) {
return false;
}
// 完成哈希
ret = sdf.SDF_HashFinal(sessionHandle, hash, hashLength);
if (ret != 0) {
return false;
}
// 使用 SDF 接口进行 SM2 验签
ret = sdf.SDF_ExternalVerify_ECC(sessionHandle, 0, publicKey, hash, hashLength.getValue(), signature);
return ret == 0;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
@Override
public String dettachedSign(byte[] origBytes, String dn) {
try {
// 获取密钥对和证书
KeyPair keyPair = certManager.getKeyPair(dn);
X509Certificate cert = certManager.getCertificate(dn);
if (keyPair == null || cert == null) {
throw new RuntimeException("Certificate not found for DN: " + dn);
}
// 创建 PKCS#7 签名
CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
generator.addSignerInfoGenerator(
new JcaSignerInfoGeneratorBuilder(
new JcaDigestCalculatorProviderBuilder().setProvider("BC").build())
.build(
new org.bouncycastle.operator.jcajce.JcaContentSignerBuilder("SHA256withECDSA")
.setProvider("BC")
.build(keyPair.getPrivate()),
cert)
);
generator.addCertificate(new org.bouncycastle.cert.jcajce.JcaX509CertificateHolder(cert));
CMSSignedData signedData = generator.generate(new CMSProcessableByteArray(origBytes), false);
// 返回 Base64 编码的签名
return new String(Base64.encode(signedData.getEncoded()));
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("Detached sign failed: " + e.getMessage(), e);
}
}
@Override
public String dettachedVerify(byte[] origBytes, String certStr) {
try {
// 解码签名
byte[] signedDataBytes = Base64.decode(certStr);
// 使用原始数据验证签名
CMSSignedData signedData = new CMSSignedData(new CMSProcessableByteArray(origBytes), signedDataBytes);
// 验证签名
for (Object signerInfoObj : signedData.getSignerInfos().getSigners()) {
org.bouncycastle.cms.SignerInformation signerInfo = (org.bouncycastle.cms.SignerInformation) signerInfoObj;
// 获取证书
java.util.Collection certMatches = signedData.getCertificates().getMatches(signerInfo.getSID());
org.bouncycastle.cert.X509CertificateHolder certHolder =
(org.bouncycastle.cert.X509CertificateHolder) certMatches.iterator().next();
X509Certificate cert = new org.bouncycastle.cert.jcajce.JcaX509CertificateConverter()
.setProvider("BC")
.getCertificate(certHolder);
// 验证签名
try {
if (signerInfo.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert.getPublicKey()))) {
// 验证证书有效性检查有效期
cert.checkValidity(new java.util.Date());
return new GenericCertificate(cert);
}
} catch (org.bouncycastle.cms.CMSSignerDigestMismatchException e) {
// 签名验证失败数据不匹配不打印堆栈信息
continue;
}
}
return null;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
@Override
public String dettachedVerifySimple(byte[] origBytes, String certStr) {
try {
// 解码签名
byte[] signedDataBytes = Base64.decode(certStr);
// 使用原始数据验证签名
CMSSignedData signedData = new CMSSignedData(new CMSProcessableByteArray(origBytes), signedDataBytes);
// 验证签名不验证证书有效性
for (Object signerInfoObj : signedData.getSignerInfos().getSigners()) {
org.bouncycastle.cms.SignerInformation signerInfo = (org.bouncycastle.cms.SignerInformation) signerInfoObj;
// 获取证书
java.util.Collection certMatches = signedData.getCertificates().getMatches(signerInfo.getSID());
org.bouncycastle.cert.X509CertificateHolder certHolder =
(org.bouncycastle.cert.X509CertificateHolder) certMatches.iterator().next();
X509Certificate cert = new org.bouncycastle.cert.jcajce.JcaX509CertificateConverter()
.setProvider("BC")
.getCertificate(certHolder);
// 只验证签名是否正确不检查证书有效期
try {
if (signerInfo.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert.getPublicKey()))) {
return new GenericCertificate(cert);
}
} catch (org.bouncycastle.cms.CMSSignerDigestMismatchException e) {
// 签名验证失败数据不匹配不打印堆栈信息
continue;
}
}
return null;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* ASN.1 DER 编码的签名转换为 r||s 格式
*/
private byte[] derToRs(byte[] derSignature) {
int offset = 2; // 跳过 0x30 和长度字节
// 读取 r
int rLength = derSignature[offset + 1] & 0xFF;
byte[] r = new byte[32];
int rStart = offset + 2;
int rCopyLength = Math.min(rLength, 32);
if (rLength <= 32) {
System.arraycopy(derSignature, rStart, r, 32 - rLength, rCopyLength);
} else {
System.arraycopy(derSignature, rStart + (rLength - 32), r, 0, 32);
}
// 读取 s
offset += 2 + rLength;
int sLength = derSignature[offset + 1] & 0xFF;
byte[] s = new byte[32];
int sStart = offset + 2;
int sCopyLength = Math.min(sLength, 32);
if (sLength <= 32) {
System.arraycopy(derSignature, sStart, s, 32 - sLength, sCopyLength);
} else {
System.arraycopy(derSignature, sStart + (sLength - 32), s, 0, 32);
}
// 组合 r||s
byte[] result = new byte[64];
System.arraycopy(r, 0, result, 0, 32);
System.arraycopy(s, 0, result, 32, 32);
return result;
}
/**
* r||s 格式的签名转换为 ASN.1 DER 编码
*/
private byte[] rsToDer(byte[] rsSignature) {
if (rsSignature.length < 64) {
return rsSignature;
}
byte[] r = new byte[32];
byte[] s = new byte[32];
System.arraycopy(rsSignature, 0, r, 0, 32);
System.arraycopy(rsSignature, 32, s, 0, 32);
r = stripLeadingZeros(r);
s = stripLeadingZeros(s);
if ((r[0] & 0x80) != 0) {
byte[] temp = new byte[r.length + 1];
System.arraycopy(r, 0, temp, 1, r.length);
r = temp;
}
if ((s[0] & 0x80) != 0) {
byte[] temp = new byte[s.length + 1];
System.arraycopy(s, 0, temp, 1, s.length);
s = temp;
}
int totalLength = 2 + 2 + r.length + 2 + s.length;
byte[] der = new byte[totalLength];
int offset = 0;
der[offset++] = 0x30;
der[offset++] = (byte) (totalLength - 2);
der[offset++] = 0x02;
der[offset++] = (byte) r.length;
System.arraycopy(r, 0, der, offset, r.length);
offset += r.length;
der[offset++] = 0x02;
der[offset++] = (byte) s.length;
System.arraycopy(s, 0, der, offset, s.length);
return der;
}
/**
* 去除字节数组前面的零字节
*/
private byte[] stripLeadingZeros(byte[] data) {
int start = 0;
while (start < data.length && data[start] == 0) {
start++;
}
if (start == data.length) {
return new byte[]{0};
}
byte[] result = new byte[data.length - start];
System.arraycopy(data, start, result, 0, result.length);
return result;
}
}

View File

@ -27,6 +27,7 @@ import org.springframework.web.servlet.HandlerInterceptor;
@Component @Component
@RequiredArgsConstructor @RequiredArgsConstructor
public class OpenApiSignAuthInterceptor implements HandlerInterceptor { public class OpenApiSignAuthInterceptor implements HandlerInterceptor {
private static final String APP_ID_HEADER = "X-App-Id"; private static final String APP_ID_HEADER = "X-App-Id";