为SSL通信创建证书

Mar*_*sen 6 java ssl bouncycastle x509certificate

我正在开发一个分布式应用程序,它具有许多唯一标识的从属进程,这些进程将通过启用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
Run Code Online (Sandbox Code Playgroud)

第一个问题:这种证书链是解决问题的正确方法吗?我认为这是实现主设备和从设备的最简单方式,所有设备都具有唯一标识,而无需对每个证书进行CA签名.

第二个问题:我如何以编程方式在java中创建SSL证书?我想在这里创建链中的最后一个证书,假设我现在已经拥有"主服务器证书".到目前为止,我已经为证书生成了一个密钥(其中type是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);
Run Code Online (Sandbox Code Playgroud)

我不认为将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);
Run Code Online (Sandbox Code Playgroud)

我错过了其他任何步骤吗?

Jcs*_*Jcs 1

从技术角度来看,您的解决方案是正确的。但是,不要忘记安全考虑:谁可以请求证书、如何执行身份验证、如何将证书/私钥分发到服务器......

这些元素对于证书生成是必需的:

  • 主题名称
  • 发行人名称
  • 证书序列号
  • 主题公钥
  • 有效日期(不早于,不晚于)

添加一些扩展也是一个好习惯:

  • 主题密钥标识符
  • 授权密钥标识符
  • 基本约束
  • 按键用法
  • 扩展密钥用法

此代码片段概述了证书的生成:

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));
}
Run Code Online (Sandbox Code Playgroud)

更新:根据 Martin Nielsen 的评论修复了代码。