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
```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' \
--pin ****** \
[--add-to-remote]
@@ -21,10 +21,30 @@ $ java -jar yubikey-ca-java.jar --generate-root-ca \
# Issue Intermediate CA
```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' \
--cert-slot 89 --root-ca-id 39 \
--pin ****** \
[--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")
boolean generateKeypair = false;
@CommandLine.Option(names = {"--generate-root-ca"}, description = "Generate root CA")
boolean generateRootCa = false;
@CommandLine.Option(names = {"--issue-root-ca"}, description = "Issue root CA")
boolean issueRootCa = false;
@CommandLine.Option(names = {"--generate-intermediate-ca"}, description = "Generate intermediate CA")
boolean generateIntermediateCa = false;
@CommandLine.Option(names = {"--issue-intermediate-ca"}, description = "Issue intermediate CA")
boolean issueIntermediateCa = false;
@CommandLine.Option(names = {"--generate-server-ca"}, description = "Generate server CA")
boolean generateServerCa = false;
@CommandLine.Option(names = {"--issue-server-ca"}, description = "Issue server CA")
boolean issueServerCa = false;
@CommandLine.Option(names = {"--generate-client-ca"}, description = "Generate client CA")
boolean generateClientCa = false;
@CommandLine.Option(names = {"--issue-client-ca"}, description = "Issue client CA")
boolean issueClientCa = false;
@CommandLine.Option(names = {"--subject"}, description = "Certificate subject")
String subject;
@@ -29,6 +29,12 @@ public class YubikeyCaArgs {
@CommandLine.Option(names = {"--root-ca-id"}, description = "Root certificate ID")
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." +
" RSA1024, RSA2048, RSA3072, RSA4096," +
" secp192k1, secp192r1, secp224k1, secp256k1," +

View File

@@ -37,19 +37,72 @@ public class YubikeyCaMain {
generateKeyPair(args);
return;
}
if (args.generateRootCa) {
generateRootCa(args);
if (args.issueRootCa) {
issueRootCa(args);
return;
}
if (args.generateIntermediateCa) {
generateIntermediateCa(args);
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 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 (StringUtil.isEmpty(args.rootCaId)) {
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;
final JSONObject signPivMetaJsonObject = CardCliUtil.getPivMeta(args.signSlot);
@@ -123,22 +176,27 @@ public class YubikeyCaMain {
}
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;
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;
return null;
}
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");
return pkTypeOpt.get();
}
}