feat: server/client ca
This commit is contained in:
@@ -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]
|
||||||
|
```
|
||||||
|
|||||||
@@ -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," +
|
||||||
|
|||||||
@@ -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");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user