feat: server/client ca

This commit is contained in:
2023-05-20 19:07:47 +08:00
parent fa4844d472
commit 5f6aff38ce
3 changed files with 108 additions and 24 deletions

View File

@@ -12,7 +12,7 @@ $ java -jar yubikey-ca-java.jar --generate-keypair --keypair-type secp256r1
# Issue ROOT CA # Issue ROOT CA
```shell ```shell
$ java -jar yubikey-ca-java.jar --generate-root-ca \ $ java -jar yubikey-ca-java.jar --issue-root-ca \
--sign-slot 88 --subject 'CN=Hatter Yubikey EC Root CA' \ --sign-slot 88 --subject 'CN=Hatter Yubikey EC Root CA' \
--pin ****** \ --pin ****** \
[--add-to-remote] [--add-to-remote]
@@ -21,10 +21,30 @@ $ java -jar yubikey-ca-java.jar --generate-root-ca \
# Issue Intermediate CA # Issue Intermediate CA
```shell ```shell
$ java -jar yubikey-ca-java.jar --generate-intermediate-ca \ $ java -jar yubikey-ca-java.jar --issue-intermediate-ca \
--sign-slot 88 --subject 'CN=Hatter Yubikey EC Intermediate CA' \ --sign-slot 88 --subject 'CN=Hatter Yubikey EC Intermediate CA' \
--cert-slot 89 --root-ca-id 39 \ --cert-slot 89 --root-ca-id 39 \
--pin ****** \ --pin ****** \
[--add-to-remote] [--add-to-remote]
``` ```
# Issue Server CA
```shell
$ java -jar yubikey-ca-java.jar --issue-server-ca \
--sign-slot 89 --subject 'CN=hatter-test' \
--intermediate-ca-id 40 --keypair-type secp256r1 \
--dns-name a.example.com --dns-name b.example.com \
--pin 123456 \
[--add-to-remote]
```
# Issue Client CA
```shell
$ java -jar yubikey-ca-java.jar --issue-client-ca \
--sign-slot 89 --subject 'CN=hatter-test' \
--intermediate-ca-id 40 --keypair-type secp256r1 \
--pin 123456 \
[--add-to-remote]
```

View File

