forked from woggioni/rbcs
227 lines
8.5 KiB
Java
227 lines
8.5 KiB
Java
package net.woggioni.gbcs.utils;
|
|
|
|
import org.bouncycastle.asn1.DERSequence;
|
|
import org.bouncycastle.asn1.x500.X500Name;
|
|
import org.bouncycastle.asn1.x509.BasicConstraints;
|
|
import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
|
|
import org.bouncycastle.asn1.x509.Extension;
|
|
import org.bouncycastle.asn1.x509.GeneralName;
|
|
import org.bouncycastle.asn1.x509.GeneralNames;
|
|
import org.bouncycastle.asn1.x509.KeyPurposeId;
|
|
import org.bouncycastle.asn1.x509.KeyUsage;
|
|
import org.bouncycastle.asn1.x509.SubjectAltPublicKeyInfo;
|
|
import org.bouncycastle.cert.X509v3CertificateBuilder;
|
|
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
|
|
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
|
|
import org.bouncycastle.operator.ContentSigner;
|
|
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
|
|
|
|
import java.io.FileOutputStream;
|
|
import java.math.BigInteger;
|
|
import java.net.InetAddress;
|
|
import java.security.KeyPair;
|
|
import java.security.KeyPairGenerator;
|
|
import java.security.PrivateKey;
|
|
import java.security.SecureRandom;
|
|
import java.security.cert.X509Certificate;
|
|
import java.time.Instant;
|
|
import java.time.temporal.ChronoUnit;
|
|
import java.util.Date;
|
|
|
|
public class CertificateUtils {
|
|
|
|
public record X509Credentials(
|
|
KeyPair keyPair,
|
|
X509Certificate certificate
|
|
){ }
|
|
public static class CertificateAuthority {
|
|
private final PrivateKey privateKey;
|
|
private final X509Certificate certificate;
|
|
|
|
public CertificateAuthority(PrivateKey privateKey, X509Certificate certificate) {
|
|
this.privateKey = privateKey;
|
|
this.certificate = certificate;
|
|
}
|
|
|
|
public PrivateKey getPrivateKey() { return privateKey; }
|
|
public X509Certificate getCertificate() { return certificate; }
|
|
}
|
|
|
|
/**
|
|
* Creates a new Certificate Authority (CA)
|
|
* @param commonName The CA's common name
|
|
* @param validityDays How long the CA should be valid for
|
|
* @return The generated CA containing both private key and certificate
|
|
*/
|
|
public static X509Credentials createCertificateAuthority(String commonName, int validityDays)
|
|
throws Exception {
|
|
// Generate key pair
|
|
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
|
|
keyPairGenerator.initialize(4096);
|
|
KeyPair keyPair = keyPairGenerator.generateKeyPair();
|
|
|
|
// Prepare certificate data
|
|
X500Name issuerName = new X500Name("CN=" + commonName);
|
|
BigInteger serialNumber = new BigInteger(160, new SecureRandom());
|
|
Instant now = Instant.now();
|
|
Date startDate = Date.from(now);
|
|
Date endDate = Date.from(now.plus(validityDays, ChronoUnit.DAYS));
|
|
|
|
// Create certificate builder
|
|
X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(
|
|
issuerName,
|
|
serialNumber,
|
|
startDate,
|
|
endDate,
|
|
issuerName,
|
|
keyPair.getPublic()
|
|
);
|
|
|
|
// Add CA extensions
|
|
certBuilder.addExtension(
|
|
Extension.basicConstraints,
|
|
true,
|
|
new BasicConstraints(true)
|
|
);
|
|
certBuilder.addExtension(
|
|
Extension.keyUsage,
|
|
true,
|
|
new KeyUsage(KeyUsage.keyCertSign | KeyUsage.cRLSign)
|
|
);
|
|
|
|
// Sign the certificate
|
|
ContentSigner signer = new JcaContentSignerBuilder("SHA256withRSA")
|
|
.build(keyPair.getPrivate());
|
|
X509Certificate cert = new JcaX509CertificateConverter()
|
|
.getCertificate(certBuilder.build(signer));
|
|
|
|
return new X509Credentials(keyPair, cert);
|
|
}
|
|
|
|
/**
|
|
* Creates a server certificate signed by the CA
|
|
* @param ca The Certificate Authority to sign with
|
|
* @param subjectName The server's common name
|
|
* @param validityDays How long the certificate should be valid for
|
|
* @return KeyPair containing the server's private key and certificate
|
|
*/
|
|
public static X509Credentials createServerCertificate(X509Credentials ca, X500Name subjectName, int validityDays)
|
|
throws Exception {
|
|
// Generate server key pair
|
|
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
|
|
keyPairGenerator.initialize(2048);
|
|
KeyPair serverKeyPair = keyPairGenerator.generateKeyPair();
|
|
|
|
// Prepare certificate data
|
|
X500Name issuerName = new X500Name(ca.certificate().getSubjectX500Principal().getName());
|
|
BigInteger serialNumber = new BigInteger(160, new SecureRandom());
|
|
Instant now = Instant.now();
|
|
Date startDate = Date.from(now);
|
|
Date endDate = Date.from(now.plus(validityDays, ChronoUnit.DAYS));
|
|
|
|
// Create certificate builder
|
|
X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(
|
|
issuerName,
|
|
serialNumber,
|
|
startDate,
|
|
endDate,
|
|
subjectName,
|
|
serverKeyPair.getPublic()
|
|
);
|
|
|
|
// Add server certificate extensions
|
|
certBuilder.addExtension(
|
|
Extension.basicConstraints,
|
|
true,
|
|
new BasicConstraints(false)
|
|
);
|
|
certBuilder.addExtension(
|
|
Extension.keyUsage,
|
|
true,
|
|
new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyEncipherment)
|
|
);
|
|
certBuilder.addExtension(
|
|
Extension.extendedKeyUsage,
|
|
true,
|
|
new ExtendedKeyUsage(new KeyPurposeId[]{KeyPurposeId.id_kp_serverAuth})
|
|
);
|
|
GeneralNames subjectAltNames = GeneralNames.getInstance(
|
|
new DERSequence(
|
|
new GeneralName[] {
|
|
new GeneralName(GeneralName.iPAddress, "127.0.0.1")
|
|
}
|
|
)
|
|
);
|
|
certBuilder.addExtension(
|
|
Extension.subjectAlternativeName,
|
|
true,
|
|
subjectAltNames
|
|
);
|
|
|
|
// Sign the certificate
|
|
ContentSigner signer = new JcaContentSignerBuilder("SHA256withRSA")
|
|
.build(ca.keyPair().getPrivate());
|
|
X509Certificate cert = new JcaX509CertificateConverter()
|
|
.getCertificate(certBuilder.build(signer));
|
|
|
|
|
|
return new X509Credentials(serverKeyPair, cert);
|
|
}
|
|
|
|
/**
|
|
* Creates a client certificate signed by the CA
|
|
* @param ca The Certificate Authority to sign with
|
|
* @param subjectName The client's common name
|
|
* @param validityDays How long the certificate should be valid for
|
|
* @return KeyPair containing the client's private key and certificate
|
|
*/
|
|
public static X509Credentials createClientCertificate(X509Credentials ca, X500Name subjectName, int validityDays)
|
|
throws Exception {
|
|
// Generate client key pair
|
|
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
|
|
keyPairGenerator.initialize(2048);
|
|
KeyPair clientKeyPair = keyPairGenerator.generateKeyPair();
|
|
|
|
// Prepare certificate data
|
|
X500Name issuerName = new X500Name(ca.certificate().getSubjectX500Principal().getName());
|
|
BigInteger serialNumber = new BigInteger(160, new SecureRandom());
|
|
Instant now = Instant.now();
|
|
Date startDate = Date.from(now);
|
|
Date endDate = Date.from(now.plus(validityDays, ChronoUnit.DAYS));
|
|
|
|
// Create certificate builder
|
|
X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(
|
|
issuerName,
|
|
serialNumber,
|
|
startDate,
|
|
endDate,
|
|
subjectName,
|
|
clientKeyPair.getPublic()
|
|
);
|
|
|
|
// Add client certificate extensions
|
|
certBuilder.addExtension(
|
|
Extension.basicConstraints,
|
|
true,
|
|
new BasicConstraints(false)
|
|
);
|
|
certBuilder.addExtension(
|
|
Extension.keyUsage,
|
|
true,
|
|
new KeyUsage(KeyUsage.digitalSignature)
|
|
);
|
|
certBuilder.addExtension(
|
|
Extension.extendedKeyUsage,
|
|
true,
|
|
new ExtendedKeyUsage(new KeyPurposeId[]{KeyPurposeId.id_kp_clientAuth})
|
|
);
|
|
|
|
// Sign the certificate
|
|
ContentSigner signer = new JcaContentSignerBuilder("SHA256withRSA")
|
|
.build(ca.keyPair().getPrivate());
|
|
X509Certificate cert = new JcaX509CertificateConverter()
|
|
.getCertificate(certBuilder.build(signer));
|
|
|
|
return new X509Credentials(clientKeyPair, cert);
|
|
}
|
|
} |