203 lines
7.7 KiB
Java
203 lines
7.7 KiB
Java
package me.hatter.tools.yubikeyca;
|
|
|
|
import me.hatter.tools.commons.collection.Tuple2;
|
|
import me.hatter.tools.commons.log.LogConfig;
|
|
import me.hatter.tools.commons.log.LogTool;
|
|
import me.hatter.tools.commons.log.LogTools;
|
|
import me.hatter.tools.commons.security.cert.X509CertUtil;
|
|
import me.hatter.tools.commons.security.key.KeyPairTool;
|
|
import me.hatter.tools.commons.security.key.KeyUtil;
|
|
import me.hatter.tools.commons.security.key.PKType;
|
|
import me.hatter.tools.commons.string.StringUtil;
|
|
import me.hatter.tools.crypto.ca.CertificateAuthority;
|
|
import me.hatter.tools.yubikeyca.cardcli.CardCliPivCustomerSigner;
|
|
import me.hatter.tools.yubikeyca.cardcli.CardCliUtil;
|
|
import me.hatter.tools.yubikeyca.hatterink.CertificateUtil;
|
|
|
|
import java.security.KeyPair;
|
|
import java.security.PublicKey;
|
|
import java.security.cert.X509Certificate;
|
|
import java.util.Arrays;
|
|
import java.util.Optional;
|
|
|
|
public class YubikeyCaMain {
|
|
private static final LogTool log;
|
|
|
|
static {
|
|
LogConfig.initMuteInfoMode();
|
|
log = LogTools.getLogTool(YubikeyCaMain.class);
|
|
}
|
|
|
|
public static void main(String[] stringArgs) {
|
|
final YubikeyCaArgs args = YubikeyCaArgsUtil.parseArgs(stringArgs);
|
|
if (args == null) {
|
|
return;
|
|
}
|
|
if (args.generateKeypair) {
|
|
generateKeyPair(args);
|
|
return;
|
|
}
|
|
if (args.issueRootCa) {
|
|
issueRootCa(args);
|
|
return;
|
|
}
|
|
if (args.issueIntermediateCa) {
|
|
issueIntermediateCa(args);
|
|
return;
|
|
}
|
|
if (args.issueServerCa || args.issueClientCa) {
|
|
issueServerClientCa(args);
|
|
return;
|
|
}
|
|
|
|
log.error("Unknown command, use --help for help");
|
|
}
|
|
|
|
private static void issueServerClientCa(YubikeyCaArgs args) {
|
|
if (checkCertificateArgs(args)) return;
|
|
if (StringUtil.isEmpty(args.intermediateCaId)) {
|
|
log.error("Intermediate CA id is required.");
|
|
return;
|
|
}
|
|
if (StringUtil.isEmpty(args.keypairType)) {
|
|
log.error("Keypair type is required.");
|
|
return;
|
|
}
|
|
if (args.issueServerCa && (args.dnsNames == null || args.dnsNames.length == 0)) {
|
|
log.error("DNS name is required.");
|
|
return;
|
|
}
|
|
|
|
|
|
final PKType pkType = getPkTypeFromArgs(args);
|
|
if (pkType == null) return;
|
|
|
|
final X509Certificate intermediateCertificate = CertificateUtil.getCertificate(args.pin, args.intermediateCaId);
|
|
|
|
final Tuple2<String, PublicKey> signPivPublicKey = CardCliUtil.getPivPublicKey(args.signSlot);
|
|
final String signAlgorithm = signPivPublicKey.getVal1();
|
|
|
|
final KeyPair keyPair = KeyPairTool.instance(pkType).generateKeyPair().getKeyPair();
|
|
|
|
final String cardCliCmd = CardCliUtil.getCardCliCmd();
|
|
final CertificateAuthority ca = CertificateAuthority.instance()
|
|
.subject(args.subject)
|
|
.signCert(intermediateCertificate)
|
|
.certPubKey(keyPair.getPublic())
|
|
.validYears(2)
|
|
.customerSigner(new CardCliPivCustomerSigner(args.pin, args.signSlot, signAlgorithm, cardCliCmd));
|
|
|
|
final X509Certificate cert;
|
|
if (args.issueServerCa) {
|
|
cert = ca.createServerCert(Arrays.asList(args.dnsNames));
|
|
} else {
|
|
cert = ca.createClientCert();
|
|
}
|
|
final String certPem = X509CertUtil.serializeX509CertificateToPEM(cert);
|
|
final String privateKeyPem = KeyUtil.serializePrivateKeyToPEM(keyPair.getPrivate());
|
|
|
|
log.info("Issued CA: \n" + certPem);
|
|
if (args.addToRemote) {
|
|
CertificateUtil.addCertificate(args.pin, args.intermediateCaId, args.memo, certPem, privateKeyPem);
|
|
} else {
|
|
log.info("Issued CA private Key: \n" + privateKeyPem);
|
|
}
|
|
}
|
|
|
|
private static void issueIntermediateCa(YubikeyCaArgs args) {
|
|
if (checkCertificateArgs(args)) return;
|
|
if (StringUtil.isEmpty(args.rootCaId)) {
|
|
log.error("Root CA id is required.");
|
|
return;
|
|
}
|
|
if (StringUtil.isEmpty(args.certSlot)) {
|
|
log.error("Cert slot is required.");
|
|
return;
|
|
}
|
|
|
|
final X509Certificate rootCertificate = CertificateUtil.getCertificate(args.pin, args.rootCaId);
|
|
|
|
final Tuple2<String, PublicKey> certPivPublicKey = CardCliUtil.getPivPublicKey(args.certSlot);
|
|
final String signAlgorithm = certPivPublicKey.getVal1();
|
|
final PublicKey certPublicKey = certPivPublicKey.getVal2();
|
|
|
|
final String cardCliCmd = CardCliUtil.getCardCliCmd();
|
|
final X509Certificate intermediateCa = CertificateAuthority.instance()
|
|
.subject(args.subject)
|
|
.signCert(rootCertificate)
|
|
.certPubKey(certPublicKey)
|
|
.validYears(10)
|
|
.customerSigner(new CardCliPivCustomerSigner(args.pin, args.signSlot, signAlgorithm, cardCliCmd))
|
|
.createIntermediateCert();
|
|
final String intermediateCaPem = X509CertUtil.serializeX509CertificateToPEM(intermediateCa);
|
|
|
|
log.info("Issued intermediate CA: " + intermediateCaPem);
|
|
if (args.addToRemote) {
|
|
CertificateUtil.addCertificate(args.pin, args.rootCaId, args.memo, intermediateCaPem, null);
|
|
}
|
|
}
|
|
|
|
private static void issueRootCa(YubikeyCaArgs args) {
|
|
if (checkCertificateArgs(args)) return;
|
|
|
|
final Tuple2<String, PublicKey> signPivPublicKey = CardCliUtil.getPivPublicKey(args.signSlot);
|
|
final String signAlgorithm = signPivPublicKey.getVal1();
|
|
final PublicKey certPublicKey = signPivPublicKey.getVal2();
|
|
|
|
final String cardCliCmd = CardCliUtil.getCardCliCmd();
|
|
final X509Certificate rootCa = CertificateAuthority.instance()
|
|
.subject(args.subject)
|
|
.certPubKey(certPublicKey)
|
|
.validYears(40)
|
|
.customerSigner(new CardCliPivCustomerSigner(args.pin, args.signSlot, signAlgorithm, cardCliCmd))
|
|
.createCA();
|
|
final String rootCaPem = X509CertUtil.serializeX509CertificateToPEM(rootCa);
|
|
|
|
log.info("Issued root CA: " + rootCaPem);
|
|
if (args.addToRemote) {
|
|
CertificateUtil.addCertificate(args.pin, null, args.memo, rootCaPem, null);
|
|
}
|
|
}
|
|
|
|
private static boolean checkCertificateArgs(YubikeyCaArgs args) {
|
|
if (StringUtil.isEmpty(args.signSlot)) {
|
|
log.error("Sign slot is required.");
|
|
return true;
|
|
}
|
|
if (StringUtil.isEmpty(args.subject)) {
|
|
log.error("Certificate subject is required.");
|
|
return true;
|
|
}
|
|
if (StringUtil.isEmpty(args.pin)) {
|
|
log.error("PIV PIN is required.");
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private static void generateKeyPair(YubikeyCaArgs args) {
|
|
final PKType pkType = getPkTypeFromArgs(args);
|
|
if (pkType == null) return;
|
|
|
|
final KeyPair keyPair = KeyPairTool.instance(pkType).generateKeyPair().getKeyPair();
|
|
|
|
System.out.println("Private key:\n" + KeyUtil.serializePrivateKeyToPEM(keyPair.getPrivate()) + "\n");
|
|
System.out.println("Public key: \n" + KeyUtil.serializePublicKeyToPEM(keyPair.getPublic()) + "\n");
|
|
}
|
|
|
|
private static PKType getPkTypeFromArgs(YubikeyCaArgs args) {
|
|
if (StringUtil.isEmpty(args.keypairType)) {
|
|
log.error("Keypair type is required.");
|
|
return null;
|
|
}
|
|
Optional<PKType> pkTypeOpt = Arrays.stream(PKType.values())
|
|
.filter(t -> t.name().equalsIgnoreCase(args.keypairType))
|
|
.findFirst();
|
|
if (!pkTypeOpt.isPresent()) {
|
|
log.error("Invalid keypair type: " + args.keypairType);
|
|
return null;
|
|
}
|
|
return pkTypeOpt.get();
|
|
}
|
|
}
|