X509 RSA充气城堡标志数据和验证
Posted
技术标签:
【中文标题】X509 RSA充气城堡标志数据和验证【英文标题】:X509 RSA bouncy castle sign data and verification 【发布时间】:2020-07-03 07:51:40 【问题描述】:我正在尝试使用数字证书对数据进行签名和验证。验证签名时出现以下错误
线程“主”java.lang.IllegalArgumentException 中的异常:缺失 提供者在 java.security.MessageDigest.getInstance(MessageDigest.java:237) 在 org.bouncycastle.jcajce.util.NamedJcaJceHelper.createDigest(未知 来源)在 org.bouncycastle.operator.jcajce.OperatorHelper.createDigest(未知 来源)在 org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder$1.get(未知 来源)在 org.bouncycastle.cms.SignerInformationVerifier.getDigestCalculator(未知 来源)在 org.bouncycastle.cms.SignerInformation.doVerify(未知 来源)在 org.bouncycastle.cms.SignerInformation.verify(未知 来源)在 Crypto.verifySignedData(Crypto.java:257) 在 Crypto.main(Crypto.java:361)
有什么想法吗? 使用 bcpkix-jdk15on-1.58.jar 代码如下。提前致谢
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.Base64;
import org.apache.commons.lang.StringUtils;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.*;
import org.bouncycastle.asn1.x500.RDN;
import org.bouncycastle.asn1.x500.AttributeTypeAndValue;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.asn1.x500.style.IETFUtils;
import org.bouncycastle.operator.*;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.cms.CMSTypedData;
import org.bouncycastle.cms.DefaultCMSSignatureAlgorithmNameGenerator;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.cms.SignerInformationVerifier;
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
public class Crypto
private static String provider;
private static String algorithm;
private static String privateKsType;
private static String privateKsPath;
private static String privateKsPassword;
private static String alias;
private static String privateKeyPassword;
private static PrivateKey privateKey;
private static PublicKey publicKey;
private static X509Certificate certToSign;
private static PrivateKey encryptPrivateKey;
private static X509Certificate encryptCert;
private final static DefaultCMSSignatureAlgorithmNameGenerator defaultNameGenerator =
new DefaultCMSSignatureAlgorithmNameGenerator();
private final static DefaultSignatureAlgorithmIdentifierFinder
defaultIdentifierFinder = new DefaultSignatureAlgorithmIdentifierFinder();
private final static Map<String, Map<BigInteger, X509Certificate>> mCert = new HashMap<>();
private static final Hashtable<ASN1ObjectIdentifier, String> DefaultSymbols = new Hashtable<>();
private static DigestCalculatorProvider jcaDigestCalculatorProviderBuilder;
static
DefaultSymbols.put(BCStyle.C, "c");
DefaultSymbols.put(BCStyle.O, "o");
DefaultSymbols.put(BCStyle.T, "title");
DefaultSymbols.put(BCStyle.OU, "ou");
DefaultSymbols.put(BCStyle.CN, "cn");
DefaultSymbols.put(BCStyle.L, "l");
DefaultSymbols.put(BCStyle.ST, "st");
DefaultSymbols.put(BCStyle.SN, "serialNumber");
DefaultSymbols.put(BCStyle.EmailAddress, "e");
DefaultSymbols.put(BCStyle.DC, "dc");
DefaultSymbols.put(BCStyle.UID, "uid");
DefaultSymbols.put(BCStyle.STREET, "street");
DefaultSymbols.put(BCStyle.SURNAME, "sn");
DefaultSymbols.put(BCStyle.GIVENNAME, "givenName");
DefaultSymbols.put(BCStyle.INITIALS, "initials");
DefaultSymbols.put(BCStyle.GENERATION, "generation");
DefaultSymbols.put(BCStyle.UnstructuredAddress, "unstructuredAddress");
DefaultSymbols.put(BCStyle.UnstructuredName, "unstructuredName");
DefaultSymbols.put(BCStyle.UNIQUE_IDENTIFIER, "uniqueIdentifier");
DefaultSymbols.put(BCStyle.DN_QUALIFIER, "dn");
DefaultSymbols.put(BCStyle.PSEUDONYM, "pseudonym");
DefaultSymbols.put(BCStyle.POSTAL_ADDRESS, "postalAddress");
DefaultSymbols.put(BCStyle.NAME_AT_BIRTH, "nameAtBirth");
DefaultSymbols.put(BCStyle.COUNTRY_OF_CITIZENSHIP, "countryOfCitizenship");
DefaultSymbols.put(BCStyle.COUNTRY_OF_RESIDENCE, "countryOfResidence");
DefaultSymbols.put(BCStyle.GENDER, "gender");
DefaultSymbols.put(BCStyle.PLACE_OF_BIRTH, "placeOfBirth");
DefaultSymbols.put(BCStyle.DATE_OF_BIRTH, "dateOfBirth");
DefaultSymbols.put(BCStyle.POSTAL_CODE, "postalCode");
DefaultSymbols.put(BCStyle.BUSINESS_CATEGORY, "businessCategory");
DefaultSymbols.put(BCStyle.TELEPHONE_NUMBER, "telephoneNumber");
DefaultSymbols.put(BCStyle.NAME, "name");
public static void appendTypeAndValue(StringBuilder buf,
AttributeTypeAndValue typeAndValue,
Hashtable<ASN1ObjectIdentifier, String> oidSymbols)
String sym = oidSymbols.get(typeAndValue.getType());
if (sym != null)
buf.append(sym);
else
buf.append(typeAndValue.getType().getId());
buf.append('=');
buf.append(IETFUtils.valueToString(typeAndValue.getValue()));
public static byte[] signData(byte[] data,
X509Certificate signingCertificate, PrivateKey signingKey, String signatureAlgorithm, String certProvider)
throws Exception
if (privateKey == null)
throw new IllegalStateException(
"Crypto error, failed to load private key");
if (certProvider == null || data == null || signatureAlgorithm == null)
return null;
byte[] signedMessage = null;
try
CMSTypedData cmsData = new CMSProcessableByteArray(data);
CMSSignedDataGenerator cmsGenerator = new CMSSignedDataGenerator();
ContentSigner contentSigner = new JcaContentSignerBuilder(
signatureAlgorithm).setProvider(certProvider).build(signingKey);
cmsGenerator.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(
new JcaDigestCalculatorProviderBuilder()
.build()).build(contentSigner, signingCertificate));
CMSSignedData cms = cmsGenerator.generate(cmsData, false);
signedMessage = cms.getEncoded();
catch (Exception e)
System.out.println(e.getMessage());
throw e;
return signedMessage;
private static X509Certificate getCertificate(final String issuerDN,
final BigInteger certSN)
Map<BigInteger, X509Certificate> hmI = mCert.get(issuerDN);
if (hmI != null)
final X509Certificate cert = hmI.get(certSN);
if (cert != null)
return cert;
throw new IllegalStateException(
"Crypto error, failed to load certificate");
public static String toStringReversed(X500Name name)
final StringBuilder buf = new StringBuilder();
boolean first = true;
final RDN[] rdns = name.getRDNs();
for (int i = rdns.length - 1; i >= 0; i--)
if (first)
first = false;
else
buf.append(',');
if (rdns[i].isMultiValued())
AttributeTypeAndValue[] atv = rdns[i].getTypesAndValues();
boolean firstAtv = true;
for (int j = 0; j != atv.length; j++)
if (firstAtv)
firstAtv = false;
else
buf.append('+');
appendTypeAndValue(buf, atv[j], DefaultSymbols);
else
appendTypeAndValue(buf, rdns[i].getFirst(), DefaultSymbols);
return buf.toString();
private static String getSubject(final X509Certificate cert)
throws CertificateEncodingException
final JcaX509CertificateHolder jcaX509CertificateHolder = new JcaX509CertificateHolder(
cert);
final X500Name x500Subject = jcaX509CertificateHolder.getSubject();
return toStringReversed(x500Subject);
private static boolean verifySignedData(String providerName,
final String signature, final String stringData,
final String signatureAlgorithm) throws Exception
if (signature == null || stringData == null
|| signatureAlgorithm == null)
return false;
final byte[] pkcs7 = Base64.getDecoder().decode(signature);
final byte[] bytesToCheck = stringData.getBytes("UTF-8");
final CMSProcessableByteArray typedData = new CMSProcessableByteArray(
bytesToCheck);
final CMSSignedData cms = new CMSSignedData(typedData, pkcs7);
final SignerInformationStore signers = cms.getSignerInfos();
final Iterator<SignerInformation> iter = signers.getSigners().iterator();
boolean bRes = false;
while (iter.hasNext())
final SignerInformation signer = iter.next();
final BigInteger certSN = signer.getSID().getSerialNumber();
final String issuerDN = toStringReversed(X500Name
.getInstance(signer.getSID().getIssuer().getEncoded()));
System.out.println(signer.getSID().getIssuer());
System.out.println(issuerDN);
final X509Certificate cert = getCertificate(issuerDN, certSN);
cert.checkValidity();
final String subject = getSubject(cert);
final ContentVerifierProvider contentVerifierProvider;
contentVerifierProvider = new JcaContentVerifierProviderBuilder()
.setProvider(providerName).build(cert.getPublicKey());
System.out.println(contentVerifierProvider);
SignerInformationVerifier signerInformation = new SignerInformationVerifier(defaultNameGenerator,
defaultIdentifierFinder, contentVerifierProvider, jcaDigestCalculatorProviderBuilder);
System.out.println(signerInformation);
if (!signer.verify(signerInformation))
System.out.println("checkSign BAD, subj:" + subject);
throw new Exception("Failed to check signature: " + subject);
System.out.println("checkSign OK, subj:" + subject);
bRes = true;
return bRes;
public static String initPrivateKey() throws Exception
if ("BC".equals(provider) && Security.getProvider("BC") == null)
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
jcaDigestCalculatorProviderBuilder = new JcaDigestCalculatorProviderBuilder()
.setProvider(provider).build();
if (!StringUtils.isEmpty(privateKsType))
System.out.println("Crypto init:PrivateKS, KeyStoreType:"
+ privateKsType + ", KeyStorePath:" + privateKsPath
+ ", Alias:" + alias);
KeyStore privateKS;
try
System.out.println("opening keyStore");
final InputStream is;
if (!StringUtils.isEmpty(privateKsPath))
is = new FileInputStream(new File(privateKsPath));
else
is = null;
privateKS = KeyStore.getInstance(privateKsType);
privateKS.load(is, privateKsPassword.toCharArray());
certToSign = (X509Certificate) privateKS.getCertificate(alias);
System.out.println("reading private key");
privateKey = (PrivateKey) privateKS.getKey(alias,
privateKeyPassword.toCharArray());
if (privateKey == null)
throw new IllegalStateException(
"Crypto error, failed to load private key " + alias);
if (encryptCert == null)
encryptCert = certToSign;
encryptPrivateKey = privateKey;
catch (KeyStoreException | NoSuchAlgorithmException | IOException
| CertificateException | UnrecoverableKeyException e)
System.out.println(e.getMessage());
throw new Exception(e);
System.out.println("Crypto init:PrivateKS done, Provider - "
+ privateKS.getProvider().getName());
return privateKS.getProvider().getName();
else
return null;
【问题讨论】:
您的类路径中有提供程序 jar 吗? mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on 是的,我已经在类路径中包含了这个 jar。 代码不完整是吗?你在哪里调用verifySignedData?您之前是否调用过 initPrivateKey 以便将 BC 添加为提供程序? 我调用 initPrivateKey 从 .jks 文件中提取私钥用于签名数据。使用函数 signData 对数据进行签名后,我调用 verifySignedData 来验证签名。我实际上必须在验证部分工作,但显然,在验证之前创建签名是必要的。我现在也添加了我的主要方法。 【参考方案1】:问题已解决。我在函数中错过了以下行
jcaDigestCalculatorProviderBuilder = new JcaDigestCalculatorProviderBuilder() .setProvider(providerName).build(); 这是完整的代码
public class CryptoUtils
private final static DefaultCMSSignatureAlgorithmNameGenerator defaultNameGenerator = new DefaultCMSSignatureAlgorithmNameGenerator();
private final static DefaultSignatureAlgorithmIdentifierFinder defaultIdentifierFinder = new DefaultSignatureAlgorithmIdentifierFinder();
private static final Hashtable<ASN1ObjectIdentifier, String> DefaultSymbols = new Hashtable<ASN1ObjectIdentifier, String>();
static
DefaultSymbols.put(BCStyle.C, "c");
DefaultSymbols.put(BCStyle.O, "o");
DefaultSymbols.put(BCStyle.T, "title");
DefaultSymbols.put(BCStyle.OU, "ou");
DefaultSymbols.put(BCStyle.CN, "cn");
DefaultSymbols.put(BCStyle.L, "l");
DefaultSymbols.put(BCStyle.ST, "st");
DefaultSymbols.put(BCStyle.SN, "serialNumber");
DefaultSymbols.put(BCStyle.EmailAddress, "e");
DefaultSymbols.put(BCStyle.DC, "dc");
DefaultSymbols.put(BCStyle.UID, "uid");
DefaultSymbols.put(BCStyle.STREET, "street");
DefaultSymbols.put(BCStyle.SURNAME, "sn");
DefaultSymbols.put(BCStyle.GIVENNAME, "givenName");
DefaultSymbols.put(BCStyle.INITIALS, "initials");
DefaultSymbols.put(BCStyle.GENERATION, "generation");
DefaultSymbols.put(BCStyle.UnstructuredAddress, "unstructuredAddress");
DefaultSymbols.put(BCStyle.UnstructuredName, "unstructuredName");
DefaultSymbols.put(BCStyle.UNIQUE_IDENTIFIER, "uniqueIdentifier");
DefaultSymbols.put(BCStyle.DN_QUALIFIER, "dn");
DefaultSymbols.put(BCStyle.PSEUDONYM, "pseudonym");
DefaultSymbols.put(BCStyle.POSTAL_ADDRESS, "postalAddress");
DefaultSymbols.put(BCStyle.NAME_AT_BIRTH, "nameAtBirth");
DefaultSymbols.put(BCStyle.COUNTRY_OF_CITIZENSHIP,
"countryOfCitizenship");
DefaultSymbols.put(BCStyle.COUNTRY_OF_RESIDENCE, "countryOfResidence");
DefaultSymbols.put(BCStyle.GENDER, "gender");
DefaultSymbols.put(BCStyle.PLACE_OF_BIRTH, "placeOfBirth");
DefaultSymbols.put(BCStyle.DATE_OF_BIRTH, "dateOfBirth");
DefaultSymbols.put(BCStyle.POSTAL_CODE, "postalCode");
DefaultSymbols.put(BCStyle.BUSINESS_CATEGORY, "businessCategory");
DefaultSymbols.put(BCStyle.TELEPHONE_NUMBER, "telephoneNumber");
DefaultSymbols.put(BCStyle.NAME, "name");
public static void appendTypeAndValue(StringBuilder buf,
AttributeTypeAndValue typeAndValue,
Hashtable<ASN1ObjectIdentifier, String> oidSymbols)
String sym = oidSymbols.get(typeAndValue.getType());
if (sym != null)
buf.append(sym);
else
buf.append(typeAndValue.getType().getId());
buf.append('=');
buf.append(IETFUtils.valueToString(typeAndValue.getValue()));
// private static X509Certificate getCertificate(final String issuerDN,
// final BigInteger certSN)
// Map<BigInteger, X509Certificate> hmI = mCert.get(issuerDN);
//
// if (hmI != null)
// final X509Certificate cert = hmI.get(certSN);
// if (cert != null)
// return cert;
//
//
// throw new IllegalStateException(
// "Crypto error, failed to load certificate");
//
public static String toStringReversed(X500Name name)
final StringBuilder buf = new StringBuilder();
boolean first = true;
final RDN[] rdns = name.getRDNs();
for (int i = rdns.length - 1; i >= 0; i--)
if (first)
first = false;
else
buf.append(',');
if (rdns[i].isMultiValued())
AttributeTypeAndValue[] atv = rdns[i].getTypesAndValues();
boolean firstAtv = true;
for (int j = 0; j != atv.length; j++)
if (firstAtv)
firstAtv = false;
else
buf.append('+');
appendTypeAndValue(buf, atv[j], DefaultSymbols);
else
appendTypeAndValue(buf, rdns[i].getFirst(), DefaultSymbols);
return buf.toString();
private static String getSubject(final X509Certificate cert)
throws CertificateEncodingException
final JcaX509CertificateHolder jcaX509CertificateHolder = new JcaX509CertificateHolder(
cert);
final X500Name x500Subject = jcaX509CertificateHolder.getSubject();
return toStringReversed(x500Subject);
public static Boolean verifySignedData(String providerName,
final String signature, final String stringData,
final String signatureAlgorithm)
try
if (signature == null || stringData == null
|| signatureAlgorithm == null)
logInfo("default", "Signature/ Data/ Algorithm not found");
return false;
final byte[] pkcs7 = Base64.getDecoder().decode(signature);
final byte[] bytesToCheck = stringData.getBytes("UTF-8");
final CMSProcessableByteArray typedData = new CMSProcessableByteArray(
bytesToCheck); //$NON-NLS-1$
final CMSSignedData cms = new CMSSignedData(typedData, pkcs7);
final SignerInformationStore signers = cms.getSignerInfos();
final Iterator<SignerInformation> iter = signers.getSigners()
.iterator();
boolean bRes = false;
while (iter.hasNext())
final SignerInformation signer = iter.next();
FileInputStream fin = new FileInputStream("D:\\JB\\jb.cer");
CertificateFactory f = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) f
.generateCertificate(fin);
PublicKey pk = cert.getPublicKey();
cert.checkValidity();
final String subject = getSubject(cert);
final ContentVerifierProvider contentVerifierProvider;
DigestCalculatorProvider jcaDigestCalculatorProviderBuilder = new JcaDigestCalculatorProviderBuilder()
.setProvider(providerName).build();
contentVerifierProvider = new JcaContentVerifierProviderBuilder()
.setProvider(providerName).build(pk);
SignerInformationVerifier signerInformation = new SignerInformationVerifier(
defaultNameGenerator, defaultIdentifierFinder,
contentVerifierProvider,
jcaDigestCalculatorProviderBuilder);
try
if (!signer.verify(signerInformation))
logInfo("default", "CheckSign BAD, subj:" + subject);
return false;
catch (Exception e)
logException("default",
"Exception occurred in verifying signature: ", e);
return false;
// System.out.println("checkSign OK, subj:" + subject);
bRes = true;
return bRes;
catch (Exception e)
logException("default",
"Exception occurred in verifying signature: ", e);
return false;
public static List<Serializable> initPrivateKey(String KeystorePath,
String keystorePassword, String keystoreAlias, String keystoreType)
throws Exception
PrivateKey privateKey = null;
X509Certificate certificateToSign;
if (!StringUtils.isEmpty(keystoreType))
// System.out.println("Crypto init:PrivateKS, KeyStoreType: "
// + keystoreType + ", KeyStorePath:" + KeystorePath
// + ", Alias:" + keystoreAlias);
KeyStore privateKS;
try
// System.out.println("opening keyStore");
final InputStream is;
if (!StringUtils.isEmpty(KeystorePath))
is = new FileInputStream(new File(KeystorePath));
else
is = null;
privateKS = KeyStore.getInstance(keystoreType);
privateKS.load(is, keystorePassword.toCharArray());
certificateToSign = (X509Certificate) privateKS
.getCertificate(keystoreAlias);
// System.out.println("reading private key");
privateKey = (PrivateKey) privateKS.getKey(keystoreAlias,
keystorePassword.toCharArray());
if (privateKey == null)
logInfo("default", "Failed to load private key");
return null;
catch (KeyStoreException | NoSuchAlgorithmException | IOException
| CertificateException | UnrecoverableKeyException e)
logException("default",
"Exception occurred in genersting privateKey: ", e);
return null;
return Arrays.asList(privateKS.getProvider().getName(), privateKey,
certificateToSign);
else
logInfo("default", "Keystore type is null");
return null;
public static String generateSignature(String stringData,
String signatureAlgorithm, String KeystorePath,
String keystorePassword, String keystoreAlias, String keystoreType)
try
List<Serializable> certificateDetails = initPrivateKey(
KeystorePath, keystorePassword, keystoreAlias, keystoreType);
String providerName = (String) certificateDetails.get(0);
PrivateKey certPrivateKey = (PrivateKey) certificateDetails.get(1);
X509Certificate certificateToSign = (X509Certificate) certificateDetails
.get(2);
if (certificateToSign == null)
logInfo("default", "Failed to load certificate");
return null;
if (certPrivateKey == null)
logInfo("default", "Failed to load private key");
return null;
if (providerName == null || stringData == null
|| signatureAlgorithm == null)
return null;
final byte[] buf = stringData.getBytes("UTF-8");
CMSTypedData typedData = new CMSProcessableByteArray(buf);
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
ContentSigner signer;
signer = new JcaContentSignerBuilder(signatureAlgorithm)
.setProvider(providerName).build(certPrivateKey);
DigestCalculatorProvider digProvider = new JcaDigestCalculatorProviderBuilder()
.setProvider(providerName).build();
JcaSignerInfoGeneratorBuilder builder = new JcaSignerInfoGeneratorBuilder(
digProvider);
gen.addSignerInfoGenerator(builder.build(signer, certificateToSign));
CMSSignedData signed = gen.generate(typedData, false);
byte[] der = signed.getEncoded();
final String result = Base64.getEncoder().encodeToString(der);
return result;
catch (Exception e)
logException("default",
"Exception occurred in generating signature: ", e);
return null;
【讨论】:
你能不能这么好,并发布完整的代码,以便其他人能够获得一个运行示例 - 谢谢?以上是关于X509 RSA充气城堡标志数据和验证的主要内容,如果未能解决你的问题,请参考以下文章
验证充气城堡上的 javacard 签名 ALG_ECDSA_SHA