java - 为 SSL 通信创建证书

标签 java ssl x509certificate bouncycastle

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

相关文章:

powershell - 在其他用户的当前用户存储上安装证书

java - 验证证书链时是否应将中间证书添加为 TrustAnchors?

java - Java中的单例(线程安全) - 一旦没有人使用该对象,如何销毁成员?

java - 隐藏 BIRTH 中的网格/表格,没有来自数据源的结果

java - 日期/时间转换/算术

asp.net-mvc - FilterAttributes 是否在 ASP.NET MVC 中缓存?

wcf - 如何在具有传输安全性的 WCF 调用中使用自签名客户端证书?

javascript - 从 glassfish 服务器日志上的 javascript 函数打印 JSON 对象

通过 netsh 添加 IP 时 IIS 7.0-7.5 IP 地址重新绑定(bind)

openssl - 是否有一个 OPENSSL api 可以解析证书并判断它是客户端证书还是服务器证书?