Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating certificates for SSL communication

I am working on a distributed application with a number of uniquely identified slave processes that will communicate with a master application via SSL enabled sockets. The application is written in java.

I need some help understanding SSLSockets, or rather, the certificates they use.

What i am looking for is someone who can tell me if i have understood the basic workings of certificate chains correctly, but i wouldn't say no to a code sample either.

I would like a setup where the server itself has a CA signed certificate, and every slave will get their own certificate created by the master application.

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

First question: Is this kind of certificate chain the correct way to tackle the problem? I am thinking this is the simplest way of achieving the master and slaves all have a unique identity without having to CA sign every certificate.

Second question: How do i programatically go about creating an SSL certificate in java? I am trying to create the last certificate in the chain here, assuming i already have the "Main server cert" for now. I have gotten so far as generating a key for the certificate (Where type is 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);

I don't assume that setting the serverCert as the issuer is enough to sign the certificate? As far as i have understood i need to sign the new certificate with the next certificate in the chain somehow, but how do i do that? Do i sign the certificate with the serverCert's private key like:

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);

Are there any other steps i missed?

like image 239
Martin Nielsen Avatar asked Nov 24 '13 14:11

Martin Nielsen


1 Answers

From a technical point of view your solution is correct. However do not forget the security considerations: who can request a certificate, how the authentication is performed, how the certificates/private keys are distributed to the servers...

These elements are mandatory for a certificate generation:

  • Subject Name
  • Issuer name
  • certificate serial number
  • subject public key
  • validity dates (not before, not after)

It is also a good practice to add some extensions:

  • Subject Key Identifier
  • Authority Key Indentifier
  • Basic Constraints
  • Key Usage
  • Extended Key Usages

This code snippet outlines the certificate generation:

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));
}

UPDATE: fixed the code according to Martin Nielsen's comment.

like image 104
Jcs Avatar answered Nov 18 '22 21:11

Jcs