Java tutorial
/************************************************************************* * * * EJBCA Community: The OpenSource Certificate Authority * * * * This software is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2.1 of the License, or any later version. * * * * See terms of license at gnu.org. * * * *************************************************************************/ package org.ejbca.ui.cli; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.math.BigInteger; import java.security.Key; import java.security.KeyStore; import java.security.PrivateKey; import java.security.PublicKey; import java.security.Security; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.Collections; import java.util.Date; import java.util.Enumeration; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map.Entry; import java.util.Properties; import java.util.Set; import org.apache.commons.io.IOUtils; import org.apache.log4j.Logger; import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.DERNull; import org.bouncycastle.asn1.icao.ICAOObjectIdentifiers; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier; import org.bouncycastle.asn1.x509.Extension; import org.bouncycastle.asn1.x509.Extensions; import org.bouncycastle.asn1.x509.ExtensionsGenerator; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.X509v3CertificateBuilder; import org.bouncycastle.cms.CMSEnvelopedGenerator; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.operator.BufferingContentSigner; import org.bouncycastle.operator.ContentSigner; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import org.cesecore.certificates.ca.internal.SernoGeneratorRandom; import org.cesecore.keys.token.CachingKeyStoreWrapper; import org.cesecore.keys.token.p11.Pkcs11SlotLabelType; import org.cesecore.keys.util.KeyStoreTools; import org.cesecore.util.CertTools; import org.ejbca.cvc.AccessRights; import org.ejbca.cvc.AlgorithmUtil; import org.ejbca.cvc.AuthorizationRole; import org.ejbca.cvc.CAReferenceField; import org.ejbca.cvc.CVCertificate; import org.ejbca.cvc.CardVerifiableCertificate; import org.ejbca.cvc.CertificateGenerator; import org.ejbca.cvc.HolderReferenceField; import org.ejbca.cvc.OIDField; import org.ejbca.util.CMS; import org.ejbca.util.CliTools; import org.ejbca.util.PerformanceTest.NrOfThreadsAndNrOfTests; import org.ejbca.util.keystore.KeyStoreToolsFactory; /** * Manages a key store on a HSM. This class may be extended by a class specific for a typical HSM. * * @version $Id$ * */ public class HSMKeyTool extends ClientToolBox { // private static String CREATE_CA_SWITCH = "createca"; private static final String ENCRYPT_SWITCH = "encrypt"; private static final String DECRYPT_SWITCH = "decrypt"; private static final String VERIFY_SWITCH = "verify"; private static final String GENERATE_SWITCH = "generate"; private static final String GENERATE_BATCH_SWITCH = "batchgenerate"; private static final String DELETE_SWITCH = "delete"; private static final String TEST_SWITCH = "test"; private static final String MOVE_SWITCH = "move"; private static final String CERT_REQ = "certreq"; private static final String INSTALL_CERT = "installcert"; private static final String RENAME = "rename"; private static final String INSTALL_TRUSTED_ROOT = "installtrusted"; private static final String SIGN_SWITCH = "sign"; private static final String LINKCERT_SWITCH = "linkcert"; final static private Logger log = Logger.getLogger(HSMKeyTool.class); final static private String TOKEN_ID_PARAM = "<PKCS#11 token identifier>"; static private void sunConfigFileUseDescription() { System.err.println( "If <PKCS#11 token identifier> is omitted then <the shared library name> will specify the sun config file."); } static private void printTokenIdDescription() { System.err.print("The "); System.err.print(TOKEN_ID_PARAM); System.err.println(" is one of these:"); System.err.println(" An integer referring to slot ID."); System.err.println( " A string starting with \'i\' followed by an integer. The integer is the index in the slot list."); System.err.println( " A string starting with \'TOKEN_LABEL:\' followed by a string. The string is the label of the token."); System.err.println(); } private static String commandString(String[] sa) { final StringBuilder sb = new StringBuilder(); for (int i = 0; i < sa.length; i++) { sb.append(sa[i]); if (i + 1 < sa.length) { sb.append(' '); } } return sb.toString(); } private static void tooFewArguments(String[] args) { System.err.println("Too few arguments in command: '" + commandString(args) + '\''); System.exit(3); // NOPMD, it's not a JEE app } private static void generateBatch(final String batchFile, final KeyStoreTools keyStoreContainer) throws Exception { final Properties p = new Properties(); { try (final InputStream is = new FileInputStream(batchFile)) { p.load(is); } } for (final Entry<Object, Object> entry : p.entrySet()) { final String alias = (String) entry.getKey(); final String keySpec = (String) entry.getValue(); if (alias == null || alias.trim().length() < 1) { continue; } if (keySpec == null || keySpec.trim().length() < 1) { System.err.println("No key specification for alias '" + alias + "'."); continue; } try { keyStoreContainer.generateKeyPair(keySpec, alias); System.out.println("Key with specification '" + keySpec + "' generated for alias '" + alias + "'."); } catch (Exception e) { final String m = "Failed to generate key for alias '" + alias + "' with key specification '" + keySpec + "'."; System.err.println(m); log.error(m, e); } } } final static String KEY_SPEC_DESC = "all decimal digits RSA key with specified length, otherwise name of ECC curve or DSA key using syntax DSAnnnn"; private static void printCommandString(final String args[], final boolean withCharedLib, Object... objects) { final StringBuilder sb = new StringBuilder(); sb.append(args[0]); sb.append(' '); sb.append(args[1]); sb.append(' '); if (withCharedLib) { sb.append("<shared library name>"); sb.append(' '); } for (final Object o : objects) { sb.append(o); } System.err.println(sb); } private static void printCommandString(final String args[], Object... objects) { printCommandString(args, true, objects); } private static void printCommandStringNoSharedLib(final String args[], Object... objects) { printCommandString(args, false, objects); } private static boolean doIt(final String[] orgArgs) throws Exception { // Get and remove optional switches final List<String> argsList = CliTools.getAsModifyableList(orgArgs); final KeyStore.ProtectionParameter protectionParameter; final String password = CliTools.getAndRemoveParameter("-password", argsList); if (password != null) { protectionParameter = new KeyStore.PasswordProtection(password.toCharArray()); } else { protectionParameter = null; } final String[] args = CliTools.getAsArgs(argsList); if (args[1].toLowerCase().trim().contains(GENERATE_BATCH_SWITCH)) { if (args.length < 4) { printCommandString(args, "<name of batch file> [", TOKEN_ID_PARAM, "]"); printTokenIdDescription(); sunConfigFileUseDescription(); System.err.println( "The batch file is a file which specifies alias and key specification for each key to be generated."); System.err .println("Each row is starting with a key alias then the key specification is following."); System.err.println("The specification of the key is done like this: " + KEY_SPEC_DESC); tooFewArguments(args); } final String storeId; final Pkcs11SlotLabelType slotType; if (args.length > 4) { storeId = trimStoreId(args[4]); slotType = getTokenLabelType(args[4]); } else { storeId = null; slotType = Pkcs11SlotLabelType.SUN_FILE; } final KeyStoreTools store = KeyStoreToolsFactory.getInstance(args[2], storeId, slotType, null, protectionParameter, "batch-" + new Date().getTime()); generateBatch(args[3], store); return true; } if (args[1].toLowerCase().trim().contains(GENERATE_SWITCH)) { if (args.length < 4) { printCommandString(args, Character.valueOf('<'), KEY_SPEC_DESC, "> <key entry name> [", TOKEN_ID_PARAM, "]"); printTokenIdDescription(); sunConfigFileUseDescription(); tooFewArguments(args); } final String keyEntryName = args.length > 4 ? args[4] : "myKey"; final String storeId; final Pkcs11SlotLabelType slotType; if (args.length > 5) { storeId = trimStoreId(args[5]); slotType = getTokenLabelType(args[5]); } else { storeId = null; slotType = Pkcs11SlotLabelType.SUN_FILE; } System.out.println("Using Slot Reference Type: " + slotType + '.'); final KeyStoreTools store = KeyStoreToolsFactory.getInstance(args[2], storeId, slotType, null, protectionParameter, "priv-" + keyEntryName); store.generateKeyPair(args[3], keyEntryName); System.out.println("Created certificate with entry " + keyEntryName + '.'); return true; } if (args[1].toLowerCase().trim().equals(DELETE_SWITCH)) { if (args.length < 4) { printCommandString(args, TOKEN_ID_PARAM, " [<key entry name>]"); printTokenIdDescription(); tooFewArguments(args); } final String alias = args.length > 4 ? args[4] : null; System.out.println("Deleting certificate with alias " + alias + '.'); final String storeId = trimStoreId(args[3]); final Pkcs11SlotLabelType slotType = getTokenLabelType(args[3]); KeyStoreToolsFactory.getInstance(args[2], storeId, slotType, null, protectionParameter) .deleteEntry(alias); return true; } if (args[1].toLowerCase().trim().equals(CERT_REQ)) { // First we check if we have a switch for "-explicitecc" for explicit ecc parameters used in ICAO epassports. final List<String> argsListLocal = CliTools.getAsModifyableList(args); final boolean explicitEccParameters = argsListLocal.remove("-explicitecc"); final boolean forAllKeys = argsListLocal.remove("-all"); final String modArgs[] = argsListLocal.toArray(new String[argsListLocal.size()]); if (modArgs.length < 4 || (modArgs.length < 5 && !forAllKeys)) { printCommandString(args, TOKEN_ID_PARAM, " <key entry name> [<CN>] [-explicitecc]"); printCommandString(args, TOKEN_ID_PARAM, " [-all] [-explicitecc]"); printTokenIdDescription(); tooFewArguments(modArgs); } final String storeId = trimStoreId(modArgs[3]); final Pkcs11SlotLabelType slotType = getTokenLabelType(modArgs[3]); final KeyStoreTools container = KeyStoreToolsFactory.getInstance(modArgs[2], storeId, slotType, null, protectionParameter); final List<String> entries; if (forAllKeys) { entries = new LinkedList<>(); final CachingKeyStoreWrapper ks = container.getKeyStore(); final Enumeration<String> aliases = ks.aliases(); while (aliases.hasMoreElements()) { final String alias = aliases.nextElement(); if (ks.isKeyEntry(alias)) { entries.add(alias); } } } else { entries = Collections.singletonList(modArgs[4]); } for (String entry : entries) { container.generateCertReq(entry, modArgs.length > 5 ? modArgs[5] : null, explicitEccParameters); } return true; } if (args[1].toLowerCase().trim().equals(INSTALL_CERT)) { if (args.length < 5) { printCommandString(args, TOKEN_ID_PARAM, " <certificate chain files in PEM format (one chain per file)>"); printTokenIdDescription(); tooFewArguments(args); } final String storeId = trimStoreId(args[3]); final Pkcs11SlotLabelType slotType = getTokenLabelType(args[3]); final KeyStoreTools container = KeyStoreToolsFactory.getInstance(args[2], storeId, slotType, null, protectionParameter); boolean failure = false; for (int i = 4; i < args.length; i++) { try { container.installCertificate(args[i]); } catch (Exception ex) { failure = true; log.error("File " + args[i] + " failed.", ex); } } if (failure) { throw new Exception("At least one certificate could not be installed. See the log for more info."); } return true; } if (args[1].toLowerCase().trim().equals(INSTALL_TRUSTED_ROOT)) { if (args.length < 5) { printCommandString(args, TOKEN_ID_PARAM, " <trusted root certificate in PEM format>"); printTokenIdDescription(); tooFewArguments(args); } final String storeId = trimStoreId(args[3]); final Pkcs11SlotLabelType slotType = getTokenLabelType(args[3]); KeyStoreToolsFactory.getInstance(args[2], storeId, slotType, null, protectionParameter) .installTrustedRoot(args[4]); return true; } if (args[1].toLowerCase().trim().equals(ENCRYPT_SWITCH)) { String symmAlgOid = CMSEnvelopedGenerator.AES128_CBC; if (args.length < 5) { System.err.println("There are two ways of doing the encryption:"); printCommandString(args, TOKEN_ID_PARAM, " <input file> <output file> <key alias> [optional symm algorithm oid]"); printCommandStringNoSharedLib(args, "<input file> <output file> <file with certificate with public key to use> [optional symm algorithm oid]"); printTokenIdDescription(); System.err.println( "Optional symmetric encryption algorithm OID can be for example 2.16.840.1.101.3.4.1.42 (AES256_CBC) or 1.2.392.200011.61.1.1.1.4 (CAMELLIA256_CBC). Default is to use AES256_CBC."); tooFewArguments(args); } if (args.length < 7) { Security.addProvider(new BouncyCastleProvider()); if (args.length > 5) { // We have a symmAlg as last parameter symmAlgOid = args[5]; } System.out.println("Using symmetric encryption algorithm: " + symmAlgOid); try (final InputStream certIS = new FileInputStream(args[4]); final InputStream is = new FileInputStream(args[2]); final OutputStream os = new FileOutputStream(args[3])) { final X509Certificate cert = (X509Certificate) CertificateFactory.getInstance("X.509") .generateCertificate(new BufferedInputStream(certIS)); CMS.encrypt(is, os, cert, symmAlgOid); } } else { if (args.length > 7) { // We have a symmAlg as last parameter symmAlgOid = args[7]; } System.out.println("Using symmstric encryption algorithm: " + symmAlgOid); final String storeId = trimStoreId(args[3]); final Pkcs11SlotLabelType slotType = getTokenLabelType(args[3]); try (final InputStream is = new FileInputStream(args[4]); final OutputStream os = new FileOutputStream(args[5]);) { final Certificate cert = KeyStoreToolsFactory .getInstance(args[2], storeId, slotType, null, protectionParameter).getKeyStore() .getCertificate(args[6]); CMS.encrypt(is, os, (X509Certificate) cert, symmAlgOid); } } return true; } if (args[1].toLowerCase().trim().equals(DECRYPT_SWITCH)) { if (args.length < 7) { printCommandString(args, TOKEN_ID_PARAM, " <input file> <output file> <key alias>"); printTokenIdDescription(); tooFewArguments(args); } final String storeId = trimStoreId(args[3]); final Pkcs11SlotLabelType slotType = getTokenLabelType(args[3]); try (final InputStream is = new FileInputStream(args[4]); final OutputStream os = new FileOutputStream(args[5])) { final KeyStoreTools keyStore = KeyStoreToolsFactory.getInstance(args[2], storeId, slotType, null, protectionParameter); CMS.decrypt(is, os, (PrivateKey) keyStore.getKeyStore().getKey(args[6], null), keyStore.getProviderName()); } return true; } if (args[1].toLowerCase().trim().equals(SIGN_SWITCH)) { if (args.length < 7) { printCommandString(args, TOKEN_ID_PARAM, " <input file> <output file> <key alias>"); printTokenIdDescription(); tooFewArguments(args); } final String storeId = trimStoreId(args[3]); final Pkcs11SlotLabelType slotType = getTokenLabelType(args[3]); final KeyStoreTools keyStore = KeyStoreToolsFactory.getInstance(args[2], storeId, slotType, null, protectionParameter); final String alias = args[6]; final PrivateKey key = (PrivateKey) keyStore.getKeyStore().getKey(alias, null); final X509Certificate cert = (X509Certificate) keyStore.getKeyStore().getCertificate(alias); try (final InputStream is = new FileInputStream(args[4]); final OutputStream os = new FileOutputStream(args[5]);) { CMS.sign(is, os, key, keyStore.getProviderName(), cert); } return true; } if (args[1].toLowerCase().trim().equals(LINKCERT_SWITCH)) { if (args.length < 8) { printCommandString(args, TOKEN_ID_PARAM, " <old ca-cert> <new ca-cert> <output link-cert> <key alias> [<sig alg override>]"); printTokenIdDescription(); System.err.println(); System.err.println("Creates a link certificate that links the old and new certificate files."); System.err.println("You should use this command with the old HSM key. It does not need any"); System.err.println("access to the new key."); System.err.println(); tooFewArguments(args); } final String storeId = trimStoreId(args[3]); final Pkcs11SlotLabelType slotType = getTokenLabelType(args[3]); final KeyStoreTools ksc = KeyStoreToolsFactory.getInstance(args[2], storeId, slotType, null, protectionParameter); final String alias = args[7]; final String oldCertPath = args[4]; final String newCertPath = args[5]; final String outputPath = args[6]; final String signProviderName = ksc.getProviderName(); final String sigAlgOverride = (args.length > 8 ? args[8] : "null"); // Parse certificates final byte[] oldCertBytes; try (final InputStream is = new FileInputStream(oldCertPath)) { oldCertBytes = IOUtils.toByteArray(is); } final byte[] newCertBytes; try (final InputStream is = new FileInputStream(newCertPath)) { newCertBytes = IOUtils.toByteArray(is); } final Certificate oldCert = CertTools.getCertfromByteArray(oldCertBytes, BouncyCastleProvider.PROVIDER_NAME, Certificate.class); final Certificate newCert = CertTools.getCertfromByteArray(newCertBytes, BouncyCastleProvider.PROVIDER_NAME, Certificate.class); final boolean isCVCA = (oldCert instanceof CardVerifiableCertificate); if (isCVCA != (newCert instanceof CardVerifiableCertificate)) { log.error("Error: Old and new certificates are not of the same type (X509 / CVC)"); return true; // = valid command-line syntax } System.out.println("Type of certificates: " + (isCVCA ? "CVC" : "X509")); // Detect name change final String oldDN = CertTools.getSubjectDN(oldCert); final String newDN = CertTools.getSubjectDN(newCert); System.out.println("Old DN: " + oldDN); System.out.println("New DN: " + newDN); final boolean nameChange; if (!oldDN.equals(newDN)) { if (isCVCA) { System.out.println("Name change detected."); } else { System.out.println("Name change detected. Will add Name Change extension."); } nameChange = true; } else { System.out.println("No name change detected."); nameChange = false; } final ByteArrayOutputStream baos = new ByteArrayOutputStream(); // Get new and old key final PublicKey newPubKey = newCert.getPublicKey(); if (newPubKey == null) { System.err.println("Error: Failed to extract public key from new certificate"); return true; } final Key oldKey = ksc.getKeyStore().getKey(alias, null); if (oldKey == null) { System.err.println("Error: Could not find the key named " + alias); return true; } final PrivateKey oldPrivKey = (PrivateKey) oldKey; if (isCVCA) { final CVCertificate oldCertCVC = ((CardVerifiableCertificate) oldCert).getCVCertificate(); final CVCertificate newCertCVC = ((CardVerifiableCertificate) newCert).getCVCertificate(); final String linkSigAlg; if (sigAlgOverride.equalsIgnoreCase("null")) { final OIDField oldKeyTypeOid = oldCertCVC.getCertificateBody().getPublicKey() .getObjectIdentifier(); linkSigAlg = AlgorithmUtil.getAlgorithmName(oldKeyTypeOid); } else { System.err.println("Error: Overriding the signature algorithm is not supported for CVC"); return true; } System.out.println("Using signature algorithm " + linkSigAlg); final HolderReferenceField caHolder = oldCertCVC.getCertificateBody().getHolderReference(); final CAReferenceField caRef = new CAReferenceField(caHolder.getCountry(), caHolder.getMnemonic(), caHolder.getSequence()); final HolderReferenceField certHolder = newCertCVC.getCertificateBody().getHolderReference(); final AuthorizationRole authRole = newCertCVC.getCertificateBody().getAuthorizationTemplate() .getAuthorizationField().getAuthRole(); final AccessRights rights = newCertCVC.getCertificateBody().getAuthorizationTemplate() .getAuthorizationField().getAccessRights(); final Date validFrom = new Date(new Date().getTime() - 60L * 15L * 1000L); // back date by 15 minutes to allow for clock skew final Date validTo = oldCertCVC.getCertificateBody().getValidTo(); final CVCertificate linkCert = CertificateGenerator.createCertificate(newPubKey, oldPrivKey, linkSigAlg, caRef, certHolder, authRole, rights, validFrom, validTo, signProviderName); try (final DataOutputStream dos = new DataOutputStream(baos)) { linkCert.encode(dos); } } else { // X509 CA final X509Certificate oldCertX509 = (X509Certificate) oldCert; final X509Certificate newCertX509 = (X509Certificate) newCert; final String linkSigAlg; if (sigAlgOverride.equalsIgnoreCase("null")) { // Actually, we should use signature algorithm of new cert if the old key allows that. // Instead of doing that we allow the user to manually override the signature algorithm if needed. linkSigAlg = oldCertX509.getSigAlgName(); } else { System.err.println("Warning: Signature algorithm manually overridden!"); linkSigAlg = sigAlgOverride; } System.out.println("Using signature algorithm " + linkSigAlg); final BigInteger serno = SernoGeneratorRandom.instance().getSerno(); final SubjectPublicKeyInfo pkinfo = SubjectPublicKeyInfo.getInstance(newPubKey.getEncoded()); final Date validFrom = new Date(new Date().getTime() - 60L * 15L * 1000L); // back date by 15 minutes to allow for clock skew final Date validTo = oldCertX509.getNotAfter(); final X500Name oldDNName = X500Name.getInstance(oldCertX509.getSubjectX500Principal().getEncoded()); final X500Name newDNName = X500Name.getInstance(newCertX509.getSubjectX500Principal().getEncoded()); final X509v3CertificateBuilder certbuilder = new X509v3CertificateBuilder(oldDNName, serno, validFrom, validTo, newDNName, pkinfo); // Copy all extensions except AKID final ExtensionsGenerator extgen = new ExtensionsGenerator(); final Set<String> oids = new LinkedHashSet<>(); final Set<String> criticalOids = newCertX509.getCriticalExtensionOIDs(); oids.addAll(criticalOids); oids.addAll(newCertX509.getNonCriticalExtensionOIDs()); for (final String extOidStr : oids) { final ASN1ObjectIdentifier extoid = new ASN1ObjectIdentifier(extOidStr); if (!extoid.equals(Extension.authorityKeyIdentifier)) { final byte[] extbytes = newCertX509.getExtensionValue(extOidStr); final ASN1OctetString str = (ASN1OctetString) ASN1Primitive.fromByteArray(extbytes); extgen.addExtension(extoid, criticalOids.contains(extOidStr), ASN1Primitive.fromByteArray(str.getOctets())); } } if (nameChange) { // id-icao-mrtd-security-extensions-nameChange = 2.23.136.1.1.6.1 extgen.addExtension(ICAOObjectIdentifiers.id_icao_extensions_namechangekeyrollover, false, DERNull.INSTANCE); } // Some checks if (newCertX509.getExtensionValue(Extension.subjectKeyIdentifier.getId()) == null) { System.err.println( "Warning: Certificate of new CSCA is missing the Subject Key Identifier extension, which is mandatory."); } if (newCertX509.getExtensionValue(Extension.authorityKeyIdentifier.getId()) == null) { System.err.println( "Warning: Certificate of new CSCA is missing the Authority Key Identifier extension, which is mandatory."); } // If the new cert has an AKID, then add that extension but with the key id value of the old cert final byte[] oldSKIDBytes = oldCertX509.getExtensionValue(Extension.subjectKeyIdentifier.getId()); if (oldSKIDBytes != null) { final ASN1OctetString str = (ASN1OctetString) ASN1Primitive.fromByteArray(oldSKIDBytes); final ASN1OctetString innerStr = (ASN1OctetString) ASN1Primitive.fromByteArray(str.getOctets()); final AuthorityKeyIdentifier akidExt = new AuthorityKeyIdentifier(innerStr.getOctets()); extgen.addExtension(Extension.authorityKeyIdentifier, false, akidExt); } else { System.err.println( "Warning: The old certificate doesn't have any SubjectKeyIdentifier. The link certificate will not have any AuthorityKeyIdentifier."); } // Add extensions to the certificate final Extensions exts = extgen.generate(); for (final ASN1ObjectIdentifier extoid : exts.getExtensionOIDs()) { final Extension ext = exts.getExtension(extoid); certbuilder.addExtension(extoid, ext.isCritical(), ext.getParsedValue()); } // Sign the certificate final ContentSigner signer = new BufferingContentSigner( new JcaContentSignerBuilder(linkSigAlg).setProvider(signProviderName).build(oldPrivKey), 20480); final X509CertificateHolder certHolder = certbuilder.build(signer); baos.write(certHolder.getEncoded()); // Save to output file try (final FileOutputStream fos = new FileOutputStream(outputPath)) { baos.writeTo(fos); } } return true; } if (args[1].toLowerCase().trim().equals(VERIFY_SWITCH)) { final CMS.VerifyResult verifyResult; if (args.length < 5) { System.err.println("There are two ways of doing the encryption:"); printCommandString(args, TOKEN_ID_PARAM, " <input file> <output file> <key alias>"); printTokenIdDescription(); printCommandStringNoSharedLib(args, "<input file> <output file> <file with certificate with public key to use>"); tooFewArguments(args); } if (args.length < 7) { Security.addProvider(new BouncyCastleProvider()); try (final InputStream certIS = new FileInputStream(args[4]); final InputStream is = new FileInputStream(args[2]); final OutputStream os = new FileOutputStream(args[3]);) { final X509Certificate cert = (X509Certificate) CertificateFactory.getInstance("X.509") .generateCertificate(new BufferedInputStream(certIS)); verifyResult = CMS.verify(is, os, cert); } } else { final String storeId = trimStoreId(args[3]); final Pkcs11SlotLabelType slotType = getTokenLabelType(args[3]); final KeyStoreTools keyStore = KeyStoreToolsFactory.getInstance(args[2], storeId, slotType, null, protectionParameter); final X509Certificate cert = (X509Certificate) keyStore.getKeyStore().getCertificate(args[6]); try (final InputStream is = new FileInputStream(args[4]); final OutputStream os = new FileOutputStream(args[5])) { verifyResult = CMS.verify(is, os, cert); } } if (verifyResult == null) { System.err.println("Not possible to parse signed file."); System.exit(4); // Not verifying // NOPMD, it's not a JEE app return false;//will never be executes. just to avoid warning. } System.out.println( "The signature of the input " + (verifyResult.isVerifying ? "has been" : "could not be") + " verified. The file was signed on '" + verifyResult.signDate + "'. The public part of the signing key is in a certificate with serial number " + verifyResult.signerId.getSerialNumber() + " issued by '" + verifyResult.signerId.getIssuer() + "'."); if (!verifyResult.isVerifying) { System.exit(4); // Not verifying // NOPMD, it's not a JEE app } return true; } if (args[1].toLowerCase().trim().equals(TEST_SWITCH)) { if (args.length < 4) { printCommandString(args, TOKEN_ID_PARAM, " [<'m:n' m # of threads, n # of tests>] [<alias for stress test>] [<type of stress test>]"); printTokenIdDescription(); System.err.println( " If a file named \"./testData\" exists then the data that is signed, is read from this file."); tooFewArguments(args); } final String storeId = trimStoreId(args[3]); final Pkcs11SlotLabelType slotType = getTokenLabelType(args[3]); final NrOfThreadsAndNrOfTests notanot = new NrOfThreadsAndNrOfTests(args.length > 4 ? args[4] : null); KeyStoreContainerTest.test(args[2], storeId, slotType, notanot.threads, notanot.tests, args.length > 5 ? args[5].trim() : null, args.length > 6 ? args[6].trim() : null, protectionParameter); return true; } if (args[1].toLowerCase().trim().equals(RENAME)) { if (args.length < 6) { printCommandString(args, TOKEN_ID_PARAM, " <old key alias> <new key alias>"); printTokenIdDescription(); tooFewArguments(args); } final String storeId = trimStoreId(args[3]); final Pkcs11SlotLabelType slotType = getTokenLabelType(args[3]); final KeyStoreTools keyStore = KeyStoreToolsFactory.getInstance(args[2], storeId, slotType, null, protectionParameter); keyStore.renameEntry(args[4], args[5]); return true; } if (args[1].toLowerCase().trim().equals(MOVE_SWITCH)) { if (args.length < 5) { printCommandString(args, "<from PKCS#11 token identifier> <to PKCS#11 token identifier>"); printTokenIdDescription(); tooFewArguments(args); } final KeyStoreTools fromKS = KeyStoreToolsFactory.getInstance(args[2], trimStoreId(args[3]), getTokenLabelType(args[3]), null, protectionParameter); final KeyStoreTools toKS = KeyStoreToolsFactory.getInstance(args[2], trimStoreId(args[4]), getTokenLabelType(args[4]), null, protectionParameter); System.out.println("Moving entry with alias '" + args[3] + "' to alias '" + args[4] + '.'); final Enumeration<String> e = fromKS.getKeyStore().aliases(); while (e.hasMoreElements()) { final String alias = e.nextElement(); if (fromKS.getKeyStore().isKeyEntry(alias)) { final Key key = fromKS.getKeyStore().getKey(alias, null); final Certificate chain[] = fromKS.getKeyStore().getCertificateChain(alias); toKS.setKeyEntry(alias, key, chain); } fromKS.getKeyStore().deleteEntry(alias); } fromKS.getKeyStore().store(null, null); toKS.getKeyStore().store(null, null); return true; } return false; } /* (non-Javadoc) * @see org.ejbca.ui.cli.ClientToolBox#execute(java.lang.String[]) */ @Override protected void execute(String[] args) { try { if (args.length > 1 && doIt(args)) { return; // command was found. } final PrintWriter pw = new PrintWriter(System.err); pw.println("Use one of following commands: "); // pw.println(" "+args[0]+" "+CREATE_CA_SWITCH); pw.println(" " + args[0] + " " + GENERATE_SWITCH); pw.println(" " + args[0] + " " + GENERATE_BATCH_SWITCH); pw.println(" " + args[0] + " " + CERT_REQ); pw.println(" " + args[0] + " " + INSTALL_CERT); pw.println(" " + args[0] + " " + DELETE_SWITCH); pw.println(" " + args[0] + " " + TEST_SWITCH); pw.println(" " + args[0] + " " + RENAME); pw.println(" " + args[0] + " " + ENCRYPT_SWITCH); pw.println(" " + args[0] + " " + DECRYPT_SWITCH); pw.println(" " + args[0] + " " + SIGN_SWITCH); pw.println(" " + args[0] + " " + VERIFY_SWITCH); pw.println(" " + args[0] + " " + MOVE_SWITCH); pw.println(" " + args[0] + " " + LINKCERT_SWITCH); pw.println( " The optional -password <password> switch can be specified as a last argument for scripting any of these commands."); pw.flush(); if (args.length > 1) { // Don't print this if it is only a general usage message System.err.println("Command '" + commandString(args) + "' not found."); } System.exit(1); // Command not found. // NOPMD, it's not a JEE app } catch (SecurityException e) { throw e; // in test of the tool System.exit() is throwing exception that should be thrown to the the app. } catch (Throwable e) { System.err.println("Command could not be executed. See log for stack trace."); log.error("Command '" + commandString(args) + "' could not be executed.", e); System.exit(2); // Command did not execute OK! // NOPMD, it's not a JEE app } } @Override protected String getName() { return "PKCS11HSMKeyTool"; } private static final String trimStoreId(String storeId) { if (storeId.contains(":")) { return storeId.split(":", 2)[1]; } return storeId; } private static final Pkcs11SlotLabelType getTokenLabelType(String storeId) { if (storeId.contains(":")) { String prefix = storeId.split(":", 2)[0].trim(); if (prefix.equals("TOKEN_LABEL") || prefix.equals(Pkcs11SlotLabelType.SLOT_LABEL.getKey())) { return Pkcs11SlotLabelType.SLOT_LABEL; } if (prefix.equals("SLOT_ID") || prefix.equals(Pkcs11SlotLabelType.SLOT_NUMBER.getKey())) { return Pkcs11SlotLabelType.SLOT_NUMBER; } if (prefix.equals("SLOT_LIST_IX") || prefix.equals(Pkcs11SlotLabelType.SLOT_INDEX.getKey())) { return Pkcs11SlotLabelType.SLOT_INDEX; } throw new IllegalArgumentException("Type " + prefix + " was not valid"); } if (Pkcs11SlotLabelType.SLOT_NUMBER.validate(storeId)) { return Pkcs11SlotLabelType.SLOT_NUMBER; } if (Pkcs11SlotLabelType.SLOT_INDEX.validate(storeId)) { return Pkcs11SlotLabelType.SLOT_INDEX; } return Pkcs11SlotLabelType.SLOT_LABEL; } }