我正在努力解决以下问题:
我正在使用此代码签署CSR:
@Override
public X509Certificate signCSR( Reader pemcsr, int validityDays ) throws APIException
{
try ( PEMParser reader = new PEMParser( pemcsr ) )
{
KeyStore keystore = getKeyStore();
Properties cryptoProps = getCryptoProperties();
String caKeyAlias = cryptoProps.getProperty( PROPERTY_KEYSTORE_CA_CERT_ALIAS );
String caKeyPassword = cryptoProps.getProperty( PROPERTY_KEYSTORE_CA_CERT_PASSWORD );
PrivateKey cakey = (PrivateKey) keystore.getKey( caKeyAlias, caKeyPassword.toCharArray() );
X509Certificate cacert = (X509Certificate) keystore.getCertificate( caKeyAlias );
PKCS10CertificationRequest csr = (PKCS10CertificationRequest) reader.readObject();
AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find( "SHA1withRSA" );
AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find( sigAlgId );
X500Name issuer = new X500Name( cacert.getSubjectX500Principal().getName() );
BigInteger serial = new BigInteger( 32, new SecureRandom() );
Date from = new Date();
Date to = new Date( System.currentTimeMillis() + ( validityDays * 86400000L ) );
DigestCalculator digCalc = new BcDigestCalculatorProvider().get( new AlgorithmIdentifier( OIWObjectIdentifiers.idSHA1 ) );
X509ExtensionUtils x509ExtensionUtils = new X509ExtensionUtils( digCalc );
X509v3CertificateBuilder certgen = new X509v3CertificateBuilder( issuer, serial, from, to, csr.getSubject(), csr.getSubjectPublicKeyInfo() );
// Basic Constraints
// certgen.addExtension( Extension.basicConstraints, true, new
// BasicConstraints( 0 ) );
// Subject Key Identifier
// certgen.addExtension( Extension.subjectKeyIdentifier, false,
// x509ExtensionUtils.createSubjectKeyIdentifier(
// csr.getSubjectPublicKeyInfo() ) );
// Authority Key Identifier
// byte[] caKeyEncoded = cacert.getPublicKey().getEncoded();
// SubjectPublicKeyInfo caSubjectPublicKeyInfo =
// SubjectPublicKeyInfo.getInstance( caKeyEncoded );
// certgen.addExtension( Extension.authorityKeyIdentifier, false,
// x509ExtensionUtils.createAuthorityKeyIdentifier( caSubjectPublicKeyInfo
// ) );
// Key Usage
// certgen.addExtension( Extension.keyUsage, false, new KeyUsage(
// KeyUsage.digitalSignature | KeyUsage.keyCertSign | KeyUsage.cRLSign )
// );
ContentSigner signer = new BcRSAContentSignerBuilder( sigAlgId, digAlgId ).build( PrivateKeyFactory.createKey( cakey.getEncoded() ) );
// ContentSigner signer = new JcaContentSignerBuilder(
// "SHA1WithRSAEncryption" ).setProvider( "BC" ).build( cakey );
X509CertificateHolder holder = certgen.build( signer );
return new JcaX509CertificateConverter().setProvider( "BC" ).getCertificate( holder );
}
catch ( NoSuchAlgorithmException | KeyStoreException | CertificateException | OperatorCreationException | UnrecoverableKeyException | CertIOException e )
{
throw new APIException( API_ERROR_CODE.CRYPTOGRAPHY_ERROR, e );
}
catch ( IOException e )
{
throw new APIException( API_ERROR_CODE.IO_ERROR, e );
}
}
这成功完成。但是,当我尝试使用以下方法检查密钥时:
KeyStore ks = getKeyStore();
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm() );
trustManagerFactory.init( ks );
for ( TrustManager trustManager : trustManagerFactory.getTrustManagers() )
{
if ( trustManager instanceof X509TrustManager )
{
X509TrustManager x509TrustManager = (X509TrustManager) trustManager;
x509TrustManager.checkClientTrusted( new X509Certificate[] { certificate }, "RSA" );
}
}
...它因CertificateException而失败。请注意,我在这里使用了非常相同的密钥库,这意味着我正在签署的CA密钥包含在其中。为什么会这样?
顺便说一句,奇怪的是,当我使用Windows的证书查看器打开生成的签名证书时,它会显示颁发的CA名称,但其条目不会显示在证书链中。似乎CA根证书不存在于Windows可信任的authirities列表中,但实际上它也存在。
更奇怪的是:如果我使用OpenSSL签署CSR,证书链看起来不错。我还认为,通过PKCS12将CA密钥对从OpenSSL导入Java密钥库作为中间格式的过程并不成功,但实际上如果我从Java密钥库导出CA证书并使用Windows证书打开它查看器,它显示为可信...
更新:对于熟悉ASN.1的人,这里有两个编码证书。一个是使用BouncyCastle制作的,不受信任,另一个是由与OpenSSL相同的CA密钥签名而且是受信任的。它们可以用这样的工具解码:ASN.1 decoder我会非常感激有人可以查看这些解码数据并告诉我它们之间可能有什么区别。
这个不受信任:
-----BEGIN CERTIFICATE-----
MIIC6TCCAlKgAwIBAgIESdsI/TANBgkqhkiG9w0BAQUFADCBgzEgMB4GCSqGSIb3
DQEJARYRdGVzdGNhQHRlc3RjYS5jb20xEDAOBgNVBAMMB1Rlc3QgQ0ExEDAOBgNV
BAsMB1Rlc3QgQ0ExEDAOBgNVBAoMB1Rlc3QgQ0ExDTALBgNVBAcMBFdpZW4xDTAL
BgNVBAgMBFdpZW4xCzAJBgNVBAYTAkFUMB4XDTE0MDUxOTExNTYwM1oXDTE1MDUx
OTExNTYwM1owajELMAkGA1UEBhMCVUsxCzAJBgNVBAgTAlBiMQswCQYDVQQHEwJC
cDETMBEGA1UEChMKZmdmZ2ZnZGZnZDEPMA0GA1UECxMGYWJjZGVmMRswGQYDVQQD
DBJwZXRlcnZlbG9zeV90aWdyaXMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQCdL7taENsONBazc2iMDV5nw9ACP5mevmnzPwOJRUcd5GlGgry/iSa3tTwL
l6Um3zNc4X0m5nVVskKeJE4dTvYFV3+vJlEKCra86yQfa6XkGllU4EG6SdG8lRhE
Btk1QbOQZKrUz77IdOWWOUvIsNxtDDQcUhnrSjSxHohdoe/yoCl+60RBdjrgUrRo
uctSHFPvVt2uZaVM2rAVovx56vvJHOag2++rcvXaOh9WHvdwRAIZt/4aOv2O4jdI
jKdRrmF8dOudjR89wIeVjX9fvyvx+hw+ZolUio9GOVKLlBcYno6lEupHLUDK9ECs
W8F6y65nYGlm9/0G0+gB7K1yy1dBAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAKJpM
7AbkWBH3ho1YV0d1glJvefQ1xaXGpDfd+Tzf3+cR1o3+YxxEyuYvBbiQ/MBxKD9/
hsFCqEWzOfu2lAZ+/6uHvt7BCEGhaLdWKXehoaIw/kEMeISIUDFbKORCsKJNbYRB
xgqBXGglTQ4gVXMDRBxzOmButN31j1VDt55gvn4=
-----END CERTIFICATE-----
这个是TRUSTED,这是使用理论上相同的CA证书但通过OpenSSL生成的:
-----BEGIN CERTIFICATE-----
MIIC+TCCAmICAhI4MA0GCSqGSIb3DQEBBQUAMIGDMQswCQYDVQQGEwJBVDENMAsG
A1UECAwEV2llbjENMAsGA1UEBwwEV2llbjEQMA4GA1UECgwHVGVzdCBDQTEQMA4G
A1UECwwHVGVzdCBDQTEQMA4GA1UEAwwHVGVzdCBDQTEgMB4GCSqGSIb3DQEJARYR
dGVzdGNhQHRlc3RjYS5jb20wHhcNMTQwNTE0MTkzMTAzWhcNMTUwNTA5MTkzMTAz
WjCBgDELMAkGA1UEBhMCSFUxETAPBgNVBAgTCEJ1ZGFwZXN0MREwDwYDVQQHEwhC
dWRhcGVzdDEWMBQGA1UEChMNTWVyY2hhbnQgVGVzdDEWMBQGA1UECxMNTWVyY2hh
bnQgVGVzdDEbMBkGA1UEAwwScGV0ZXJ2ZWxvc3lfdGlncmlzMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1vuY4MQ5b9Jb0MiyEuCrR4E+7VgmrvEwlswO
aMIF4H6i538PwPml5dbqx/3whxR/BcQJuJYWI/Hh7xxGS7FvSQ+DNhzxv9TpECKS
/5OZNm+JikPZwTiwrS/Cf4NP+ZcXOjtVZp6ngVtTarn3NC/J7gJVYaHVVO4NbUkt
kCYhdfCXg71QiJ42RWMjMC9tJFrrlfem+SVzh8yMtUCBKm7nbMjQ6LngawjTzDK8
2Zcdqwdzvt2pcYcsYSViO5j5t/r7rIDGjRkjJqRSEiJMOvn0W+sdTdmFoZbyj7Qe
pgyCyf28uFyCO9QZro337D8klPLXaWJOwPDXXiuYOTDYAjBVbwIDAQABMA0GCSqG
SIb3DQEBBQUAA4GBAGU60GVjR+2oEiJMSe1CKU7gf+bGuxaCxXQTzVQLU652i1sp
Fv56o6jnLtw46/rQydNKX4GBH022B/BDEPAQQiQv31YKQAoWtBZod0SRonogcx7p
AULacoma9QEgHSX0l+2yEn42/qo7o0pAmmewJlsCnHVIqI0eU8x1XbCEAf53
-----END CERTIFICATE-----
更新2:
感谢Bruno的回答,证书链现在看起来很好,并生成以下证书:
-----BEGIN CERTIFICATE-----
MIIC6TCCAlKgAwIBAgIEI2vbpTANBgkqhkiG9w0BAQUFADCBgzELMAkGA1UEBhMC
QVQxDTALBgNVBAgMBFdpZW4xDTALBgNVBAcMBFdpZW4xEDAOBgNVBAoMB1Rlc3Qg
Q0ExEDAOBgNVBAsMB1Rlc3QgQ0ExEDAOBgNVBAMMB1Rlc3QgQ0ExIDAeBgkqhkiG
9w0BCQEWEXRlc3RjYUB0ZXN0Y2EuY29tMB4XDTE0MDUyMDA3MzkyMFoXDTE1MDUy
MDA3MzkyMFowajELMAkGA1UEBhMCVUsxCzAJBgNVBAgTAlBiMQswCQYDVQQHEwJC
cDETMBEGA1UEChMKZmdmZ2ZnZGZnZDEPMA0GA1UECxMGYWJjZGVmMRswGQYDVQQD
DBJwZXRlcnZlbG9zeV90aWdyaXMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQCdL7taENsONBazc2iMDV5nw9ACP5mevmnzPwOJRUcd5GlGgry/iSa3tTwL
l6Um3zNc4X0m5nVVskKeJE4dTvYFV3+vJlEKCra86yQfa6XkGllU4EG6SdG8lRhE
Btk1QbOQZKrUz77IdOWWOUvIsNxtDDQcUhnrSjSxHohdoe/yoCl+60RBdjrgUrRo
uctSHFPvVt2uZaVM2rAVovx56vvJHOag2++rcvXaOh9WHvdwRAIZt/4aOv2O4jdI
jKdRrmF8dOudjR89wIeVjX9fvyvx+hw+ZolUio9GOVKLlBcYno6lEupHLUDK9ECs
W8F6y65nYGlm9/0G0+gB7K1yy1dBAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAIdFF
h6uLY7ioKQ3O0c4cZHHjRA0HTlWjih8P2xvXY/V9jF914BT7OW52UJ16tQaJlOf+
mAeeBDq9srKnkmOQp3mCejVnkyVZF8pOOzNbqSVzylt0Csg2twnxZ0NcM63Oda5b
YSQI8+arryxykLWkHWH8i/6rPCDCtbAHBo7fSeQ=
-----END CERTIFICATE-----
但是,上面的TrustManager代码拒绝它。如果我绕过TrustManager并做这样的事情:
KeyStore ks = getKeyStore();
Enumeration<String> aliases = ks.aliases();
while ( aliases.hasMoreElements() )
{
String alias = aliases.nextElement();
Certificate currentCert = ks.getCertificate( alias );
try
{
certificate.verify( currentCert.getPublicKey() );
return true;
}
catch ( Exception e )
{
// the certificate cannot be verified with this key.
}
}
return false;
......它过去了。有人知道为什么它在TrustManager检查上失败了吗?
P.S。 CA证书如下所示:
-----BEGIN CERTIFICATE-----
MIICfzCCAegCCQCU+Ah6M5qQGTANBgkqhkiG9w0BAQUFADCBgzELMAkGA1UEBhMC
QVQxDTALBgNVBAgMBFdpZW4xDTALBgNVBAcMBFdpZW4xEDAOBgNVBAoMB1Rlc3Qg
Q0ExEDAOBgNVBAsMB1Rlc3QgQ0ExEDAOBgNVBAMMB1Rlc3QgQ0ExIDAeBgkqhkiG
9w0BCQEWEXRlc3RjYUB0ZXN0Y2EuY29tMB4XDTE0MDQyMzA3MjYzNFoXDTI0MDQy
MDA3MjYzNFowgYMxCzAJBgNVBAYTAkFUMQ0wCwYDVQQIDARXaWVuMQ0wCwYDVQQH
DARXaWVuMRAwDgYDVQQKDAdUZXN0IENBMRAwDgYDVQQLDAdUZXN0IENBMRAwDgYD
VQQDDAdUZXN0IENBMSAwHgYJKoZIhvcNAQkBFhF0ZXN0Y2FAdGVzdGNhLmNvbTCB
nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAldKTo8iqF52dsOwln0Oppu+ODiaG
R4T7Znrca4Cs5FBQOmuMwqUP6ilW115p/WvkBHhm8dZyVACPKdshEfhh4VFAW5r2
mJnosYgjafQpTEv83sc938DwtK6iikZ0uvdBJKG/IuYblNq9TPMLFeTYjD8mgf9j
m6JOvA/Q9J4nRW0CAwEAATANBgkqhkiG9w0BAQUFAAOBgQB8ACYeC+zjV/KqxPr1
cyzfJP9xfUnxDTEKUJS2YVuxJqpfbHeUtvKoN89BfY07XWdnj8cgMDfJp10Kdc2A
clwP2lVDtOgHZS07UUW98q9FKQ33mLHIn0nDKNwTo5VH8t/NJVeMFuZPAbFiI2gj
KH2sTU2GNNvKC4jHh0PS+OZFtg==
-----END CERTIFICATE-----
答案 0 :(得分:7)
如果您查看两个证书中的颁发者DN,它们不匹配(从openssl x509 -text
输出):
Issuer: C=AT, ST=Wien, L=Wien, O=Test CA, OU=Test CA, CN=Test CA/emailAddress=testca@testca.com
和
Issuer: emailAddress=testca@testca.com, CN=Test CA, OU=Test CA, O=Test CA, L=Wien, ST=Wien, C=AT
因此,它无法将错误的发行者与CA的主题DN相匹配。
不幸的是,X500Name issuer = new X500Name(cacert.getSubjectX500Principal().getName())
并没有达到您所期望的效果。 RDN的顺序相反。通常,从字符串表示重新构建DN可能会失败,因为有不同的方法将ASN.1表示序列化为字符串。 Java的X500Principal
有多种格式可用于getName(...)
,它甚至提供了一种向字符串映射提供自己的OID的方法(对于更加模糊的OID)。 emailAddress
分离的方式也可能导致问题(注意用逗号或斜线分隔的方式)。
相反,从编码形式构建X500Name,这应始终有效:
X500Name x500Name = X500Name.getInstance(cert
.getSubjectX500Principal().getEncoded());