diff options
author | Endi S. Dewata <edewata@redhat.com> | 2015-01-27 00:35:59 -0500 |
---|---|---|
committer | Endi S. Dewata <edewata@redhat.com> | 2015-01-28 14:44:12 -0500 |
commit | 7de81fedeba1a3904c127dc612a937903e622d81 (patch) | |
tree | 47a52fdf47d30fee5d23576ace5985d22e5f05d4 | |
parent | 22ff1fbd2de37395e219a7e7362722517a3f4dc3 (diff) | |
download | pki-7de81fedeba1a3904c127dc612a937903e622d81.tar.gz pki-7de81fedeba1a3904c127dc612a937903e622d81.tar.xz pki-7de81fedeba1a3904c127dc612a937903e622d81.zip |
Refactored CRMFPopClient.
The CRMFPopClient has been refactored such that it is easier
to understand and reuse. The code has been fixed such that it
can read a normal PEM transport certificate. It also has been
fixed to parse the request submission result properly.
The client-cert-request CLI command was modified to support CRMF
requests.
The MainCLI and ClientConfig were modified to accept a security
token name.
The pki_java_command_wrapper.in was modified to include the Apache
Commons IO library.
https://fedorahosted.org/pki/ticket/1074
5 files changed, 753 insertions, 508 deletions
diff --git a/base/common/src/com/netscape/certsrv/client/ClientConfig.java b/base/common/src/com/netscape/certsrv/client/ClientConfig.java index f59f7320e..a81669536 100644 --- a/base/common/src/com/netscape/certsrv/client/ClientConfig.java +++ b/base/common/src/com/netscape/certsrv/client/ClientConfig.java @@ -51,10 +51,13 @@ public class ClientConfig { URI serverURI; String certDatabase; + String tokenName; String certNickname; String certPassword; + String username; String password; + String messageFormat; public ClientConfig() { @@ -62,11 +65,15 @@ public class ClientConfig { public ClientConfig(ClientConfig config) { serverURI = config.serverURI; + certDatabase = config.certDatabase; + tokenName = config.tokenName; certNickname = config.certNickname; certPassword = config.certPassword; + username = config.username; password = config.password; + messageFormat = config.messageFormat; } @@ -101,6 +108,15 @@ public class ClientConfig { this.certDatabase = certDatabase; } + @XmlElement(name="Token") + public String getTokenName() { + return tokenName; + } + + public void setTokenName(String tokenName) { + this.tokenName = tokenName; + } + @XmlElement(name="CertNickname") public String getCertNickname() { return certNickname; @@ -156,6 +172,7 @@ public class ClientConfig { result = prime * result + ((messageFormat == null) ? 0 : messageFormat.hashCode()); result = prime * result + ((password == null) ? 0 : password.hashCode()); result = prime * result + ((serverURI == null) ? 0 : serverURI.hashCode()); + result = prime * result + ((tokenName == null) ? 0 : tokenName.hashCode()); result = prime * result + ((username == null) ? 0 : username.hashCode()); return result; } @@ -199,6 +216,11 @@ public class ClientConfig { return false; } else if (!serverURI.equals(other.serverURI)) return false; + if (tokenName == null) { + if (other.tokenName != null) + return false; + } else if (!tokenName.equals(other.tokenName)) + return false; if (username == null) { if (other.username != null) return false; diff --git a/base/java-tools/src/com/netscape/cmstools/CRMFPopClient.java b/base/java-tools/src/com/netscape/cmstools/CRMFPopClient.java index 7edac0af6..dd20cba92 100644 --- a/base/java-tools/src/com/netscape/cmstools/CRMFPopClient.java +++ b/base/java-tools/src/com/netscape/cmstools/CRMFPopClient.java @@ -19,21 +19,22 @@ package com.netscape.cmstools; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; -import java.io.FileOutputStream; -import java.io.FileReader; -import java.io.IOException; +import java.io.File; +import java.io.FileWriter; import java.io.InputStream; import java.io.InputStreamReader; -import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.StringWriter; import java.net.URL; import java.net.URLConnection; import java.net.URLEncoder; import java.security.KeyPair; import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; import netscape.security.x509.X500Name; +import org.apache.commons.io.FileUtils; import org.mozilla.jss.CryptoManager; import org.mozilla.jss.asn1.ASN1Util; import org.mozilla.jss.asn1.BIT_STRING; @@ -46,7 +47,6 @@ import org.mozilla.jss.asn1.SEQUENCE; import org.mozilla.jss.asn1.TeletexString; import org.mozilla.jss.asn1.UTF8String; import org.mozilla.jss.asn1.UniversalString; -import org.mozilla.jss.crypto.AlreadyInitializedException; import org.mozilla.jss.crypto.CryptoToken; import org.mozilla.jss.crypto.IVParameterSpec; import org.mozilla.jss.crypto.KeyGenAlgorithm; @@ -74,6 +74,7 @@ import org.mozilla.jss.pkix.primitive.SubjectPublicKeyInfo; import org.mozilla.jss.util.Password; import com.netscape.cmsutil.crypto.CryptoUtil; +import com.netscape.cmsutil.util.Cert; import com.netscape.cmsutil.util.HMACDigest; import com.netscape.cmsutil.util.Utils; @@ -82,10 +83,8 @@ import com.netscape.cmsutil.util.Utils; * Format (CRMF) request with proof of possesion (POP). * * <pre> - * IMPORTANT: The file "transport.txt" needs to be created to contain the - * transport certificate in its base64 encoded format. This - * file should consist of one line containing a single certificate - * in base64 encoded format with the header and footer removed. + * IMPORTANT: The transport certificate file needs to be created to contain the + * transport certificate in its base64 encoded format. * </pre> * <p> * @@ -93,6 +92,8 @@ import com.netscape.cmsutil.util.Utils; */ public class CRMFPopClient { + public boolean verbose; + private static void usage() { System.out.println("Usage: CRMFPopClient -d <location of certdb> -p <token password> -h <tokenname> -o <output file which saves the base64 CRMF request> -n <subjectDN> -a <algorithm: 'rsa' or 'ec'> -l <rsa key length> -c <ec curve name> -m <hostname:port> -f <profile name; rsa default caEncUserCert; ec default caEncECUserCert> -u <user name> -r <requestor name> -q <POP_NONE, POP_SUCCESS, or POP_FAIL; default POP_SUCCESS> \n"); @@ -103,652 +104,681 @@ public class CRMFPopClient { System.out.println(" -e <1 for extractable; 0 for non-extractable; -1 token dependent; default is -1>\n"); System.out.println(" Also optional for ECC key generation:\n"); System.out.println(" -x <true for SSL cert that does ECDH ECDSA; false otherwise; default false>\n"); + System.out.println(" --transport-cert <transport cert file; default transport.txt>\n"); System.out.println(" note: '-x true' can only be used with POP_NONE"); System.out.println(" available ECC curve names (if provided by the crypto module): nistp256 (secp256r1),nistp384 (secp384r1),nistp521 (secp521r1),nistk163 (sect163k1),sect163r1,nistb163 (sect163r2),sect193r1,sect193r2,nistk233 (sect233k1),nistb233 (sect233r1),sect239k1,nistk283 (sect283k1),nistb283 (sect283r1),nistk409 (sect409k1),nistb409 (sect409r1),nistk571 (sect571k1),nistb571 (sect571r1),secp160k1,secp160r1,secp160r2,secp192k1,nistp192 (secp192r1, prime192v1),secp224k1,nistp224 (secp224r1),secp256k1,prime192v2,prime192v3,prime239v1,prime239v2,prime239v3,c2pnb163v1,c2pnb163v2,c2pnb163v3,c2pnb176v1,c2tnb191v1,c2tnb191v2,c2tnb191v3,c2pnb208w1,c2tnb239v1,c2tnb239v2,c2tnb239v3,c2pnb272w1,c2pnb304w1,c2tnb359w1,c2pnb368w1,c2tnb431r1,secp112r1,secp112r2,secp128r1,secp128r2,sect113r1,sect113r2,sect131r1,sect131r2\n"); System.out.println("\n"); - System.out.println("IMPORTANT: The file \"transport.txt\" needs to be created to contain the"); - System.out.println(" transport certificate in its base64 encoded format. This"); - System.out.println(" file should consist of one line containing a single certificate"); - System.out.println(" in base64 encoded format with the header and footer removed.\n"); + System.out.println("IMPORTANT: The transport certificate file needs to be created to contain the"); + System.out.println(" transport certificate in its base64 encoded format."); } - public static void main(String args[]) { - -// int argsLen = getRealArgsLength(args); + public static void main(String args[]) throws Exception { - System.out.println("\n\nCRMF Proof Of Possession Utility...."); - System.out.println(""); - - if (args.length < 4) - { - usage(); - System.exit(1); - } + CRMFPopClient client = new CRMFPopClient(); - String DB_DIR = "./"; - String TOKEN_PWD = null; - String TOKEN_NAME = null; + String databaseDir = "."; + String tokenPassword = null; + String tokenName = null; // "rsa" or "ec" - String alg = "rsa"; + String algorithm = "rsa"; /* default RSA key size */ - int RSA_keylen = 2048; + int keySize = 2048; + /* default ECC key curve name */ - String ECC_curve = "nistp256"; - boolean enable_encoding = false; /* enable encoding attribute values if true */ - boolean ec_temporary = true; /* session if true; token if false */ - int ec_sensitive = -1; /* -1, 0, or 1 */ - int ec_extractable = -1; /* -1, 0, or 1 */ - boolean ec_ssl_ecdh = false; + String curve = "nistp256"; + + boolean encodingEnabled = false; /* enable encoding attribute values if true */ + boolean temporary = true; /* session if true; token if false */ + int sensitive = -1; /* -1, 0, or 1 */ + int extractable = -1; /* -1, 0, or 1 */ + boolean sslECDH = false; - String USER_NAME = null; - String REQUESTOR_NAME = null; - String PROFILE_NAME = null; + String username = null; + String requestor = null; + String profileName = null; // format: "host:port" - String HOST_PORT = null; - String SUBJ_DN = null; - int doServerHit = 0; + String hostPort = null; + String subjectDN = null; + boolean submitRequest = false; // POP_NONE, POP_SUCCESS, or POP_FAIL - String POP_OPTION = "POP_SUCCESS"; - int dont_do_pop = 0; + String popOption = "POP_SUCCESS"; + boolean withPop = true; - String REQ_OUT_FILE = null; + String output = null; + String transportCertFilename = "transport.txt"; for (int i=0; i<args.length; i+=2) { String name = args[i]; - if (name.equals("-p")) { - TOKEN_PWD = args[i+1]; + if (name.equals("-v")) { + client.verbose = Boolean.parseBoolean(args[i+1]); + + } else if (name.equals("-p")) { + tokenPassword = args[i+1]; + } else if (name.equals("-d")) { - DB_DIR = args[i+1]; + databaseDir = args[i+1]; + } else if (name.equals("-h")) { - TOKEN_NAME = args[i+1]; + tokenName = args[i+1]; + } else if (name.equals("-a")) { - alg = args[i+1]; - if (!alg.equals("rsa") && !alg.equals("ec")) { - System.out.println("CRMFPopClient: ERROR: invalid algorithm: " + alg); + algorithm = args[i+1]; + if (!algorithm.equals("rsa") && !algorithm.equals("ec")) { + System.out.println("ERROR: invalid algorithm: " + algorithm); System.exit(1); } + } else if (name.equals("-x")) { String temp = args[i+1]; if (temp.equals("true")) - ec_ssl_ecdh = true; + sslECDH = true; else - ec_ssl_ecdh = false; + sslECDH = false; + } else if (name.equals("-t")) { String temp = args[i+1]; if (temp.equals("true")) - ec_temporary = true; + temporary = true; else - ec_temporary = false; + temporary = false; } else if (name.equals("-k")) { String temp = args[i+1]; if (temp.equals("true")) - enable_encoding = true; + encodingEnabled = true; else - enable_encoding = false; + encodingEnabled = false; + } else if (name.equals("-s")) { String ec_sensitive_s = args[i+1]; - ec_sensitive = Integer.parseInt(ec_sensitive_s); - if ((ec_sensitive != 0) && - (ec_sensitive != 1) && - (ec_sensitive != -1)) { - System.out.println("PKCS10Client: Illegal input parameters for -s."); + sensitive = Integer.parseInt(ec_sensitive_s); + if ((sensitive != 0) && + (sensitive != 1) && + (sensitive != -1)) { + System.out.println("ERROR: Illegal input parameters for -s."); usage(); System.exit(1); } + } else if (name.equals("-e")) { String ec_extractable_s = args[i+1]; - ec_extractable = Integer.parseInt(ec_extractable_s); - if ((ec_extractable != 0) && - (ec_extractable != 1) && - (ec_extractable != -1)) { - System.out.println("PKCS10Client: Illegal input parameters for -e."); + extractable = Integer.parseInt(ec_extractable_s); + if ((extractable != 0) && + (extractable != 1) && + (extractable != -1)) { + System.out.println("ERROR: Illegal input parameters for -e."); usage(); System.exit(1); } + } else if (name.equals("-l")) { - RSA_keylen = Integer.parseInt(args[i+1]); + keySize = Integer.parseInt(args[i+1]); + } else if (name.equals("-c")) { - ECC_curve = args[i+1]; + curve = args[i+1]; + } else if (name.equals("-m")) { - HOST_PORT = args[i+1]; - doServerHit = 1; + hostPort = args[i+1]; + submitRequest = true; + } else if (name.equals("-f")) { - PROFILE_NAME = args[i+1]; + profileName = args[i+1]; + } else if (name.equals("-u")) { - USER_NAME = args[i+1]; + username = args[i+1]; + } else if (name.equals("-r")) { - REQUESTOR_NAME = args[i+1]; + requestor = args[i+1]; + } else if (name.equals("-n")) { - SUBJ_DN = args[i+1]; + subjectDN = args[i+1]; + } else if (name.equals("-q")) { - POP_OPTION = args[i+1]; - if (!POP_OPTION.equals("POP_SUCCESS") && - !POP_OPTION.equals("POP_FAIL") && - !POP_OPTION.equals("POP_NONE")) { - System.out.println("CRMFPopClient: ERROR: invalid POP option: "+ POP_OPTION); + popOption = args[i+1]; + if (!popOption.equals("POP_SUCCESS") && + !popOption.equals("POP_FAIL") && + !popOption.equals("POP_NONE")) { + System.out.println("ERROR: invalid POP option: "+ popOption); System.exit(1); } - if (POP_OPTION.equals("POP_NONE")) - dont_do_pop = 1; + if (popOption.equals("POP_NONE")) + withPop = false; + } else if (name.equals("-o")) { - REQ_OUT_FILE = args[i+1]; + output = args[i+1]; + + } else if (name.equals("--transport-cert")) { + transportCertFilename = args[i+1]; + } else { System.out.println("Unrecognized argument(" + i + "): " + name); usage(); System.exit(1); } - } //for + } - URL url = null; - URLConnection conn = null; - InputStream is = null; - BufferedReader reader = null; - KeyPair pair = null; + if (tokenPassword == null) { + System.out.println("missing password"); + usage(); + System.exit(1); + } - boolean foundTransport = false; - String transportCert = null; - BufferedReader br = null; - try { - br = new BufferedReader(new FileReader("./transport.txt")); - transportCert = br.readLine(); - foundTransport = true; - } catch (Exception e) { - System.out.println("CRMFPopClient: ERROR: cannot find ./transport.txt, so no key archival"); + if (profileName == null) { + if (algorithm.equals("rsa")) { + profileName = "caEncUserCert"; - System.exit(1); - } finally { - if (br != null) { - try { - br.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } + } else if (algorithm.equals("ec")) { + profileName = "caEncECUserCert"; - try { - CryptoManager.initialize( DB_DIR ); - } catch (AlreadyInitializedException ae) { - // it is ok if it is already initialized - System.out.println("CRMFPopClient: already initialized, continue"); - } catch (Exception e) { - System.out.println("CRMFPopClient: INITIALIZATION ERROR: " + e.toString()); - System.exit(1); + } else { + throw new Exception("Unknown algorithm: " + algorithm); + } } try { + if (client.verbose) System.out.println("Initializing security database"); + CryptoManager.initialize(databaseDir); + CryptoManager manager = CryptoManager.getInstance(); - String token_pwd = TOKEN_PWD; - if (token_pwd == null) { - System.out.println("missing password"); - usage(); - System.exit(1); - } - CryptoToken token = null; - if (TOKEN_NAME == null) { + + CryptoToken token; + if (tokenName == null) { token = manager.getInternalKeyStorageToken(); - TOKEN_NAME = token.getName(); + tokenName = token.getName(); } else { - token = manager.getTokenByName(TOKEN_NAME); + token = manager.getTokenByName(tokenName); } - System.out.println("CRMFPopClient: getting token: "+TOKEN_NAME); manager.setThreadToken(token); - Password password = new Password(token_pwd.toCharArray()); + + Password password = new Password(tokenPassword.toCharArray()); try { token.login(password); } catch (Exception e) { - System.out.println("CRMFPopClient: login Exception: " + e.toString()); - System.exit(1); + throw new Exception("Unable to login: " + e, e); } - System.out.println("."); //"done with cryptomanager"); - - String profileName = PROFILE_NAME; - if (profileName == null) { - if (alg.equals("rsa")) - profileName = "caEncUserCert"; - else if (alg.equals("ec")) - profileName = "caEncECUserCert"; - else { - System.out.println("CRMFPopClient: unsupported algorithm: " + alg); - usage(); - System.exit(1); - } - } + if (client.verbose) System.out.println("Loading transport certificate"); + String encoded = FileUtils.readFileToString(new File(transportCertFilename)); + encoded = Cert.normalizeCertStrAndReq(encoded); + encoded = Cert.stripBrackets(encoded); + byte[] transportCertData = Utils.base64decode(encoded); - if (alg.equals("rsa")) { - KeyPairGenerator kg = token.getKeyPairGenerator( - KeyPairAlgorithm.RSA); - kg.initialize(RSA_keylen); - - pair = kg.genKeyPair(); - } else if (alg.equals("ec")) { - /* - * used with SSL server cert that does ECDH ECDSA - * ** can only be used with POP_NONE ** - */ - org.mozilla.jss.crypto.KeyPairGeneratorSpi.Usage usages_mask_ECDH[] = { - org.mozilla.jss.crypto.KeyPairGeneratorSpi.Usage.SIGN, - org.mozilla.jss.crypto.KeyPairGeneratorSpi.Usage.SIGN_RECOVER - }; - - /* used for other certs including SSL server cert that does ECDHE ECDSA */ - org.mozilla.jss.crypto.KeyPairGeneratorSpi.Usage usages_mask[] = { - org.mozilla.jss.crypto.KeyPairGeneratorSpi.Usage.DERIVE - }; - - pair = CryptoUtil.generateECCKeyPair(TOKEN_NAME, ECC_curve, - null, - (ec_ssl_ecdh==true) ? usages_mask_ECDH: usages_mask, - ec_temporary /*temporary*/, - ec_sensitive /*sensitive*/, ec_extractable /*extractable*/); + X509Certificate transportCert = manager.importCACertPackage(transportCertData); + + if (client.verbose) System.out.println("Parsing subject DN"); + Name subject = client.createName(subjectDN, encodingEnabled); + + if (subject == null) { + subject = new Name(); + subject.addCommonName("Me"); + subject.addCountryName("US"); + subject.addElement(new AVA(new OBJECT_IDENTIFIER("0.9.2342.19200300.100.1.1"), new PrintableString("MyUid"))); } - System.out.println("CRMFPopClient: key pair generated."); //key pair generated"); + if (client.verbose) System.out.println("Generating key pair"); + KeyPair keyPair; + if (algorithm.equals("rsa")) { + keyPair = client.generateRSAKeyPair(token, keySize); - // wrap private key - byte transport[] = Utils.base64decode(transportCert); + } else if (algorithm.equals("ec")) { + keyPair = client.generateECCKeyPair(token, curve, sslECDH, temporary, sensitive, extractable); - X509Certificate tcert = manager.importCACertPackage(transport); + } else { + throw new Exception("Unknown algorithm: " + algorithm); + } - byte iv[] = { 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 }; + if (client.verbose) System.out.println("Creating certificate request"); + CertRequest certRequest = client.createCertRequest(token, transportCert, algorithm, keyPair, subject); - KeyGenerator kg1 = token.getKeyGenerator(KeyGenAlgorithm.DES3); - SymmetricKey sk = kg1.generate(); + ProofOfPossession pop = null; - System.out.println(".before KeyWrapper"); + if (withPop) { - // wrap private key using session - KeyWrapper wrapper1 = - token.getKeyWrapper(KeyWrapAlgorithm.DES3_CBC_PAD); + if (client.verbose) System.out.println("Creating signer"); + Signature signer = client.createSigner(token, algorithm, keyPair); - System.out.println(".key wrapper created"); + if (popOption.equals("POP_SUCCESS")) { - wrapper1.initWrap(sk, new IVParameterSpec(iv)); + ByteArrayOutputStream bo = new ByteArrayOutputStream(); + certRequest.encode(bo); + signer.update(bo.toByteArray()); - System.out.println(".key wrapper inited"); - byte key_data[] = wrapper1.wrap((org.mozilla.jss.crypto.PrivateKey) pair.getPrivate()); + } else if (popOption.equals("POP_FAIL")) { - System.out.println(".key wrapper wrapped"); + byte[] data = { 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 }; - // wrap session key using DRM transport cert - // currently, a transport cert has to be an RSA cert, - // regardless of the key you are wrapping - KeyWrapper rsaWrap = token.getKeyWrapper( - KeyWrapAlgorithm.RSA); + signer.update(data); + } - System.out.println(".got rsaWrapper"); + byte[] signature = signer.sign(); - rsaWrap.initWrap(tcert.getPublicKey(), null); + if (client.verbose) System.out.println("Creating POP"); + pop = client.createPop(algorithm, signature); + } - System.out.println(".rsaWrap inited"); + if (client.verbose) System.out.println("Creating CRMF requrest"); + String request = client.createCRMFRequest(certRequest, pop); - byte session_data[] = rsaWrap.wrap(sk); + StringWriter sw = new StringWriter(); + try (PrintWriter out = new PrintWriter(sw)) { + out.println("-----BEGIN NEW CERTIFICATE REQUEST-----"); + out.println(request); + out.println("-----END NEW CERTIFICATE REQUEST-----"); + } + String csr = sw.toString(); + + if (submitRequest) { + System.out.println("Submitting CRMF request to " + hostPort); + client.submitRequest( + request, + hostPort, + username, + profileName, + requestor); + + } else if (output != null) { + System.out.println("Storing CRMF requrest into " + output); + try (FileWriter out = new FileWriter(output)) { + out.write(csr); + } - System.out.println(".rsaWrapped"); + } else { + System.out.println(csr); + } - try { - // create CRMF - CertTemplate certTemplate = new CertTemplate(); - certTemplate.setVersion(new INTEGER(2)); + } catch (Exception e) { + System.out.println("ERROR: " + e); + e.printStackTrace(); + System.exit(1); + } + } - Name n1 = getJssName(enable_encoding, SUBJ_DN); + public KeyPair generateRSAKeyPair(CryptoToken token, int length) throws Exception { + KeyPairGenerator kg = token.getKeyPairGenerator(KeyPairAlgorithm.RSA); + kg.initialize(length); + return kg.genKeyPair(); + } - Name n = new Name(); + public KeyPair generateECCKeyPair( + CryptoToken token, + String curve, + boolean sslECDH, + boolean temporary, + int sensitive, + int extractable) throws Exception { + /* + * used with SSL server cert that does ECDH ECDSA + * ** can only be used with POP_NONE ** + */ + org.mozilla.jss.crypto.KeyPairGeneratorSpi.Usage[] usagesMaskECDH = { + org.mozilla.jss.crypto.KeyPairGeneratorSpi.Usage.SIGN, + org.mozilla.jss.crypto.KeyPairGeneratorSpi.Usage.SIGN_RECOVER + }; + + /* used for other certs including SSL server cert that does ECDHE ECDSA */ + org.mozilla.jss.crypto.KeyPairGeneratorSpi.Usage[] usagesMask = { + org.mozilla.jss.crypto.KeyPairGeneratorSpi.Usage.DERIVE + }; + + return CryptoUtil.generateECCKeyPair( + token.getName(), + curve, + null, + sslECDH ? usagesMaskECDH : usagesMask, + temporary, + sensitive, + extractable); + } - n.addCommonName("Me"); - n.addCountryName("US"); - n.addElement(new AVA(new OBJECT_IDENTIFIER("0.9.2342.19200300.100.1.1"), new PrintableString("MyUid"))); + public byte[] wrapPrivateKey(CryptoToken token, SymmetricKey sessionKey, byte[] iv, KeyPair keyPair) throws Exception { - if (n1 != null) - certTemplate.setSubject(n1); - else - certTemplate.setSubject(n); - - certTemplate.setPublicKey(new SubjectPublicKeyInfo(pair.getPublic())); - // set extension - AlgorithmIdentifier algS = null; - if (alg.equals("rsa")) { - algS = new AlgorithmIdentifier(new OBJECT_IDENTIFIER("1.2.840.113549.3.7"), new OCTET_STRING(iv)); - } else { // ec - algS = new AlgorithmIdentifier(new OBJECT_IDENTIFIER("1.2.840.10045.2.1"), new OCTET_STRING(iv)); - } + // wrap private key using session + KeyWrapper wrapper = token.getKeyWrapper(KeyWrapAlgorithm.DES3_CBC_PAD); + wrapper.initWrap(sessionKey, new IVParameterSpec(iv)); + return wrapper.wrap((org.mozilla.jss.crypto.PrivateKey) keyPair.getPrivate()); + } - EncryptedValue encValue = new EncryptedValue(null, algS, new BIT_STRING(session_data, 0),null, null,new BIT_STRING(key_data, 0)); - EncryptedKey key = new EncryptedKey(encValue); - PKIArchiveOptions opt = new PKIArchiveOptions(key); - SEQUENCE seq = new SEQUENCE(); - if (foundTransport) { - seq.addElement(new AVA(new OBJECT_IDENTIFIER("1.3.6.1.5.5.7.5.1.4"), opt)); - } + public byte[] wrapSessionKey(CryptoToken token, X509Certificate transportCert, SymmetricKey sessionKey) throws Exception { - // Add idPOPLinkWitness control - String secretValue = "testing"; - byte[] key1 = null; - byte[] finalDigest = null; - try { - MessageDigest SHA1Digest = MessageDigest.getInstance("SHA1"); - key1 = SHA1Digest.digest(secretValue.getBytes()); - } catch (NoSuchAlgorithmException ex) { - System.exit(1); - } + // wrap session key using KRA transport cert + // currently, a transport cert has to be an RSA cert, + // regardless of the key you are wrapping + KeyWrapper wrapper = token.getKeyWrapper(KeyWrapAlgorithm.RSA); + wrapper.initWrap(transportCert.getPublicKey(), null); + return wrapper.wrap(sessionKey); + } - /* Example of adding the POP link witness control to CRMF */ - byte[] b = - { 0x10, 0x53, 0x42, 0x24, 0x1a, 0x2a, 0x35, 0x3c, - 0x7a, 0x52, 0x54, 0x56, 0x71, 0x65, 0x66, 0x4c, - 0x51, 0x34, 0x35, 0x23, 0x3c, 0x42, 0x43, 0x45, - 0x61, 0x4f, 0x6e, 0x43, 0x1e, 0x2a, 0x2b, 0x31, - 0x32, 0x34, 0x35, 0x36, 0x55, 0x51, 0x48, 0x14, - 0x16, 0x29, 0x41, 0x42, 0x43, 0x7b, 0x63, 0x44, - 0x6a, 0x12, 0x6b, 0x3c, 0x4c, 0x3f, 0x00, 0x14, - 0x51, 0x61, 0x15, 0x22, 0x23, 0x5f, 0x5e, 0x69 }; - - try { - MessageDigest SHA1Digest = MessageDigest.getInstance("SHA1"); - HMACDigest hmacDigest = new HMACDigest(SHA1Digest, key1); - hmacDigest.update(b); - finalDigest = hmacDigest.digest(); - } catch (NoSuchAlgorithmException ex) { - System.exit(1); - } + public CertRequest createCertRequest( + CryptoToken token, + X509Certificate transportCert, + String algorithm, + KeyPair keyPair, + Name subject) throws Exception { - OCTET_STRING ostr = new OCTET_STRING(finalDigest); - seq.addElement(new AVA(OBJECT_IDENTIFIER.id_cmc_idPOPLinkWitness, ostr)); - CertRequest certReq = new CertRequest(new INTEGER(1), certTemplate, seq); + PKIArchiveOptions opts = createPKIArchiveOptions(token, transportCert, algorithm, keyPair); + CertTemplate certTemplate = createCertTemplate(subject, keyPair.getPublic()); - System.out.println(".CertRequest created"); + SEQUENCE seq = new SEQUENCE(); + seq.addElement(new AVA(new OBJECT_IDENTIFIER("1.3.6.1.5.5.7.5.1.4"), opts)); - ByteArrayOutputStream bo = new ByteArrayOutputStream(); - certReq.encode(bo); - byte[] toBeVerified = bo.toByteArray(); + OCTET_STRING ostr = createIDPOPLinkWitness(); + seq.addElement(new AVA(OBJECT_IDENTIFIER.id_cmc_idPOPLinkWitness, ostr)); - // byte popdata[] = ASN1Util.encode(certReq); - byte signature[]; + return new CertRequest(new INTEGER(1), certTemplate, seq); + } - System.out.println(".CertRequest encoded"); + public OCTET_STRING createIDPOPLinkWitness() throws Exception { + + String secretValue = "testing"; + MessageDigest digest1 = MessageDigest.getInstance("SHA1"); + byte[] key1 = digest1.digest(secretValue.getBytes()); + + /* Example of adding the POP link witness control to CRMF */ + byte[] b = { + 0x10, 0x53, 0x42, 0x24, 0x1a, 0x2a, 0x35, 0x3c, + 0x7a, 0x52, 0x54, 0x56, 0x71, 0x65, 0x66, 0x4c, + 0x51, 0x34, 0x35, 0x23, 0x3c, 0x42, 0x43, 0x45, + 0x61, 0x4f, 0x6e, 0x43, 0x1e, 0x2a, 0x2b, 0x31, + 0x32, 0x34, 0x35, 0x36, 0x55, 0x51, 0x48, 0x14, + 0x16, 0x29, 0x41, 0x42, 0x43, 0x7b, 0x63, 0x44, + 0x6a, 0x12, 0x6b, 0x3c, 0x4c, 0x3f, 0x00, 0x14, + 0x51, 0x61, 0x15, 0x22, 0x23, 0x5f, 0x5e, 0x69 + }; + + MessageDigest digest2 = MessageDigest.getInstance("SHA1"); + HMACDigest hmacDigest = new HMACDigest(digest2, key1); + hmacDigest.update(b); + byte[] finalDigest = hmacDigest.digest(); + + return new OCTET_STRING(finalDigest); + } - Signature signer = null; - if (alg.equals("rsa")) { - signer = token.getSignatureContext( - SignatureAlgorithm.RSASignatureWithMD5Digest); - } else { //ec - signer = token.getSignatureContext( - SignatureAlgorithm.ECSignatureWithSHA1Digest); - } + public PKIArchiveOptions createPKIArchiveOptions( + CryptoToken token, + X509Certificate transportCert, + String algorithm, + KeyPair keyPair) throws Exception { - System.out.println(". signer created"); + KeyGenerator keyGen = token.getKeyGenerator(KeyGenAlgorithm.DES3); + SymmetricKey sessionKey = keyGen.generate(); - signer.initSign((org.mozilla.jss.crypto.PrivateKey) pair.getPrivate()); + byte[] iv = { 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 }; - System.out.println(".signer inited"); + byte[] wrappedPrivateKey = wrapPrivateKey(token, sessionKey, iv, keyPair); + byte[] wrappedSessionKey = wrapSessionKey(token, transportCert, sessionKey); - System.out.println("."); //FAIL_OR_SUCC " + FAIL_OR_SUCC); + AlgorithmIdentifier algorithmID; + if (algorithm.equals("rsa")) { + algorithmID = new AlgorithmIdentifier(new OBJECT_IDENTIFIER("1.2.840.113549.3.7"), new OCTET_STRING(iv)); - if (POP_OPTION.equals("POP_SUCCESS")) { - System.out.println("CRMFPopClient: Generating Legal POP Data....."); - signer.update(toBeVerified); - } else if (POP_OPTION.equals("POP_FAIL")) { - System.out.println("CRMFPopClient: Generating Illegal POP Data....."); - signer.update(iv); - } else if (dont_do_pop == 1) { - System.out.println("CRMFPopClient: Generating NO POP Data....."); - } + } else if (algorithm.equals("ec")) { + algorithmID = new AlgorithmIdentifier(new OBJECT_IDENTIFIER("1.2.840.10045.2.1"), new OCTET_STRING(iv)); - System.out.println("."); //signer updated"); + } else { + throw new Exception("Unknown algorithm: " + algorithm); + } - CertReqMsg crmfMsg = null; + EncryptedValue encValue = new EncryptedValue( + null, + algorithmID, + new BIT_STRING(wrappedSessionKey, 0), + null, + null, + new BIT_STRING(wrappedPrivateKey, 0)); - if (dont_do_pop == 0) - { - signature = signer.sign(); + EncryptedKey key = new EncryptedKey(encValue); - System.out.println("CRMFPopClient: Signature completed..."); - System.out.println(""); + return new PKIArchiveOptions(key); + } - AlgorithmIdentifier algID = null; - if (alg.equals("rsa")) { - algID = new AlgorithmIdentifier(SignatureAlgorithm.RSASignatureWithMD5Digest.toOID(), null ); - } else { // "ec" - algID = new AlgorithmIdentifier(SignatureAlgorithm.ECSignatureWithSHA1Digest.toOID(), null ); - } - POPOSigningKey popoKey = new POPOSigningKey(null,algID, new BIT_STRING(signature,0)); + public CertTemplate createCertTemplate(Name subject, PublicKey publicKey) throws Exception { - ProofOfPossession pop = ProofOfPossession.createSignature(popoKey); + CertTemplate template = new CertTemplate(); + template.setVersion(new INTEGER(2)); + template.setSubject(subject); + template.setPublicKey(new SubjectPublicKeyInfo(publicKey)); - crmfMsg = new CertReqMsg(certReq, pop, null); - } else { - crmfMsg = new CertReqMsg(certReq, null, null); - } + return template; + } - //crmfMsg.verify(); + public Signature createSigner( + CryptoToken token, + String algorithm, + KeyPair keyPair) throws Exception { - SEQUENCE s1 = new SEQUENCE(); - s1.addElement(crmfMsg); - byte encoded[] = ASN1Util.encode(s1); + Signature signer; + if (algorithm.equals("rsa")) { + signer = token.getSignatureContext(SignatureAlgorithm.RSASignatureWithMD5Digest); - String Req1 = Utils.base64encode(encoded); + } else if (algorithm.equals("ec")) { + signer = token.getSignatureContext(SignatureAlgorithm.ECSignatureWithSHA1Digest); - if (REQ_OUT_FILE != null) - { - System.out.println("CRMFPopClient: Generated Cert Request: ...... "); - System.out.println(""); + } else { + throw new Exception("Unknown algorithm: " + algorithm); + } - System.out.println(Req1); - System.out.println(""); - System.out.println("CRMFPopClient: End Request:"); + signer.initSign((org.mozilla.jss.crypto.PrivateKey) keyPair.getPrivate()); - PrintStream ps = null; - ps = new PrintStream(new FileOutputStream(REQ_OUT_FILE)); - ps.println("-----BEGIN NEW CERTIFICATE REQUEST-----"); - ps.println(Req1); - ps.println("-----END NEW CERTIFICATE REQUEST-----"); - ps.flush(); - ps.close(); - System.out.println("CRMFPopClient: done output request to file: "+ REQ_OUT_FILE); + return signer; + } - if (doServerHit == 0) - return; - } + public ProofOfPossession createPop(String algorithm, byte[] signature) throws Exception { - String Req = URLEncoder.encode(Req1); - - url = - new URL("http://" - + HOST_PORT + "/ca/ee/ca/profileSubmit?cert_request_type=crmf&cert_request=" - + Req + "&renewal=false&uid=" + USER_NAME + "&xmlOutput=false&&profileId=" - + profileName + "&sn_uid=" + USER_NAME +"&SubId=profile&requestor_name=" - + REQUESTOR_NAME); - - System.out.println("CRMFPopClient: Posting " + url); - - System.out.println(""); - System.out.println("CRMFPopClient: Server Response....."); - System.out.println("--------------------"); - System.out.println(""); - - conn = url.openConnection(); - is = conn.getInputStream(); - reader = new BufferedReader(new InputStreamReader(is)); - String line = null; - - while ((line = reader.readLine()) != null) { - System.out.println(line); - if (line.equals("CMS Enroll Request Success")) { - System.out.println("CRMFPopClient: Enrollment Successful: ......"); - System.out.println(""); - } - } /* while */ - } catch (Exception e) { - System.out.println("CRMFPopClient: WARNING: " + e.toString()); - e.printStackTrace(); - } - } catch (Exception e) { - System.out.println("CRMFPopClient: ERROR: " + e.toString()); - e.printStackTrace(); + AlgorithmIdentifier algorithmID; + if (algorithm.equals("rsa")) { + algorithmID = new AlgorithmIdentifier(SignatureAlgorithm.RSASignatureWithMD5Digest.toOID(), null); + + } else if (algorithm.equals("ec")) { + algorithmID = new AlgorithmIdentifier(SignatureAlgorithm.ECSignatureWithSHA1Digest.toOID(), null); + + } else { + throw new Exception("Unknown algorithm: " + algorithm); } + + POPOSigningKey popoKey = new POPOSigningKey(null, algorithmID, new BIT_STRING(signature, 0)); + return ProofOfPossession.createSignature(popoKey); } - static boolean isEncoded (String elementValue) { - boolean encoded = false; + public String createCRMFRequest( + CertRequest certRequest, + ProofOfPossession pop) throws Exception { - if (elementValue != null && ((elementValue.startsWith("UTF8String:")) || - (elementValue.startsWith("PrintableString:")) || - (elementValue.startsWith("BMPString:")) || - (elementValue.startsWith("TeletexString:")) || - (elementValue.startsWith("UniversalString:")))) { - encoded = true; - } - return encoded; + CertReqMsg crmfMessage = new CertReqMsg(certRequest, pop, null); + //crmfMessage.verify(); + + SEQUENCE seq = new SEQUENCE(); + seq.addElement(crmfMessage); + + byte[] encodedCrmfMessage = ASN1Util.encode(seq); + return Utils.base64encode(encodedCrmfMessage); } - static Name addNameElement (Name name, OBJECT_IDENTIFIER oid, int n, String elementValue) { - try { - String encodingType = (n > 0)? elementValue.substring(0, n): null; - String nameValue = (n > 0)? elementValue.substring(n+1): null; - if (encodingType != null && encodingType.length() > 0 && - nameValue != null && nameValue.length() > 0) { - if (encodingType.equals("UTF8String")) { - name.addElement( new AVA(oid, new UTF8String(nameValue))); - } else if (encodingType.equals("PrintableString")) { - name.addElement( new AVA(oid, new PrintableString(nameValue))); - } else if (encodingType.equals("BMPString")) { - name.addElement( new AVA(oid, new BMPString(nameValue))); - } else if (encodingType.equals("TeletexString")) { - name.addElement( new AVA(oid, new TeletexString(nameValue))); - } else if (encodingType.equals("UniversalString")) { - name.addElement( new AVA(oid, new UniversalString(nameValue))); + public void submitRequest( + String request, + String hostPort, + String username, + String profileName, + String requestor) throws Exception { + + String encodedRequest = URLEncoder.encode(request, "UTF-8"); + + URL url = new URL( + "http://" + hostPort + "/ca/ee/ca/profileSubmit" + + "?cert_request_type=crmf" + + "&cert_request=" + encodedRequest + + "&renewal=false&uid=" + username + + "&xmlOutput=false" + + "&profileId=" + profileName + + "&sn_uid=" + username + + "&SubId=profile" + + "&requestor_name=" + requestor); + + if (verbose) System.out.println("Opening " + url); + + URLConnection conn = url.openConnection(); + InputStream is = conn.getInputStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(is)); + + if (verbose) System.out.println("--------------------"); + String line = null; + String status = null; + String requestId = null; + while ((line = reader.readLine()) != null) { + if (verbose) System.out.println(line); + + if (line.startsWith("errorCode=")) { + int i = line.indexOf("\""); + int j = line.indexOf("\";", i+1); + String errorCode = line.substring(i+1, j); + + if ("0".equals(errorCode)) { + status = "completed"; + + } else if ("1".equals(errorCode)) { + status = "failed"; + + } else if ("2".equals(errorCode)) { + status = "pending"; + + } else { + status = "unknown"; } + + } else if (line.startsWith("requestList.requestId=")) { + int i = line.indexOf("\""); + int j = line.indexOf("\";", i+1); + requestId = line.substring(i+1, j); } - } catch (Exception e) { - System.out.println("CRMFPopClient: Error adding name element: " + elementValue + " Error: " + e.toString()); } - return name; + if (verbose) System.out.println("--------------------"); + + if (requestId != null) { + System.out.println("Request ID: " + requestId); + } + + if (status != null) { + System.out.println("Request Status: " + status); + } } - static Name getJssName(boolean enable_encoding, String dn) { + public boolean isEncoded(String elementValue) { - X500Name x5Name = null; + if (elementValue == null) return false; - try { - x5Name = new X500Name(dn); + return elementValue.startsWith("UTF8String:") + || elementValue.startsWith("PrintableString:") + || elementValue.startsWith("BMPString:") + || elementValue.startsWith("TeletexString:") + || elementValue.startsWith("UniversalString:"); + } - } catch (IOException e) { + public AVA createAVA(OBJECT_IDENTIFIER oid, int n, String elementValue) throws Exception { - System.out.println("CRMFPopClient: Illegal Subject Name: " + dn + " Error: " + e.toString()); - System.out.println("CRMFPopClient: Filling in default Subject Name......"); - return null; - } + String encodingType = n > 0 ? elementValue.substring(0, n) : null; + String nameValue = n > 0 ? elementValue.substring(n+1) : null; - Name ret = new Name(); + if (encodingType != null && encodingType.length() > 0 + && nameValue != null && nameValue.length() > 0) { - netscape.security.x509.RDN[] names = null; + if (encodingType.equals("UTF8String")) { + return new AVA(oid, new UTF8String(nameValue)); - names = x5Name.getNames(); + } else if (encodingType.equals("PrintableString")) { + return new AVA(oid, new PrintableString(nameValue)); - int nameLen = x5Name.getNamesLength(); + } else if (encodingType.equals("BMPString")) { + return new AVA(oid, new BMPString(nameValue)); - // System.out.println("x5Name len: " + nameLen); + } else if (encodingType.equals("TeletexString")) { + return new AVA(oid, new TeletexString(nameValue)); - netscape.security.x509.RDN cur = null; + } else if (encodingType.equals("UniversalString")) { + return new AVA(oid, new UniversalString(nameValue)); + + } else { + throw new Exception("Unsupported encoding: " + encodingType); + } + } - for (int i = 0; i < nameLen; i++) { - cur = names[i]; + return null; + } - String rdnStr = cur.toString(); + public Name createName(String dn, boolean encodingEnabled) throws Exception { - String[] split = rdnStr.split("="); + X500Name x500Name = new X500Name(dn); + Name jssName = new Name(); - if (split.length != 2) - continue; - int n = split[1].indexOf(':'); + for (netscape.security.x509.RDN rdn : x500Name.getNames()) { - try { + String rdnStr = rdn.toString(); + if (verbose) System.out.println("RDN: " + rdnStr); - if (split[0].equals("UID")) { - if (enable_encoding && isEncoded(split[1])) { - ret = addNameElement(ret, new OBJECT_IDENTIFIER("0.9.2342.19200300.100.1.1"), - n, split[1]); - } else { - ret.addElement(new AVA(new OBJECT_IDENTIFIER("0.9.2342.19200300.100.1.1"), - new PrintableString(split[1]))); - } - // System.out.println("UID found : " + split[1]); + String[] split = rdnStr.split("="); + if (split.length != 2) continue; - } + String attribute = split[0]; + String value = split[1]; - if (split[0].equals("C")) { - ret.addCountryName(split[1]); - // System.out.println("C found : " + split[1]); - continue; + int n = value.indexOf(':'); + if (attribute.equalsIgnoreCase("UID")) { + AVA ava; + if (encodingEnabled && isEncoded(value)) { + ava = createAVA(new OBJECT_IDENTIFIER("0.9.2342.19200300.100.1.1"), n, value); + } else { + ava = new AVA(new OBJECT_IDENTIFIER("0.9.2342.19200300.100.1.1"), new PrintableString(value)); } + jssName.addElement(ava); - if (split[0].equals("CN")) { - if (enable_encoding && isEncoded(split[1])) { - ret = addNameElement (ret, Name.commonName, n, split[1]); - } else { - ret.addCommonName(split[1]); - } - // System.out.println("CN found : " + split[1]); - continue; + } else if (attribute.equalsIgnoreCase("C")) { + jssName.addCountryName(value); + + } else if (attribute.equalsIgnoreCase("CN")) { + if (encodingEnabled && isEncoded(value)) { + jssName.addElement(createAVA(Name.commonName, n, value)); + } else { + jssName.addCommonName(value); } - if (split[0].equals("L")) { - if (enable_encoding && isEncoded(split[1])) { - ret = addNameElement (ret, Name.localityName, n, split[1]); - } else { - ret.addLocalityName(split[1]); - } - // System.out.println("L found : " + split[1]); - continue; + } else if (attribute.equalsIgnoreCase("L")) { + if (encodingEnabled && isEncoded(value)) { + jssName.addElement(createAVA(Name.localityName, n, value)); + } else { + jssName.addLocalityName(value); } - if (split[0].equals("O")) { - if (enable_encoding && isEncoded(split[1])) { - ret = addNameElement (ret, Name.organizationName, n, split[1]); - } else { - ret.addOrganizationName(split[1]); - } - // System.out.println("O found : " + split[1]); - continue; + } else if (attribute.equalsIgnoreCase("O")) { + if (encodingEnabled && isEncoded(value)) { + jssName.addElement(createAVA(Name.organizationName, n, value)); + } else { + jssName.addOrganizationName(value); } - if (split[0].equals("ST")) { - if (enable_encoding && isEncoded(split[1])) { - ret = addNameElement (ret, Name.stateOrProvinceName, n, split[1]); - } else { - ret.addStateOrProvinceName(split[1]); - } - // System.out.println("ST found : " + split[1]); - continue; + } else if (attribute.equalsIgnoreCase("ST")) { + if (encodingEnabled && isEncoded(value)) { + jssName.addElement(createAVA(Name.stateOrProvinceName, n, value)); + } else { + jssName.addStateOrProvinceName(value); } - if (split[0].equals("OU")) { - if (enable_encoding && isEncoded(split[1])) { - ret = addNameElement (ret, Name.organizationalUnitName, n, split[1]); - } else { - ret.addOrganizationalUnitName(split[1]); - } - // System.out.println("OU found : " + split[1]); - continue; + } else if (attribute.equalsIgnoreCase("OU")) { + if (encodingEnabled && isEncoded(value)) { + jssName.addElement(createAVA(Name.organizationalUnitName, n, value)); + } else { + jssName.addOrganizationalUnitName(value); } - } catch (Exception e) { - System.out.println("CRMFPopClient: Error constructing RDN: " + rdnStr + " Error: " + e.toString()); - continue; + } else { + throw new Exception("Unsupported attribute: " + attribute); } - } - return ret; - + return jssName; } } diff --git a/base/java-tools/src/com/netscape/cmstools/cli/MainCLI.java b/base/java-tools/src/com/netscape/cmstools/cli/MainCLI.java index 8c3805e00..1dbf02760 100644 --- a/base/java-tools/src/com/netscape/cmstools/cli/MainCLI.java +++ b/base/java-tools/src/com/netscape/cmstools/cli/MainCLI.java @@ -168,6 +168,10 @@ public class MainCLI extends CLI { option.setArgName("passwordfile"); options.addOption(option); + option = new Option(null, "token", true, "Security token name"); + option.setArgName("token"); + options.addOption(option); + option = new Option(null, "output", true, "Folder to store HTTP messages"); option.setArgName("folder"); options.addOption(option); @@ -286,6 +290,8 @@ public class MainCLI extends CLI { String certNickname = cmd.getOptionValue("n"); String certPassword = cmd.getOptionValue("c"); String certPasswordFile = cmd.getOptionValue("C"); + String tokenName = cmd.getOptionValue("token"); + String username = cmd.getOptionValue("u"); String password = cmd.getOptionValue("w"); String passwordFile = cmd.getOptionValue("W"); @@ -323,6 +329,9 @@ public class MainCLI extends CLI { if (certDatabase != null) config.setCertDatabase(new File(certDatabase).getAbsolutePath()); + // store token name + config.setTokenName(tokenName); + // store certificate nickname config.setCertNickname(certNickname); @@ -420,14 +429,25 @@ public class MainCLI extends CLI { // Main program should initialize client security database if (certDatabase.exists()) { + if (verbose) System.out.println("Initializing client security database"); CryptoManager.initialize(certDatabase.getAbsolutePath()); } - // If password is specified, use password to access client security database + // If password is specified, use password to access security token if (config.getCertPassword() != null) { + if (verbose) System.out.println("Logging into security token"); try { CryptoManager manager = CryptoManager.getInstance(); - CryptoToken token = manager.getInternalKeyStorageToken(); + + CryptoToken token; + String tokenName = config.getTokenName(); + if (tokenName == null) { + token = manager.getInternalKeyStorageToken(); + } else { + token = manager.getTokenByName(tokenName); + } + manager.setThreadToken(token); + Password password = new Password(config.getCertPassword().toCharArray()); token.login(password); diff --git a/base/java-tools/src/com/netscape/cmstools/client/ClientCertRequestCLI.java b/base/java-tools/src/com/netscape/cmstools/client/ClientCertRequestCLI.java index 9e7b7e3f1..996bcf275 100644 --- a/base/java-tools/src/com/netscape/cmstools/client/ClientCertRequestCLI.java +++ b/base/java-tools/src/com/netscape/cmstools/client/ClientCertRequestCLI.java @@ -18,7 +18,9 @@ package com.netscape.cmstools.client; +import java.io.ByteArrayOutputStream; import java.io.File; +import java.security.KeyPair; import java.util.Vector; import netscape.ldap.util.DN; @@ -27,15 +29,26 @@ import netscape.ldap.util.RDN; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Option; import org.apache.commons.io.FileUtils; +import org.mozilla.jss.CryptoManager; +import org.mozilla.jss.crypto.CryptoToken; +import org.mozilla.jss.crypto.Signature; +import org.mozilla.jss.crypto.X509Certificate; +import org.mozilla.jss.pkix.crmf.CertRequest; +import org.mozilla.jss.pkix.crmf.ProofOfPossession; +import org.mozilla.jss.pkix.primitive.Name; import com.netscape.certsrv.cert.CertClient; import com.netscape.certsrv.cert.CertEnrollmentRequest; import com.netscape.certsrv.cert.CertRequestInfos; import com.netscape.certsrv.profile.ProfileAttribute; import com.netscape.certsrv.profile.ProfileInput; +import com.netscape.certsrv.system.SystemCertClient; +import com.netscape.cmstools.CRMFPopClient; import com.netscape.cmstools.cert.CertCLI; import com.netscape.cmstools.cli.CLI; import com.netscape.cmstools.cli.MainCLI; +import com.netscape.cmsutil.util.Cert; +import com.netscape.cmsutil.util.Utils; /** * @author Endi S. Dewata @@ -56,15 +69,47 @@ public class ClientCertRequestCLI extends CLI { } public void createOptions() { - Option option = new Option(null, "algorithm", true, "Algorithm (default: rsa)"); - option.setArgName("algorithm"); + Option option = new Option(null, "type", true, "Request type (default: pkcs10)"); + option.setArgName("request type"); + options.addOption(option); + + option = new Option(null, "attribute-encoding", true, "Attribute encoding (default: false)"); + option.setArgName("boolean"); + options.addOption(option); + + option = new Option(null, "algorithm", true, "Algorithm (default: rsa)"); + option.setArgName("algorithm name"); options.addOption(option); option = new Option(null, "length", true, "RSA key length (default: 1024)"); - option.setArgName("length"); + option.setArgName("key length"); + options.addOption(option); + + option = new Option(null, "curve", true, "ECC key curve name (default: nistp256)"); + option.setArgName("curve name"); + options.addOption(option); + + option = new Option(null, "ssl-ecdh", true, "SSL certificate with ECDH ECDSA (default: false)"); + option.setArgName("boolean"); + options.addOption(option); + + option = new Option(null, "temporary", true, "Temporary (default: true)"); + option.setArgName("boolean"); + options.addOption(option); + + option = new Option(null, "sensitive", true, "Sensitive"); + option.setArgName("boolean"); options.addOption(option); - option = new Option(null, "profile", true, "Certificate profile (default: caUserCert)"); + option = new Option(null, "extractable", true, "Extractable"); + option.setArgName("boolean"); + options.addOption(option); + + option = new Option(null, "transport-cert", true, "Transport certificate"); + option.setArgName("path"); + options.addOption(option); + + option = new Option(null, "profile", true, "Certificate profile (default: caEncUserCert)"); option.setArgName("profile"); options.addOption(option); @@ -104,10 +149,45 @@ public class ClientCertRequestCLI extends CLI { String subjectDN = cmdArgs[0]; + // pkcs10, crmf + String requestType = cmd.getOptionValue("type", "pkcs10"); + + boolean attributeEncoding = Boolean.parseBoolean(cmd.getOptionValue("attribute-encoding", "false")); + + // rsa, ec String algorithm = cmd.getOptionValue("algorithm", "rsa"); - String length = cmd.getOptionValue("length", "1024"); - String profileID = cmd.getOptionValue("profile", "caUserCert"); - String requestType = "pkcs10"; + int length = Integer.parseInt(cmd.getOptionValue("length", "1024")); + + String curve = cmd.getOptionValue("curve", "nistp256"); + boolean sslECDH = Boolean.parseBoolean(cmd.getOptionValue("ssl-ecdh", "false")); + boolean temporary = Boolean.parseBoolean(cmd.getOptionValue("temporary", "true")); + + String s = cmd.getOptionValue("sensitive"); + int sensitive; + if (s == null) { + sensitive = -1; + } else { + sensitive = Boolean.parseBoolean(s) ? 1 : 0; + } + + s = cmd.getOptionValue("extractable"); + int extractable; + if (s == null) { + extractable = -1; + } else { + extractable = Boolean.parseBoolean(s) ? 1 : 0; + } + + String transportCertFilename = cmd.getOptionValue("transport-cert"); + + String profileID = cmd.getOptionValue("profile"); + if (profileID == null) { + if ("rsa".equals(algorithm)) { + profileID = "caUserCert"; + } else if ("ec".equals(algorithm)) { + profileID = "caEncECUserCert"; + } + } MainCLI mainCLI = (MainCLI)parent.getParent(); File certDatabase = mainCLI.certDatabase; @@ -118,38 +198,48 @@ public class ClientCertRequestCLI extends CLI { System.exit(-1); } - File csrFile = File.createTempFile("pki-client-cert-request-", ".csr", certDatabase); - csrFile.deleteOnExit(); + String csr; + if ("pkcs10".equals(requestType)) { + csr = generatePkcs10Request(certDatabase, password, algorithm, length, subjectDN); - String[] commands = { - "/usr/bin/PKCS10Client", - "-d", certDatabase.getAbsolutePath(), - "-p", password, - "-a", algorithm, - "-l", length, - "-o", csrFile.getAbsolutePath(), - "-n", subjectDN - }; + // initialize database after PKCS10Client to avoid conflict + mainCLI.init(); + client = mainCLI.getClient(); - Runtime rt = Runtime.getRuntime(); - Process p = rt.exec(commands); - int rc = p.waitFor(); - if (rc != 0) { - MainCLI.printMessage("CSR generation failed"); - return; + } else if ("crmf".equals(requestType)) { + + // initialize database before CRMFPopClient to load transport certificate + mainCLI.init(); + client = mainCLI.getClient(); + + String encoded; + if (transportCertFilename == null) { + SystemCertClient certClient = new SystemCertClient(client, "kra"); + encoded = certClient.getTransportCert().getEncoded(); + + } else { + encoded = FileUtils.readFileToString(new File(transportCertFilename)); + } + + encoded = Cert.normalizeCertStrAndReq(encoded); + encoded = Cert.stripBrackets(encoded); + byte[] transportCertData = Utils.base64decode(encoded); + + CryptoManager manager = CryptoManager.getInstance(); + X509Certificate transportCert = manager.importCACertPackage(transportCertData); + + csr = generateCrmfRequest(transportCert, subjectDN, attributeEncoding, algorithm, length, curve, sslECDH, temporary, sensitive, extractable); + + } else { + throw new Exception("Unknown request type: " + requestType); } if (verbose) { - System.out.println("CSR generated: " + csrFile); + System.out.println("CSR:"); + System.out.println(csr); } - String csr = FileUtils.readFileToString(csrFile); - - // late initialization - mainCLI.init(); - client = mainCLI.getClient(); - CertClient certClient = new CertClient(client, "ca"); if (verbose) { @@ -188,4 +278,86 @@ public class ClientCertRequestCLI extends CLI { MainCLI.printMessage("Submitted certificate request"); CertCLI.printCertRequestInfos(infos); } + + public String generatePkcs10Request( + File certDatabase, + String password, + String algorithm, + int length, + String subjectDN + ) throws Exception { + + File csrFile = File.createTempFile("pki-client-cert-request-", ".csr", certDatabase); + csrFile.deleteOnExit(); + + String[] commands = { + "/usr/bin/PKCS10Client", + "-d", certDatabase.getAbsolutePath(), + "-p", password, + "-a", algorithm, + "-l", "" + length, + "-o", csrFile.getAbsolutePath(), + "-n", subjectDN + }; + + Runtime rt = Runtime.getRuntime(); + Process p = rt.exec(commands); + + int rc = p.waitFor(); + if (rc != 0) { + throw new Exception("CSR generation failed"); + } + + if (verbose) { + System.out.println("CSR generated: " + csrFile); + } + + return FileUtils.readFileToString(csrFile); + } + + public String generateCrmfRequest( + X509Certificate transportCert, + String subjectDN, + boolean attributeEncoding, + String algorithm, + int length, + String curve, + boolean sslECDH, + boolean temporary, + int sensitive, + int extractable + ) throws Exception { + + CryptoManager manager = CryptoManager.getInstance(); + CryptoToken token = manager.getThreadToken(); + + CRMFPopClient client = new CRMFPopClient(); + + Name subject = client.createName(subjectDN, attributeEncoding); + + KeyPair keyPair; + if (algorithm.equals("rsa")) { + keyPair = client.generateRSAKeyPair(token, length); + + } else if (algorithm.equals("ec")) { + keyPair = client.generateECCKeyPair(token, curve, sslECDH, temporary, sensitive, extractable); + + } else { + throw new Exception("Unknown algorithm: " + algorithm); + } + + CertRequest certRequest = client.createCertRequest(token, transportCert, algorithm, keyPair, subject); + + Signature signer = client.createSigner(token, algorithm, keyPair); + + ByteArrayOutputStream bo = new ByteArrayOutputStream(); + certRequest.encode(bo); + signer.update(bo.toByteArray()); + + byte[] signature = signer.sign(); + + ProofOfPossession pop = client.createPop(algorithm, signature); + + return client.createCRMFRequest(certRequest, pop); + } } diff --git a/base/java-tools/templates/pki_java_command_wrapper.in b/base/java-tools/templates/pki_java_command_wrapper.in index c66cb538d..dbc498616 100644 --- a/base/java-tools/templates/pki_java_command_wrapper.in +++ b/base/java-tools/templates/pki_java_command_wrapper.in @@ -132,6 +132,7 @@ fi JNI_JAR_DIR=`source /usr/share/pki/etc/pki.conf && source /etc/pki/pki.conf && echo $JNI_JAR_DIR` CP=${JNI_JAR_DIR}/jss4.jar CP=/usr/share/java/commons-codec.jar:${CP} +CP=/usr/share/java/commons-io.jar:${CP} CP=/usr/share/java/ldapjdk.jar:${CP} CP=/usr/share/java/${PRODUCT}/pki-nsutil.jar:${CP} CP=/usr/share/java/${PRODUCT}/pki-cmsutil.jar:${CP} |