| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,158 @@ | ||
| // | ||
| // Copyright Red Hat, Inc. | ||
| // | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
| // | ||
| package org.dogtagpki.acme.issuer; | ||
|
|
||
| import java.io.PrintWriter; | ||
| import java.io.StringWriter; | ||
| import java.math.BigInteger; | ||
| import java.nio.file.Path; | ||
| import java.nio.file.Paths; | ||
| import java.security.cert.X509Certificate; | ||
|
|
||
| import org.apache.commons.codec.binary.Base64; | ||
| import org.dogtagpki.acme.database.ACMEDatabase; | ||
| import org.dogtagpki.acme.server.ACMEEngine; | ||
| import org.dogtagpki.nss.NSSDatabase; | ||
| import org.dogtagpki.nss.NSSExtensionGenerator; | ||
| import org.mozilla.jss.CryptoManager; | ||
| import org.mozilla.jss.netscape.security.pkcs.PKCS10; | ||
| import org.mozilla.jss.netscape.security.util.Cert; | ||
| import org.mozilla.jss.netscape.security.util.Utils; | ||
| import org.mozilla.jss.netscape.security.x509.CertificateExtensions; | ||
| import org.mozilla.jss.netscape.security.x509.X509CertImpl; | ||
|
|
||
| import com.netscape.cmsutil.password.IPasswordStore; | ||
| import com.netscape.cmsutil.password.PlainPasswordFile; | ||
|
|
||
| /** | ||
| * @author Endi S. Dewata | ||
| */ | ||
| public class NSSIssuer extends ACMEIssuer { | ||
|
|
||
| public static org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(NSSIssuer.class); | ||
|
|
||
| NSSDatabase nssDatabase; | ||
| IPasswordStore passwordStore; | ||
|
|
||
| org.mozilla.jss.crypto.X509Certificate issuer; | ||
|
|
||
| NSSExtensionGenerator extGenerator; | ||
| Integer monthsValid; | ||
|
|
||
| public void init() throws Exception { | ||
|
|
||
| logger.info("Initializing NSS issuer"); | ||
|
|
||
| Path instanceDir = Paths.get(System.getProperty("catalina.base")); | ||
|
|
||
| String database = config.getParameter("database"); | ||
| if (database == null) database = "conf/alias"; | ||
|
|
||
| Path databasePath = instanceDir.resolve(database); | ||
| logger.info("- database: " + databasePath); | ||
|
|
||
| nssDatabase = new NSSDatabase(databasePath); | ||
|
|
||
| String passwords = config.getParameter("passwords"); | ||
| if (passwords == null) passwords = "conf/password.conf"; | ||
|
|
||
| Path passwordsPath = instanceDir.resolve(passwords); | ||
| logger.info("- passwords: " + passwordsPath); | ||
|
|
||
| passwordStore = new PlainPasswordFile(); | ||
| passwordStore.init(passwordsPath.toString()); | ||
| nssDatabase.setPasswordStore(passwordStore); | ||
|
|
||
| String nickname = config.getParameter("nickname"); | ||
| if (nickname == null) nickname = "ca_signing"; | ||
| logger.info("- nickname: " + nickname); | ||
|
|
||
| CryptoManager cm = CryptoManager.getInstance(); | ||
| issuer = cm.findCertByNickname(nickname); | ||
|
|
||
| String monthsValid = config.getParameter("monthsValid"); | ||
| if (monthsValid != null) { | ||
| logger.info("- months valid: " + monthsValid); | ||
|
|
||
| this.monthsValid = new Integer(monthsValid); | ||
| } | ||
|
|
||
| String extensions = config.getParameter("extensions"); | ||
| if (extensions == null) extensions = "/usr/share/pki/acme/issuer/nss/sslserver.conf"; | ||
| logger.info("- extensions: " + extensions); | ||
|
|
||
| Path extPath = instanceDir.resolve(extensions); | ||
| extGenerator = new NSSExtensionGenerator(); | ||
| extGenerator.init(extPath.toString()); | ||
| } | ||
|
|
||
| public String issueCertificate(PKCS10 pkcs10) throws Exception { | ||
|
|
||
| logger.info("Issuing certificate"); | ||
|
|
||
| ACMEEngine engine = ACMEEngine.getInstance(); | ||
| ACMEDatabase acmeDatabase = engine.getDatabase(); | ||
|
|
||
| CertificateExtensions extensions = null; | ||
| if (extGenerator != null) { | ||
| extensions = extGenerator.createExtensions(issuer, pkcs10); | ||
| } | ||
|
|
||
| X509Certificate cert = nssDatabase.createCertificate( | ||
| issuer, | ||
| pkcs10, | ||
| monthsValid, | ||
| extensions); | ||
|
|
||
| BigInteger serialNumber = cert.getSerialNumber(); | ||
| String certID = Base64.encodeBase64URLSafeString(serialNumber.toByteArray()); | ||
| acmeDatabase.addCertificate(certID, cert); | ||
|
|
||
| return certID; | ||
| } | ||
|
|
||
| public X509Certificate[] getCACertificateChain() throws Exception { | ||
|
|
||
| CryptoManager cm = CryptoManager.getInstance(); | ||
| org.mozilla.jss.crypto.X509Certificate[] caCertChain = cm.buildCertificateChain(issuer); | ||
|
|
||
| X509Certificate[] caCertChainImpl = new X509Certificate[caCertChain.length]; | ||
| for (int i = 0; i < caCertChain.length; i++) { | ||
| org.mozilla.jss.crypto.X509Certificate cert = caCertChain[i]; | ||
| caCertChainImpl[i] = new X509CertImpl(cert.getEncoded()); | ||
| } | ||
|
|
||
| return caCertChainImpl; | ||
| } | ||
|
|
||
| public String getCertificateChain(String certID) throws Exception { | ||
|
|
||
| logger.info("Retrieving certificate"); | ||
|
|
||
| ACMEEngine engine = ACMEEngine.getInstance(); | ||
| ACMEDatabase acmeDatabase = engine.getDatabase(); | ||
|
|
||
| X509Certificate cert = acmeDatabase.getCertificate(certID); | ||
| X509Certificate[] certChain = getCACertificateChain(); | ||
|
|
||
| StringWriter sw = new StringWriter(); | ||
|
|
||
| try (PrintWriter out = new PrintWriter(sw, true)) { | ||
|
|
||
| out.println(Cert.HEADER); | ||
| out.print(Utils.base64encodeMultiLine(cert.getEncoded())); | ||
| out.println(Cert.FOOTER); | ||
|
|
||
| for (X509Certificate caCert : certChain) { | ||
| out.println(Cert.HEADER); | ||
| out.print(Utils.base64encodeMultiLine(caCert.getEncoded())); | ||
| out.println(Cert.FOOTER); | ||
| } | ||
| } | ||
|
|
||
| return sw.toString(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,113 @@ | ||
| // | ||
| // Copyright Red Hat, Inc. | ||
| // | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
| // | ||
| package org.dogtagpki.acme.server; | ||
|
|
||
| import java.util.Collection; | ||
| import java.util.Date; | ||
|
|
||
| import org.dogtagpki.acme.ACMEAccount; | ||
| import org.dogtagpki.acme.ACMEAuthorization; | ||
| import org.dogtagpki.acme.ACMEChallenge; | ||
| import org.dogtagpki.acme.ACMEOrder; | ||
| import org.dogtagpki.acme.validator.ACMEValidator; | ||
|
|
||
| /** | ||
| * @author Endi S. Dewata | ||
| */ | ||
| public class ACMEChallengeProcessor implements Runnable { | ||
|
|
||
| public static org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(ACMEChallengeProcessor.class); | ||
|
|
||
| ACMEAccount account; | ||
| ACMEAuthorization authorization; | ||
| ACMEChallenge challenge; | ||
| ACMEValidator validator; | ||
|
|
||
| public ACMEChallengeProcessor( | ||
| ACMEAccount account, | ||
| ACMEAuthorization authorization, | ||
| ACMEChallenge challenge, | ||
| ACMEValidator validator) { | ||
|
|
||
| this.account = account; | ||
| this.authorization = authorization; | ||
| this.challenge = challenge; | ||
| this.validator = validator; | ||
| } | ||
|
|
||
| public void run() { | ||
| try { | ||
| processChallenge(); | ||
| } catch (Exception e) { | ||
| logger.error("Unable to process challenge " + challenge.getID() + ": " + e.getMessage(), e); | ||
| } | ||
| } | ||
|
|
||
| public void processChallenge() throws Exception { | ||
|
|
||
| String authzID = authorization.getID(); | ||
| String challengeID = challenge.getID(); | ||
|
|
||
| logger.info("Processing challenge " + challengeID); | ||
|
|
||
| ACMEEngine engine = ACMEEngine.getInstance(); | ||
|
|
||
| try { | ||
| validator.validateChallenge(authorization, challenge); | ||
|
|
||
| } catch (Exception e) { | ||
|
|
||
| // RFC 8555 Section 8.2: Retrying Challenges | ||
| // | ||
| // The server MUST provide information about its retry state to the | ||
| // client via the "error" field in the challenge and the Retry-After | ||
| // HTTP header field in response to requests to the challenge resource. | ||
| // The server MUST add an entry to the "error" field in the challenge | ||
| // after each failed validation query. The server SHOULD set the Retry- | ||
| // After header field to a time after the server's next validation | ||
| // query, since the status of the challenge will not change until that | ||
| // time. | ||
|
|
||
| logger.info("Challenge " + challengeID + " is invalid"); | ||
| challenge.setStatus("invalid"); | ||
| engine.updateAuthorization(account, authorization); | ||
| throw e; | ||
| } | ||
|
|
||
| logger.info("Challenge " + challengeID + " is valid"); | ||
| challenge.setStatus("valid"); | ||
| challenge.setValidationTime(new Date()); | ||
|
|
||
| logger.info("Authorization " + authzID + " is valid"); | ||
| authorization.setStatus("valid"); | ||
|
|
||
| engine.updateAuthorization(account, authorization); | ||
|
|
||
| logger.info("Checking associated pending orders for validity"); | ||
| Collection<ACMEOrder> orders = | ||
| engine.getOrdersByAuthorizationAndStatus(account, authzID, "pending"); | ||
|
|
||
| for (ACMEOrder order : orders) { | ||
| boolean allAuthorizationsValid = true; | ||
|
|
||
| for (String orderAuthzID : order.getAuthzIDs()) { | ||
| ACMEAuthorization authz = engine.getAuthorization(account, orderAuthzID); | ||
| if (!authz.getStatus().equals("valid")) { | ||
| allAuthorizationsValid = false; | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| if (allAuthorizationsValid) { | ||
| logger.info("Order " + order.getID() + " is ready"); | ||
| order.setStatus("ready"); | ||
| engine.updateOrder(account, order); | ||
| } else { | ||
| logger.info("Order " + order.getID() + " is not ready"); | ||
| } | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| // | ||
| // Copyright Red Hat, Inc. | ||
| // | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
| // | ||
| package com.netscape.cmstools.nss; | ||
|
|
||
| import org.dogtagpki.cli.CLI; | ||
|
|
||
| public class NSSCertCLI extends CLI { | ||
|
|
||
| public NSSCertCLI(NSSCLI nssCLI) { | ||
| super("cert", "NSS certificate management commands", nssCLI); | ||
|
|
||
| addModule(new NSSCertImportCLI(this)); | ||
| addModule(new NSSCertRequestCLI(this)); | ||
| addModule(new NSSCertIssueCLI(this)); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,99 @@ | ||
| // | ||
| // Copyright Red Hat, Inc. | ||
| // | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
| // | ||
| package com.netscape.cmstools.nss; | ||
|
|
||
| import java.nio.file.Files; | ||
| import java.nio.file.Paths; | ||
|
|
||
| import org.apache.commons.cli.CommandLine; | ||
| import org.apache.commons.cli.Option; | ||
| import org.apache.commons.io.IOUtils; | ||
| import org.dogtagpki.cli.CommandCLI; | ||
| import org.dogtagpki.nss.NSSDatabase; | ||
| import org.mozilla.jss.netscape.security.util.Cert; | ||
| import org.mozilla.jss.netscape.security.x509.X509CertImpl; | ||
|
|
||
| import com.netscape.certsrv.client.ClientConfig; | ||
| import com.netscape.cmstools.cli.MainCLI; | ||
|
|
||
| public class NSSCertImportCLI extends CommandCLI { | ||
|
|
||
| public static org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(NSSCertImportCLI.class); | ||
|
|
||
| public NSSCertImportCLI(NSSCertCLI nssCertCLI) { | ||
| super("import", "Import certificate", nssCertCLI); | ||
| } | ||
|
|
||
| public void printHelp() { | ||
| formatter.printHelp(getFullName() + " [OPTIONS...] [nickname]", options); | ||
| } | ||
|
|
||
| public void createOptions() { | ||
| Option option = new Option(null, "cert", true, "Certificate to import"); | ||
| option.setArgName("path"); | ||
| options.addOption(option); | ||
|
|
||
| option = new Option(null, "format", true, "Certificate format: PEM (default), DER"); | ||
| option.setArgName("format"); | ||
| options.addOption(option); | ||
|
|
||
| option = new Option(null, "trust", true, "Trust attributes"); | ||
| option.setArgName("attributes"); | ||
| options.addOption(option); | ||
| } | ||
|
|
||
| public void execute(CommandLine cmd) throws Exception { | ||
|
|
||
| String[] cmdArgs = cmd.getArgs(); | ||
| String nickname = null; | ||
|
|
||
| if (cmdArgs.length >= 1) { | ||
| nickname = cmdArgs[0]; | ||
| } | ||
|
|
||
| String filename = cmd.getOptionValue("cert"); | ||
| String format = cmd.getOptionValue("format"); | ||
| String trustAttributes = cmd.getOptionValue("trust"); | ||
|
|
||
| if (trustAttributes == null) | ||
| trustAttributes = ",,"; | ||
|
|
||
| byte[] bytes; | ||
| if (filename == null) { | ||
| // read from standard input | ||
| bytes = IOUtils.toByteArray(System.in); | ||
|
|
||
| } else { | ||
| // read from file | ||
| bytes = Files.readAllBytes(Paths.get(filename)); | ||
| } | ||
|
|
||
| if (format == null || "PEM".equalsIgnoreCase(format)) { | ||
| bytes = Cert.parseCertificate(new String(bytes)); | ||
|
|
||
| } else if ("DER".equalsIgnoreCase(format)) { | ||
| // nothing to do | ||
|
|
||
| } else { | ||
| throw new Exception("Unsupported format: " + format); | ||
| } | ||
|
|
||
| X509CertImpl cert = new X509CertImpl(bytes); | ||
|
|
||
| MainCLI mainCLI = (MainCLI) getRoot(); | ||
| mainCLI.init(); | ||
|
|
||
| ClientConfig clientConfig = mainCLI.getConfig(); | ||
| NSSDatabase nssdb = mainCLI.getNSSDatabase(); | ||
|
|
||
| if (nickname == null) { | ||
| nssdb.addCertificate(cert, trustAttributes); | ||
|
|
||
| } else { | ||
| nssdb.addCertificate(nickname, cert, trustAttributes); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,128 @@ | ||
| // | ||
| // Copyright Red Hat, Inc. | ||
| // | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
| // | ||
| package com.netscape.cmstools.nss; | ||
|
|
||
| import java.nio.file.Files; | ||
| import java.nio.file.Paths; | ||
| import java.security.cert.X509Certificate; | ||
|
|
||
| import org.apache.commons.cli.CommandLine; | ||
| import org.apache.commons.cli.Option; | ||
| import org.dogtag.util.cert.CertUtil; | ||
| import org.dogtagpki.cli.CommandCLI; | ||
| import org.dogtagpki.nss.NSSDatabase; | ||
| import org.dogtagpki.nss.NSSExtensionGenerator; | ||
| import org.mozilla.jss.CryptoManager; | ||
| import org.mozilla.jss.netscape.security.pkcs.PKCS10; | ||
| import org.mozilla.jss.netscape.security.x509.CertificateExtensions; | ||
|
|
||
| import com.netscape.certsrv.client.ClientConfig; | ||
| import com.netscape.cmstools.cli.MainCLI; | ||
|
|
||
| public class NSSCertIssueCLI extends CommandCLI { | ||
|
|
||
| public static org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(NSSCertIssueCLI.class); | ||
|
|
||
| public NSSCertIssueCLI(NSSCertCLI nssCertCLI) { | ||
| super("issue", "Issue certificate", nssCertCLI); | ||
| } | ||
|
|
||
| public void printHelp() { | ||
| formatter.printHelp(getFullName() + " [OPTIONS...]", options); | ||
| } | ||
|
|
||
| public void createOptions() { | ||
| Option option = new Option(null, "issuer", true, "Issuer nickname (default is self-signed)"); | ||
| option.setArgName("nickname"); | ||
| options.addOption(option); | ||
|
|
||
| option = new Option(null, "csr", true, "Certificate signing request"); | ||
| option.setArgName("path"); | ||
| options.addOption(option); | ||
|
|
||
| option = new Option(null, "ext", true, "Certificate extensions configuration"); | ||
| option.setArgName("path"); | ||
| options.addOption(option); | ||
|
|
||
| option = new Option(null, "months-valid", true, "Months valid (default is 3)"); | ||
| option.setArgName("months"); | ||
| options.addOption(option); | ||
|
|
||
| option = new Option(null, "cert", true, "Certificate"); | ||
| option.setArgName("path"); | ||
| options.addOption(option); | ||
|
|
||
| option = new Option(null, "format", true, "Certificate format: PEM (default), DER"); | ||
| option.setArgName("format"); | ||
| options.addOption(option); | ||
| } | ||
|
|
||
| public void execute(CommandLine cmd) throws Exception { | ||
|
|
||
| String issuerNickname = cmd.getOptionValue("issuer"); | ||
| String csrFile = cmd.getOptionValue("csr"); | ||
| String extConf = cmd.getOptionValue("ext"); | ||
| String monthsValid = cmd.getOptionValue("months-valid"); | ||
|
|
||
| if (csrFile == null) { | ||
| throw new Exception("Missing certificate signing request"); | ||
| } | ||
|
|
||
| MainCLI mainCLI = (MainCLI) getRoot(); | ||
| mainCLI.init(); | ||
|
|
||
| ClientConfig clientConfig = mainCLI.getConfig(); | ||
| NSSDatabase nssdb = mainCLI.getNSSDatabase(); | ||
|
|
||
| org.mozilla.jss.crypto.X509Certificate issuer; | ||
| if (issuerNickname == null) { | ||
| issuer = null; | ||
|
|
||
| } else { | ||
| CryptoManager cm = CryptoManager.getInstance(); | ||
| issuer = cm.findCertByNickname(issuerNickname); | ||
| } | ||
|
|
||
| String csrPEM = new String(Files.readAllBytes(Paths.get(csrFile))); | ||
| byte[] csrBytes = CertUtil.parseCSR(csrPEM); | ||
| PKCS10 pkcs10 = new PKCS10(csrBytes); | ||
|
|
||
| CertificateExtensions extensions = null; | ||
| if (extConf != null) { | ||
| NSSExtensionGenerator generator = new NSSExtensionGenerator(); | ||
| generator.init(extConf); | ||
| extensions = generator.createExtensions(issuer, pkcs10); | ||
| } | ||
|
|
||
| X509Certificate cert = nssdb.createCertificate( | ||
| issuer, | ||
| pkcs10, | ||
| monthsValid == null ? null : new Integer(monthsValid), | ||
| extensions); | ||
|
|
||
| String format = cmd.getOptionValue("format"); | ||
| byte[] bytes; | ||
|
|
||
| if (format == null || "PEM".equalsIgnoreCase(format)) { | ||
| bytes = CertUtil.toPEM(cert).getBytes(); | ||
|
|
||
| } else if ("DER".equalsIgnoreCase(format)) { | ||
| bytes = cert.getEncoded(); | ||
|
|
||
| } else { | ||
| throw new Exception("Unsupported format: " + format); | ||
| } | ||
|
|
||
| String filename = cmd.getOptionValue("cert"); | ||
|
|
||
| if (filename != null) { | ||
| Files.write(Paths.get(filename) , bytes); | ||
|
|
||
| } else { | ||
| System.out.write(bytes); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,131 @@ | ||
| // | ||
| // Copyright Red Hat, Inc. | ||
| // | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
| // | ||
| package com.netscape.cmstools.nss; | ||
|
|
||
| import java.nio.file.Files; | ||
| import java.nio.file.Paths; | ||
|
|
||
| import org.apache.commons.cli.CommandLine; | ||
| import org.apache.commons.cli.Option; | ||
| import org.dogtag.util.cert.CertUtil; | ||
| import org.dogtagpki.cli.CommandCLI; | ||
| import org.dogtagpki.nss.NSSDatabase; | ||
| import org.dogtagpki.nss.NSSExtensionGenerator; | ||
| import org.mozilla.jss.netscape.security.pkcs.PKCS10; | ||
| import org.mozilla.jss.netscape.security.x509.CertificateExtensions; | ||
|
|
||
| import com.netscape.certsrv.client.ClientConfig; | ||
| import com.netscape.cmstools.cli.MainCLI; | ||
|
|
||
| public class NSSCertRequestCLI extends CommandCLI { | ||
|
|
||
| public static org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(NSSCertRequestCLI.class); | ||
|
|
||
| public NSSCertRequestCLI(NSSCertCLI nssCertCLI) { | ||
| super("request", "Generate certificate signing request", nssCertCLI); | ||
| } | ||
|
|
||
| public void printHelp() { | ||
| formatter.printHelp(getFullName() + " [OPTIONS...]", options); | ||
| } | ||
|
|
||
| public void createOptions() { | ||
| Option option = new Option(null, "subject", true, "Subject name"); | ||
| option.setArgName("name"); | ||
| options.addOption(option); | ||
|
|
||
| option = new Option(null, "key-id", true, "Key ID"); | ||
| option.setArgName("ID"); | ||
| options.addOption(option); | ||
|
|
||
| option = new Option(null, "key-type", true, "Key type: RSA (default), EC, DSA"); | ||
| option.setArgName("type"); | ||
| options.addOption(option); | ||
|
|
||
| option = new Option(null, "key-size", true, "RSA key size (default is 2048)"); | ||
| option.setArgName("size"); | ||
| options.addOption(option); | ||
|
|
||
| option = new Option(null, "curve", true, "Elliptic curve name"); | ||
| option.setArgName("name"); | ||
| options.addOption(option); | ||
|
|
||
| option = new Option(null, "hash", true, "Hash algorithm"); | ||
| option.setArgName("name"); | ||
| options.addOption(option); | ||
|
|
||
| option = new Option(null, "ext", true, "Certificate extensions configuration"); | ||
| option.setArgName("path"); | ||
| options.addOption(option); | ||
|
|
||
| option = new Option(null, "csr", true, "Certificate signing request"); | ||
| option.setArgName("path"); | ||
| options.addOption(option); | ||
|
|
||
| option = new Option(null, "format", true, "Certificate signing request format: PEM (default), DER"); | ||
| option.setArgName("format"); | ||
| options.addOption(option); | ||
| } | ||
|
|
||
| public void execute(CommandLine cmd) throws Exception { | ||
|
|
||
| String subject = cmd.getOptionValue("subject"); | ||
| String keyID = cmd.getOptionValue("key-id"); | ||
| String keyType = cmd.getOptionValue("key-type"); | ||
| String keySize = cmd.getOptionValue("key-size"); | ||
| String curve = cmd.getOptionValue("curve"); | ||
| String hash = cmd.getOptionValue("hash"); | ||
| String extConf = cmd.getOptionValue("ext"); | ||
|
|
||
| if (subject == null) { | ||
| throw new Exception("Missing subject name"); | ||
| } | ||
|
|
||
| MainCLI mainCLI = (MainCLI) getRoot(); | ||
| mainCLI.init(); | ||
|
|
||
| ClientConfig clientConfig = mainCLI.getConfig(); | ||
| NSSDatabase nssdb = mainCLI.getNSSDatabase(); | ||
|
|
||
| CertificateExtensions extensions = null; | ||
| if (extConf != null) { | ||
| NSSExtensionGenerator generator = new NSSExtensionGenerator(); | ||
| generator.init(extConf); | ||
| extensions = generator.createExtensions(); | ||
| } | ||
|
|
||
| PKCS10 pkcs10 = nssdb.createRequest( | ||
| subject, | ||
| keyID, | ||
| keyType, | ||
| keySize, | ||
| curve, | ||
| hash, | ||
| extensions); | ||
|
|
||
| String format = cmd.getOptionValue("format"); | ||
| byte[] bytes; | ||
|
|
||
| if (format == null || "PEM".equalsIgnoreCase(format)) { | ||
| bytes = CertUtil.toPEM(pkcs10).getBytes(); | ||
|
|
||
| } else if ("DER".equalsIgnoreCase(format)) { | ||
| bytes = pkcs10.toByteArray(); | ||
|
|
||
| } else { | ||
| throw new Exception("Unsupported format: " + format); | ||
| } | ||
|
|
||
| String filename = cmd.getOptionValue("csr"); | ||
|
|
||
| if (filename != null) { | ||
| Files.write(Paths.get(filename) , bytes); | ||
|
|
||
| } else { | ||
| System.out.write(bytes); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,228 @@ | ||
| # Authors: | ||
| # Dinesh Prasanth M K <dmoluguw@redhat.com> | ||
| # | ||
| # Copyright Red Hat, Inc. | ||
| # | ||
| # SPDX-License-Identifier: GPL-2.0-or-later | ||
| # | ||
| import logging | ||
| import time | ||
|
|
||
| from datetime import datetime | ||
|
|
||
| from pki.server.healthcheck.certs.plugin import CertsPlugin, registry | ||
| from ipahealthcheck.core.plugin import Result, duration | ||
| from ipahealthcheck.core import constants | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
||
|
|
||
| def check_cert_expiry_date(class_instance, cert): | ||
| """ | ||
| Calculate the expiry status of the given cert | ||
| :param class_instance: Reporting Class Instance | ||
| :type class_instance: object | ||
| :param cert: Certificate | ||
| :type cert: dict | ||
| :return: Result object with prefilled args | ||
| :rtype: Result | ||
| """ | ||
|
|
||
| # Get the current time in seconds | ||
| current_time = int(round(time.time())) | ||
|
|
||
| # Get the cert's expiry date in Milli seconds | ||
| cert_expiry_time = cert.get('not_after') | ||
|
|
||
| if cert_expiry_time is None: | ||
| logger.critical("Unable to retrieve cert: %s", cert['nickname']) | ||
| return Result(class_instance, constants.ERROR, | ||
| cert_id=cert['id'], | ||
| msg='Unable to get cert\'s expiry date') | ||
|
|
||
| # Convert to seconds | ||
| cert_expiry_time = cert_expiry_time / 1000 | ||
|
|
||
| # Calculate the difference in seconds | ||
| delta_sec = cert_expiry_time - current_time | ||
|
|
||
| # Calculate the number of days left/passed | ||
| current_date = datetime.fromtimestamp(current_time) | ||
| cert_expiry_date = datetime.fromtimestamp(cert_expiry_time) | ||
| delta_days = (cert_expiry_date - current_date).days | ||
|
|
||
| expiry_date_human = cert_expiry_date.strftime("%b %d %Y") | ||
|
|
||
| if delta_sec <= 0: | ||
| logger.error("Expired Cert: %s", cert['id']) | ||
| return Result(class_instance, constants.ERROR, | ||
| cert_id=cert['id'], | ||
| expiry_date=expiry_date_human, | ||
| msg='Certificate has ALREADY EXPIRED') | ||
|
|
||
| elif delta_days == 0 and delta_sec <= 86400: | ||
| # Expiring in less than a day | ||
| logger.warning("Expiring in a day: %s", cert['id']) | ||
| return Result(class_instance, constants.WARNING, | ||
| cert_id=cert['id'], | ||
| msg='Expiring within next 24 hours') | ||
|
|
||
| elif delta_days < 30: | ||
| # Expiring in a month | ||
| logger.warning("Expiring in less than 30 days: %s", cert['id']) | ||
| return Result(class_instance, constants.WARNING, | ||
| cert_id=cert['id'], | ||
| expiry_date=expiry_date_human, | ||
| msg='Your certificate expires within 30 days.') | ||
| else: | ||
| # Valid certificate | ||
| logger.info("VALID certificate: %s", cert['id']) | ||
| return Result(class_instance, constants.SUCCESS, | ||
| cert_id=cert['id'], | ||
| expiry_date=expiry_date_human) | ||
|
|
||
|
|
||
| @registry | ||
| class CASystemCertExpiryCheck(CertsPlugin): | ||
| """ | ||
| Check the expiry of CA's system certs | ||
| """ | ||
|
|
||
| @duration | ||
| def check(self): | ||
|
|
||
| if not self.instance.exists(): | ||
| logger.debug('Invalid instance: %s', self.instance.name) | ||
| yield Result(self, constants.CRITICAL, | ||
| msg='Invalid PKI instance: %s' % self.instance.name) | ||
| return | ||
|
|
||
| self.instance.load() | ||
|
|
||
| ca = self.instance.get_subsystem('ca') | ||
|
|
||
| if not ca: | ||
| logger.info("No CA configured, skipping CA System Cert Expiry check") | ||
| return | ||
|
|
||
| certs = ca.find_system_certs() | ||
|
|
||
| for cert in certs: | ||
| yield check_cert_expiry_date(class_instance=self, cert=cert) | ||
|
|
||
|
|
||
| @registry | ||
| class KRASystemCertExpiryCheck(CertsPlugin): | ||
| """ | ||
| Check the expiry of KRA's system certs | ||
| """ | ||
|
|
||
| @duration | ||
| def check(self): | ||
|
|
||
| if not self.instance.exists(): | ||
| logger.debug('Invalid instance: %s', self.instance.name) | ||
| yield Result(self, constants.CRITICAL, | ||
| msg='Invalid PKI instance: %s' % self.instance.name) | ||
| return | ||
|
|
||
| self.instance.load() | ||
|
|
||
| kra = self.instance.get_subsystem('kra') | ||
|
|
||
| if not kra: | ||
| logger.info("No KRA configured, skipping KRA System Cert Expiry check") | ||
| return | ||
|
|
||
| certs = kra.find_system_certs() | ||
|
|
||
| for cert in certs: | ||
| yield check_cert_expiry_date(class_instance=self, cert=cert) | ||
|
|
||
|
|
||
| @registry | ||
| class OCSPSystemCertExpiryCheck(CertsPlugin): | ||
| """ | ||
| Check the expiry of OCSP's system certs | ||
| """ | ||
|
|
||
| @duration | ||
| def check(self): | ||
|
|
||
| if not self.instance.exists(): | ||
| logger.debug('Invalid instance: %s', self.instance.name) | ||
| yield Result(self, constants.CRITICAL, | ||
| msg='Invalid PKI instance: %s' % self.instance.name) | ||
| return | ||
|
|
||
| self.instance.load() | ||
|
|
||
| ocsp = self.instance.get_subsystem('ocsp') | ||
|
|
||
| if not ocsp: | ||
| logger.info("No OCSP configured, skipping OCSP System Cert Expiry check") | ||
| return | ||
|
|
||
| certs = ocsp.find_system_certs() | ||
|
|
||
| for cert in certs: | ||
| yield check_cert_expiry_date(class_instance=self, cert=cert) | ||
|
|
||
|
|
||
| @registry | ||
| class TKSSystemCertExpiryCheck(CertsPlugin): | ||
| """ | ||
| Check the expiry of TKS's system certs | ||
| """ | ||
|
|
||
| @duration | ||
| def check(self): | ||
|
|
||
| if not self.instance.exists(): | ||
| logger.debug('Invalid instance: %s', self.instance.name) | ||
| yield Result(self, constants.CRITICAL, | ||
| msg='Invalid PKI instance: %s' % self.instance.name) | ||
| return | ||
|
|
||
| self.instance.load() | ||
|
|
||
| tks = self.instance.get_subsystem('tks') | ||
|
|
||
| if not tks: | ||
| logger.info("No TKS configured, skipping TKS System Cert Expiry check") | ||
| return | ||
|
|
||
| certs = tks.find_system_certs() | ||
|
|
||
| for cert in certs: | ||
| yield check_cert_expiry_date(class_instance=self, cert=cert) | ||
|
|
||
|
|
||
| @registry | ||
| class TPSSystemCertExpiryCheck(CertsPlugin): | ||
| """ | ||
| Check the expiry of TPS's system certs | ||
| """ | ||
|
|
||
| @duration | ||
| def check(self): | ||
|
|
||
| if not self.instance.exists(): | ||
| logger.debug('Invalid instance: %s', self.instance.name) | ||
| yield Result(self, constants.CRITICAL, | ||
| msg='Invalid PKI instance: %s' % self.instance.name) | ||
| return | ||
|
|
||
| self.instance.load() | ||
|
|
||
| tps = self.instance.get_subsystem('tps') | ||
|
|
||
| if not tps: | ||
| logger.info("No TPS configured, skipping TPS System Cert Expiry check") | ||
| return | ||
|
|
||
| certs = tps.find_system_certs() | ||
|
|
||
| for cert in certs: | ||
| yield check_cert_expiry_date(class_instance=self, cert=cert) |