我正在开发一个分布式应用程序,该应用程序具有许多唯一标识的从属进程,这些从属进程将通过启用 SSL 的套接字与主应用程序通信。该应用程序是用 java 编写的。
我需要一些帮助来理解 SSLSockets,或者更确切地说,他们使用的证书。
我正在寻找的是可以告诉我是否已正确理解证书链的基本工作原理,但我也不会拒绝代码示例的人。
我想要这样一种设置,其中服务器本身具有 CA 签名证书,并且每个从服务器都将获得由主应用程序创建的自己的证书。
CA->Main server cert->Master SSL cert
CA->Main server cert->Slave SSL cert 1
CA->Main server cert->Slave SSL cert 2
CA->Main server cert->Slave SSL cert 3
第一个问题:这种证书链是解决问题的正确方式吗? 我认为这是实现主从都具有唯一身份而无需 CA 签署每个证书的最简单方法。
第二个问题: 我如何以编程方式在 Java 中创建 SSL 证书?我正在尝试在此处创建链中的最后一个证书,假设我现在已经拥有“主服务器证书”。 到目前为止,我已经为证书生成了一个 key (类型是 RSA):
public KeyPair generateKeypair(String type, int bytes)
throws NoSuchAlgorithmException{
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(type);
keyPairGenerator.initialize(bytes);
return keyPairGenerator.generateKeyPair();
}
X509Principal issuer = PrincipalUtil.getSubjectX509Principal(serverCert);
SubjectPublicKeyInfo key
= SubjectPublicKeyInfo.getInstance(kpair.getPublic().getEncoded());
X509v3CertificateBuilder certGen
= new X509v3CertificateBuilder(
issuer,
BigInteger.valueOf(new SecureRandom().nextInt()),
before,
after,
subject,
key
);
AlgorithmIdentifier sigAlgId
= new DefaultSignatureAlgorithmIdentifierFinder().find("SHA1withRSA");
AlgorithmIdentifier digAlgId
= new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);
我不认为将 serverCert 设置为颁发者就足以签署证书了吗?据我所知,我需要以某种方式用链中的下一个证书签署新证书,但我该怎么做呢?我是否使用 serverCert 的私钥签署证书,例如:
AsymmetricKeyParameter akp
= PrivateKeyFactory.createKey(serverPrivateKey.getEncoded());
AlgorithmIdentifier sigAlgId
= new DefaultSignatureAlgorithmIdentifierFinder().find("SHA1withRSA");
AlgorithmIdentifier digAlgId
= new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);
ContentSigner sigGen
= new BcRSAContentSignerBuilder(sigAlgId, digAlgId).build(akp);
我还遗漏了任何其他步骤吗?
最佳答案
从技术角度来看,您的解决方案是正确的。但是不要忘记安全方面的考虑:谁可以申请证书,如何执行身份验证,如何将证书/私钥分发到服务器......
这些元素对于证书生成是必需的:
- 主题名称
- 发行人名称
- 证书序列号
- 主题公钥
- 有效期(不早于,不晚于)
添加一些扩展也是一个好习惯:
- 主题 key 标识符
- 授权 key 标识符
- 基本约束
- key 用法
- 扩展 key 用法
此代码片段概述了证书生成:
ContentSigner getCertSigner(PrivateKey issuerKey) {
AsymmetricKeyParameter akp = PrivateKeyFactory.createKey(issuerKey.getEncoded());
AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find("SHA1withRSA");
AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);
return new BcRSAContentSignerBuilder(sigAlgId, digAlgId).build(akp);
}
X509CertificateHolder generateCertificate(X509Certificate issuerCert, PrivateKey issuerKey, X500Name subject, PublicKey subjectKey, Date notBefore, Date notAfter) {
X509Principal issuerDN = PrincipalUtil.getSubjectX509Principal(issuerCert);
SubjectPublicKeyInfo key = SubjectPublicKeyInfo.getInstance(subjectKey.getEncoded());
X509v3CertificateBuilder builder = new X509v3CertificateBuilder(issuerDN, BigInteger.valueOf(new SecureRandom().nextInt()), before, after, subject, key);
// Add authority key identifier
builder.addExtension(X509Extension.authorityKeyIdentifier, false, JcaX509ExtensionUtils.createAuthorityKeyIdentifier(issuerCert));
// Add subject key identifier
builder.addExtension(X509Extension.subjectKeyIdentifier, false, JcaX509ExtensionUtils.createSubjectKeyIdentifier(subjectKey));
// Add basic constraints
builder.addExtension(X509Extension.basicConstraints, true, new BasicConstraints(false));
// Add key usage
KeyUsage keyUsage = new KeyUsage(KeyUsage.keyEncipherment|KeyUsage.digitalSignature);
builder.addExtension(X509Extension.keyUsage, true, keyUsage);
// Add extended key usage
ExtendedKeyUsage extKeyUsage = new ExtendedKeyUsage(KeyPurposeId.id_kp_serverAuth);
builder.addExtension(X509Extension.extendedKeyUsage, false, extKeyUsage);
return builder.build(getCertSigner(issuerKey));
}
更新:根据 Martin Nielsen 的评论修复了代码。
关于java - 为 SSL 通信创建证书,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20175447/