@@ -11,17 +11,17 @@ public class YubikeyCaArgs {
@CommandLine.Option(names = {"--generate-keypair"}, description = "Generate keypair") @CommandLine.Option(names = {"--generate-keypair"}, description = "Generate keypair")
boolean generateKeypair = false; boolean generateKeypair = false;
@CommandLine.Option(names = {"--generate-root-ca"}, description = "Generate root CA") @CommandLine.Option(names = {"--issue-root-ca"}, description = "Issue root CA")
boolean generateRootCa = false; boolean issueRootCa = false;
@CommandLine.Option(names = {"--generate-intermediate-ca"}, description = "Generate intermediate CA") @CommandLine.Option(names = {"--issue-intermediate-ca"}, description = "Issue intermediate CA")
boolean generateIntermediateCa = false; boolean issueIntermediateCa = false;
@CommandLine.Option(names = {"--generate-server-ca"}, description = "Generate server CA") @CommandLine.Option(names = {"--issue-server-ca"}, description = "Issue server CA")
boolean generateServerCa = false; boolean issueServerCa = false;
@CommandLine.Option(names = {"--generate-client-ca"}, description = "Generate client CA") @CommandLine.Option(names = {"--issue-client-ca"}, description = "Issue client CA")
boolean generateClientCa = false; boolean issueClientCa = false;
@CommandLine.Option(names = {"--subject"}, description = "Certificate subject") @CommandLine.Option(names = {"--subject"}, description = "Certificate subject")
String subject; String subject;
@@ -29,6 +29,12 @@ public class YubikeyCaArgs {
@CommandLine.Option(names = {"--root-ca-id"}, description = "Root certificate ID") @CommandLine.Option(names = {"--root-ca-id"}, description = "Root certificate ID")
String rootCaId; String rootCaId;
@CommandLine.Option(names = {"--intermediate-ca-id"}, description = "Intermediate certificate ID")
String intermediateCaId;
@CommandLine.Option(names = {"--dns-name"}, description = "DNS name (Subject alt name)")
String[] dnsNames;
@CommandLine.Option(names = {"--keypair-type"}, description = "Keypair type, e.g." + @CommandLine.Option(names = {"--keypair-type"}, description = "Keypair type, e.g." +
" RSA1024, RSA2048, RSA3072, RSA4096," + " RSA1024, RSA2048, RSA3072, RSA4096," +
" secp192k1, secp192r1, secp224k1, secp256k1," + " secp192k1, secp192r1, secp224k1, secp256k1," +

View File

@@ -37,19 +37,72 @@ public class YubikeyCaMain {
generateKeyPair(args); generateKeyPair(args);
return; return;
} }
if (args.generateRootCa) { if (args.issueRootCa) {
generateRootCa(args); issueRootCa(args);
return; return;
} }
if (args.generateIntermediateCa) { if (args.issueIntermediateCa) {
generateIntermediateCa(args); issueIntermediateCa(args);
return;
}
if (args.issueServerCa || args.issueClientCa) {
issueServerClientCa(args);
return; return;
} }
log.error("Unknown command, use --help for help"); log.error("Unknown command, use --help for help");
} }
private static void generateIntermediateCa(YubikeyCaArgs args) { 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 JSONObject signPivMetaJsonObject = CardCliUtil.getPivMeta(args.signSlot);
final String signAlgorithm = signPivMetaJsonObject.getString("algorithm");
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: " + certPem);
if (args.addToRemote) {
CertificateUtil.addCertificate(args.pin, args.intermediateCaId, args.memo, certPem, privateKeyPem);
}
}
private static void issueIntermediateCa(YubikeyCaArgs args) {
if (checkCertificateArgs(args)) return; if (checkCertificateArgs(args)) return;
if (StringUtil.isEmpty(args.rootCaId)) { if (StringUtil.isEmpty(args.rootCaId)) {
log.error("Root CA id is required."); log.error("Root CA id is required.");
@@ -83,7 +136,7 @@ public class YubikeyCaMain {
} }
} }
private static void generateRootCa(YubikeyCaArgs args) { private static void issueRootCa(YubikeyCaArgs args) {
if (checkCertificateArgs(args)) return; if (checkCertificateArgs(args)) return;
final JSONObject signPivMetaJsonObject = CardCliUtil.getPivMeta(args.signSlot); final JSONObject signPivMetaJsonObject = CardCliUtil.getPivMeta(args.signSlot);
@@ -123,22 +176,27 @@ public class YubikeyCaMain {
} }
private static void generateKeyPair(YubikeyCaArgs args) { 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)) { if (StringUtil.isEmpty(args.keypairType)) {
log.error("Keypair type is required."); log.error("Keypair type is required.");
return; return null;
} }
Optional<PKType> pkTypeOpt = Arrays.stream(PKType.values()) Optional<PKType> pkTypeOpt = Arrays.stream(PKType.values())
.filter(t -> t.name().equalsIgnoreCase(args.keypairType)) .filter(t -> t.name().equalsIgnoreCase(args.keypairType))
.findFirst(); .findFirst();
if (!pkTypeOpt.isPresent()) { if (!pkTypeOpt.isPresent()) {
log.error("Invalid keypair type: " + args.keypairType); log.error("Invalid keypair type: " + args.keypairType);
return; return null;
} }
return pkTypeOpt.get();
final KeyPair keyPair = KeyPairTool.instance(pkTypeOpt.get())
.generateKeyPair().getKeyPair();
System.out.println("Private key:\n" + KeyUtil.serializePrivateKeyToPEM(keyPair.getPrivate()) + "\n");
System.out.println("Public key: \n" + KeyUtil.serializePublicKeyToPEM(keyPair.getPublic()) + "\n");
} }
} }