/** * Certificate Generation engine * * @param subject Certificate subject (e.g C=US,O=Grid,OU=OGSA,CN=John Doe) * @throws Exception if an error occurs */ public CertGenerator(String subject) throws Exception { _subject = makeCertDN(subject); _strength = Integer.parseInt(GSIProperties.getString(GSIProperties.CERT_STRENGTH)); logger.debug("Constructor Subject:" + subject); }
/** * A class to automate the Globus Proxy creation and certificate installation. Emulates: * grid-proxy-init, install-certs, etc... Usage: * * <pre> * // proxy creation... * CertGenerator generator = new CertGenerator("Vladimir SIlva","IBMGrid"); * generator.generateCertAndKey("2p2dkdt"); * * GlobusCredential cred = generator.gridProxyInit("2p2dkdt", 512, 12); * System.out.println(cred.toString()); * * // save creds... * generator.saveCertificates("c:\\temp\\usercert.pem", "c:\\temp\\userkey.pem"); * generator.saveProxy(cred, "c:\\temp\\proxy.pem"); * </pre> */ public class CertGenerator { static Logger logger = Logger.getLogger(CertGenerator.class); private String _certPEM = null; private String _keyPEM = null; private String _certRQPEM = null; /* Cert gen subject */ X509Name _subject = null; private int _strength = 1024; private ResourceBundle _resBundle = GSIProperties.getResBundle(); /** * Certificate Generation engine * * @param subject Certificate subject (e.g C=US,O=Grid,OU=OGSA,CN=John Doe) * @throws Exception if an error occurs */ public CertGenerator(String subject) throws Exception { _subject = makeCertDN(subject); _strength = Integer.parseInt(GSIProperties.getString(GSIProperties.CERT_STRENGTH)); logger.debug("Constructor Subject:" + subject); } /** * Certificate Generation engine * * @param subject subject Certificate subject (e.g C=US,O=Grid,OU=OGSA,CN=John Doe) * @param strength engine stren (e.g 1024) * @throws Exception */ public CertGenerator(String subject, int strength) throws Exception { _subject = makeCertDN(subject); _strength = strength; logger.debug("Constructor Subject:" + subject + " stren=" + strength); } /** * generateCertAndKey Creates a Signed User certificate and a private key by generating a self * signed user certificate. Private key is encrypted w/ Pwd Certificates are kept internally. (CN * and OU are given to the contructor) * * @param Pwd = Challenge pwd (used to encrypt pirv key) * @throws FileNotFoundException * @throws IOException */ public void generateSelfSignedCertAndKey(String Pwd) throws NoSuchAlgorithmException, Exception { if (_subject == null) throw new Exception(_resBundle.getString(GSIProperties.MSG_DN_INFO_REQUIRED)); if (Pwd == null) throw new Exception(_resBundle.getString(GSIProperties.MSG_INVALID_PWD)); logger.debug( "generateSelfSignedCertAndKey Cert subject: " + _subject.getNameString() + " Strength=" + _strength + " Pwd=" + Pwd); // Generate A Cert RQ StringWriter sw = new StringWriter(); // wil contain the priv key PEM BufferedWriter bw = new BufferedWriter(sw); KeyPair kp = CertRequest.generateKey("RSA", _strength, Pwd, bw, true); // gen pub/priv keys // certs are valid for 1 year: 31536000 secs byte[] certBytes = CertRequest.makeSelfSignedCert(kp, _subject, 31536000); // Private key _keyPEM = sw.toString(); logger.debug("CertKeyGenerator: Private key PEM\n" + _keyPEM); // cert in PEM format // _certPEM = "Certificate:\n" + // (CertUtil.loadCertificate(new ByteArrayInputStream(certBytes))).toString() + "\n" + _certPEM = writePEM(certBytes, "-----BEGIN CERTIFICATE-----\n", "-----END CERTIFICATE-----\n"); logger.debug("CertKeyGenerator: Signed Cert RQ . signedUserCert\n" + _certPEM); } /** * createCertRequest: Create a certificate request PEM encoded string * * @param bits Certificate strenght in bits (e.g 512) * @param Pwd passphrase used to encrypt the private key * @return PEM encoded cert rq string * @throws IOException * @throws NoSuchProviderException * @throws NoSuchAlgorithmException */ public synchronized void createCertRequest(int bits, String Pwd) throws IOException, NoSuchProviderException, NoSuchAlgorithmException, GeneralSecurityException { // Pwd cannot be null if (Pwd == null) throw new GeneralSecurityException("Invalid NULL password"); /* * Generate A Cert RQ. Using the CertRequest utility class * implemented in puretls.jar */ logger.debug( "createCertRequest: Creating a cert request Subject:" + _subject.getNameString() + " bits=" + bits + " pwd=" + Pwd); StringWriter sw = new StringWriter(); // wil contain the priv key PEM BufferedWriter bw = new BufferedWriter(sw); /* * Generate public/private keys */ KeyPair kp = CertRequest.generateKey("RSA", bits, Pwd, bw, true); byte[] req = CertRequest.makePKCS10Request(kp, _subject); /* * Save data in PEM format */ _certRQPEM = buildRequestInfoHeader(_subject.getNameString()) + writePEM( req, "-----BEGIN CERTIFICATE REQUEST-----\n", "-----END CERTIFICATE REQUEST-----\n"); _keyPEM = sw.toString(); logger.debug("createCertRequest: Cert RQ\n" + _certRQPEM + "Key\n" + _keyPEM); } /* Cert RQ info header */ private String buildRequestInfoHeader(String subject) { StringBuffer buff = new StringBuffer( "This is a Certificate Request file:\nIt should be mailed to to a CA for signature"); buff.append("\n==============================================================="); buff.append("\nCertificate Subject:\n\t"); buff.append(subject); buff.append("\n\n"); return buff.toString(); } /** Write certficate bytes into a PEM encoded string */ public static String writePEM(byte[] bytes, String hdr, String ftr) throws IOException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); Base64OutputStream b64os = new Base64OutputStream(bos); b64os.write(bytes); b64os.flush(); b64os.close(); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); InputStreamReader irr = new InputStreamReader(bis); BufferedReader r = new BufferedReader(irr); StringBuffer buff = new StringBuffer(); String line; buff.append(hdr); while ((line = r.readLine()) != null) { buff.append(line + "\n"); } buff.append(ftr); return buff.toString(); } /* * makeCertDN: Creates an X509 Identity based on a string subject * e.g: "C=US,O=Grid,OU=OGSA,OU=Foo,CN=John Doe" */ private static X509Name makeCertDN(String subject) throws Exception { Vector tdn = new Vector(); // Vector elems = new Vector(); StringTokenizer st = new StringTokenizer(subject, ","); for (; st.hasMoreTokens(); ) { String s = st.nextToken(); // [key=value] if (s.indexOf("=") == -1) throw new Exception("Invalid subject format: " + subject + " Offending value: " + s); String key = s.substring(0, s.indexOf("=")).trim(); String val = s.substring(s.indexOf("=") + 1).trim(); if (val == null || val.equals("")) throw new Exception("Invalid subject format: " + subject + " Offending value: " + s); // logger.debug(key + "=" + val); String[] temp = {key, val}; tdn.addElement(temp); } // COM.claymoresystems.cert (puretls.jar) return CertRequest.makeSimpleDN(tdn); } /** * Creates an X509 version3 certificate * * @param algorithm (e.g RSA, DSA, etc...) * @param bits Cet strength e.g 1024 * @param issuer Issuer string e.g "O=Grid,OU=OGSA,CN=ACME" * @param subject Subject string e.g "O=Grid,OU=OGSA,CN=John Doe" * @param months time to live * @param outPrivKey OutputStream to the private key in PKCS#8 format (Note: this key will not be * encrypted) * @return X509 V3 Certificate * @throws GeneralSecurityException */ public static X509Certificate createX509Cert( String algorithm, int bits, String issuer, String subject, int months, OutputStream outPrivKey, String sigAlg, String pwd) throws GeneralSecurityException, IOException { // String sigAlg = "SHA1WithRSAEncryption"; // Priv key is in PKCS#8 format KeyPair kp = CertUtil.generateKeyPair(algorithm, bits); // must convert from PKCS#8 to PKCS#1 to encrypt with BouncyCastleOpenSSLKey // Priv key must be DER encoded key data in PKCS#1 format to be encrypted. OpenSSLKey PKCS_8key = new BouncyCastleOpenSSLKey(kp.getPrivate()); long serial = 0; logger.debug( "createX509Cert Alg: " + algorithm + " bits:" + bits + " Issuer: " + issuer + " Subject: " + subject); logger.debug( "createX509Cert Sig alg:" + sigAlg + " Priv key format:" + PKCS_8key.getPrivateKey().getFormat()); // if ( pwd != null && ! PKCS_8key.isEncrypted()) // PKCS_8key.encrypt(pwd); // write private key PKCS_8key.writeTo(outPrivKey); // return X509 Cert return createX509V3Certificate( kp.getPublic(), kp.getPrivate(), months, issuer, subject, serial, sigAlg); } /* Cert creation engine */ private static synchronized X509Certificate createX509V3Certificate( PublicKey pubKey, PrivateKey privKey, int ttlMonths, String issuerDN, String subjectDN, long serial, String signAlgoritm) throws GeneralSecurityException { X509V3CertificateGenerator certGenerator = new X509V3CertificateGenerator(); certGenerator.reset(); certGenerator.setSerialNumber(java.math.BigInteger.valueOf(serial)); certGenerator.setIssuerDN(new org.bouncycastle.asn1.x509.X509Name(issuerDN)); certGenerator.setNotBefore(new java.util.Date(System.currentTimeMillis())); certGenerator.setNotAfter( new java.util.Date(System.currentTimeMillis() + ttlMonths * (1000L * 60 * 60 * 24 * 30))); certGenerator.setSubjectDN(new org.bouncycastle.asn1.x509.X509Name(subjectDN)); certGenerator.setPublicKey(pubKey); certGenerator.setSignatureAlgorithm(signAlgoritm); X509Certificate cert = certGenerator.generateX509Certificate(privKey, "BC", new java.security.SecureRandom()); cert.checkValidity(new java.util.Date()); cert.verify(pubKey); return cert; } /** * readPEM: Read a PEM encoded base64 stream and decode it * * @param is Base64 PEM encoded stream * @param hdr Header delimeter (e.g. ----------CERTIFICATE---------) * @param ftr Footer delimeter (e.g. ----------END CERTIFICATE---------) * @return decoded DER bytes * @throws IOException if a read error occurs */ public static byte[] readPEM(InputStream is, String hdr, String ftr) throws IOException { logger.debug("Reading PEM hdr:" + hdr + " ftr:" + ftr); is.reset(); InputStreamReader irr = new InputStreamReader(is); BufferedReader r = new BufferedReader(irr); StringBuffer buff = new StringBuffer(); String line; boolean read = false; while ((line = r.readLine()) != null) { if (line.equals(hdr)) { read = true; continue; } if (line.equals(ftr)) read = false; if (read) buff.append(line); } return Base64.decode(buff.toString().getBytes()); } /** * Test if a X509 key is encryped * * @param inKey X509 key stream * @return true if encrypted e;se flase * @throws IOException * @throws GeneralSecurityException */ public static boolean isKeyEncrypted(InputStream inKey) throws IOException, GeneralSecurityException { return (new BouncyCastleOpenSSLKey(inKey)).isEncrypted(); } /* getter methods */ public String getCertPEM() { return _certPEM; } public String getKeyPEM() { return _keyPEM; } public String getCertRQPEM() { return _certRQPEM; } public X509Name getSubject() { return _subject; } /* setter(mutator) mthods */ public void setCertPEM(String certPEM) { _certPEM = certPEM; } public void setKeyPEM(String keyPEM) { _keyPEM = keyPEM; } public String toString() { return "\nCert Request:\n" + _certRQPEM + "\nCertificate:\n" + _certPEM + "\nKey:\n" + _keyPEM; } }