/*
* JBoss, Home of Professional Open Source.
* Copyright 2012, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This 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 (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.picketbox.keystore;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.URL;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.KeyStoreSpi;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Date;
import java.util.Enumeration;
import java.util.Scanner;
import java.util.Vector;
import org.picketbox.keystore.util.Base64;
import org.picketbox.keystore.util.CertificateUtil;
import org.picketbox.keystore.util.KeyStoreDBUtil;
/**
* An extension of {@link KeyStoreSpi}
*
* @author anil saldhana
* @since Aug 13, 2012
*/
public class PicketBoxDBKeyStore extends KeyStoreSpi {
String type = "jks";
private Connection con = null;
private String storeTableName = null, metadataTableName = null;
@Override
public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException, UnrecoverableKeyException {
PreparedStatement preparedStatement = null;
try {
if (matchKeyPass(alias, password) == false) {
throw new UnrecoverableKeyException("Key Password does not match");
}
String selectSQL = "SELECT KEY FROM " + storeTableName + " WHERE ID = ?";
preparedStatement = con.prepareStatement(selectSQL);
preparedStatement.setString(1, alias);
ResultSet rs = preparedStatement.executeQuery();
while (rs.next()) {
String keyString = rs.getString("KEY");
byte[] keyBytes = Base64.decode(keyString);
ObjectInputStream oos = new ObjectInputStream(new ByteArrayInputStream(keyBytes));
return (Key) oos.readObject();
}
} catch (KeyStoreException e) {
throw new RuntimeException(e);
} catch (SQLException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} finally {
safeClose(preparedStatement);
}
return null;
}
@Override
public Certificate[] engineGetCertificateChain(String alias) {
PreparedStatement preparedStatement = null;
try {
String selectSQL = "SELECT CHAIN FROM " + storeTableName + " WHERE ID = ?";
preparedStatement = con.prepareStatement(selectSQL);
preparedStatement.setString(1, alias);
ResultSet rs = preparedStatement.executeQuery();
while (rs.next()) {
String certString = rs.getString("CHAIN");
byte[] cert = Base64.decode(certString);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(cert));
return (Certificate[]) ois.readObject();
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
safeClose(preparedStatement);
}
return null;
}
@Override
public Certificate engineGetCertificate(String alias) {
PreparedStatement preparedStatement = null;
try {
String selectSQL = "SELECT CERT FROM " + storeTableName + " WHERE ID = ?";
preparedStatement = con.prepareStatement(selectSQL);
preparedStatement.setString(1, alias);
ResultSet rs = preparedStatement.executeQuery();
while (rs.next()) {
String certString = rs.getString("CERT");
byte[] cert = Base64.decode(certString);
CertificateFactory fact = CertificateFactory.getInstance("x509");
return fact.generateCertificate(new ByteArrayInputStream(cert));
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
safeClose(preparedStatement);
}
return null;
}
@Override
public Date engineGetCreationDate(String alias) {
PreparedStatement preparedStatement = null;
try {
String selectSQL = "SELECT CREATED FROM " + storeTableName + " WHERE ID = ?";
preparedStatement = con.prepareStatement(selectSQL);
preparedStatement.setString(1, alias);
ResultSet rs = preparedStatement.executeQuery();
while (rs.next()) {
String dateString = rs.getString("CREATED");
long timeValue = Long.parseLong(dateString);
return new Date(timeValue);
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
safeClose(preparedStatement);
}
return null;
}
@Override
public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain) throws KeyStoreException {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(key);
engineSetKeyEntry(alias, baos.toByteArray(), chain);
storeKeyPass(alias, password);
} catch (IOException e) {
throw new KeyStoreException(e);
}
}
@Override
public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain) throws KeyStoreException {
PreparedStatement preparedStatement = null;
try {
String encodedKey = Base64.encodeBytes(key);
String insertTableSQL = "INSERT INTO " + storeTableName + "(ID,KEY,CREATED) VALUES" + "(?,?,?)";
boolean rowExists = checkRowExists(alias);
String thirdIndex = (new Date()).getTime() + "";
if (rowExists) {
insertTableSQL = "UPDATE " + storeTableName + " SET ID= ? , KEY = ? WHERE ID = ?";
thirdIndex = alias;
}
preparedStatement = con.prepareStatement(insertTableSQL);
preparedStatement.setString(1, alias);
preparedStatement.setString(2, encodedKey);
preparedStatement.setString(3, thirdIndex);
preparedStatement.executeUpdate();
preparedStatement.close();
if (chain != null) {
storeCertificateChain(alias, chain);
}
} catch (Exception e) {
throw new KeyStoreException(e);
} finally {
if (preparedStatement != null) {
safeClose(preparedStatement);
}
}
}
@Override
public void engineSetCertificateEntry(String alias, Certificate cert) throws KeyStoreException {
PreparedStatement preparedStatement = null;
try {
String encodedCert = Base64.encodeBytes(cert.getEncoded());
String insertTableSQL = "INSERT INTO " + storeTableName + "(ID,CERT,CREATED) VALUES" + "(?,?,?)";
String thirdIndex = (new Date()).getTime() + "";
boolean rowExists = checkRowExists(alias);
if (rowExists) {
insertTableSQL = "UPDATE " + storeTableName + " SET ID= ? , CERT = ? WHERE ID = ?";
thirdIndex = alias;
}
preparedStatement = con.prepareStatement(insertTableSQL);
preparedStatement.setString(1, alias);
preparedStatement.setString(2, encodedCert);
preparedStatement.setString(3, thirdIndex);
preparedStatement.executeUpdate();
preparedStatement.close();
} catch (Exception e) {
throw new KeyStoreException(e);
} finally {
if (preparedStatement != null) {
safeClose(preparedStatement);
}
}
}
@Override
public void engineDeleteEntry(String alias) throws KeyStoreException {
try {
if (!checkRowExists(alias))
return;
} catch (Exception e1) {
throw new RuntimeException(e1);
}
String insertTableSQL = "UPDATE " + storeTableName + " SET ID= ? , KEY = ? WHERE ID = ?";
PreparedStatement preparedStatement = null;
try {
preparedStatement = con.prepareStatement(insertTableSQL);
preparedStatement.setString(1, alias);
preparedStatement.setString(2, "");
preparedStatement.setString(3, alias);
preparedStatement.executeUpdate();
preparedStatement.close();
} catch (Exception e) {
throw new KeyStoreException(e);
} finally {
if (preparedStatement != null) {
safeClose(preparedStatement);
}
}
throw new RuntimeException();
}
@Override
public Enumeration<String> engineAliases() {
Vector<String> vect = new Vector<String>();
try {
String selectSQL = "SELECT ID FROM " + storeTableName;
Statement stat = con.createStatement();
ResultSet rs = stat.executeQuery(selectSQL);
while (rs.next()) {
vect.add(rs.getString(1));
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return vect.elements();
}
@Override
public boolean engineContainsAlias(String alias) {
try {
return checkRowExists(alias);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public int engineSize() {
try {
String selectSQL = "SELECT COUNT(*) FROM " + storeTableName;
Statement stat = con.createStatement();
ResultSet rs = stat.executeQuery(selectSQL);
while (rs.next()) {
return rs.getInt(1);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return 0;
}
@Override
public boolean engineIsKeyEntry(String alias) {
PreparedStatement preparedStatement = null;
try {
String selectSQL = "SELECT KEY FROM " + storeTableName + " WHERE ID = ?";
preparedStatement = con.prepareStatement(selectSQL);
preparedStatement.setString(1, alias);
ResultSet rs = preparedStatement.executeQuery();
while (rs.next()) {
return true; // Atleast one entry
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
safeClose(preparedStatement);
}
return false;
}
@Override
public boolean engineIsCertificateEntry(String alias) {
PreparedStatement preparedStatement = null;
try {
String selectSQL = "SELECT CERT FROM " + storeTableName + " WHERE ID = ?";
preparedStatement = con.prepareStatement(selectSQL);
preparedStatement.setString(1, alias);
ResultSet rs = preparedStatement.executeQuery();
while (rs.next()) {
return true; // Atleast one entry
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
safeClose(preparedStatement);
}
return false;
}
@Override
public String engineGetCertificateAlias(Certificate cert) {
PreparedStatement preparedStatement = null;
try {
String selectSQL = "SELECT ID FROM " + storeTableName + " WHERE CERT = ?";
preparedStatement = con.prepareStatement(selectSQL);
String encoded = Base64.encodeBytes(cert.getEncoded());
preparedStatement.setString(1, encoded);
ResultSet rs = preparedStatement.executeQuery();
while (rs.next()) {
return rs.getString("ID");
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
safeClose(preparedStatement);
}
return null;
}
@Override
public void engineStore(OutputStream stream, char[] password) throws IOException, NoSuchAlgorithmException,
CertificateException {
// We do not do anything
}
@Override
public void engineLoad(InputStream stream, char[] password) throws IOException, NoSuchAlgorithmException,
CertificateException {
if (con == null) {
loadDatabase(password);
}
}
/**
* Check if the salt exists
*
* @return
*/
public boolean existsSalt() {
return getSalt() != null;
}
/**
* Check if the master password exists
*
* @return
*/
public boolean existsMasterPassword() {
return getMasterPassword() != null;
}
/**
* Store Master Salt if not already present
*
* @param salt
*/
public void storeMasterSalt(String salt) {
if (existsSalt()) {
throw new RuntimeException("Master Salt already present");
}
System.out.println("Storing Master Salt in the DB");
PreparedStatement preparedStatement = null;
try {
String insertTableSQL = "INSERT INTO " + metadataTableName + " (SALT) VALUES(?)";
preparedStatement = con.prepareStatement(insertTableSQL);
preparedStatement.setString(1, salt);
int result = preparedStatement.executeUpdate();
preparedStatement.close();
System.out.println("Stored Master Salt in the DB [" + result + " rows affected] ");
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
if (preparedStatement != null) {
safeClose(preparedStatement);
}
}
}
/**
* Store Master Password if not already present
*
* @param masterPassword
*/
public void storeMasterPassword(char[] masterPassword) {
if (getMasterPassword() != null) {
throw new RuntimeException("Master Password already present");
}
String salt = getSalt();
PreparedStatement preparedStatement = null;
try {
String encodedPass = KeyStoreDBUtil.saltedHmacMD5(salt, (new String(masterPassword).getBytes()));
String insertTableSQL = "UPDATE " + metadataTableName + " SET PASS = ?";
preparedStatement = con.prepareStatement(insertTableSQL);
preparedStatement.setString(1, encodedPass);
preparedStatement.executeUpdate();
preparedStatement.close();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
if (preparedStatement != null) {
safeClose(preparedStatement);
}
}
}
private void loadDatabase(char[] password) {
if (password == null) {
throw new IllegalArgumentException("KeyStore Password is null");
}
loadDBConnection();
// Let us evaluate the password
String salt = getSalt();
if (salt == null)
throw new RuntimeException("Salt is null");
try {
String saltedPassword = KeyStoreDBUtil.saltedHmacMD5(salt, (new String(password)).getBytes());
if (saltedPassword.equals(getMasterPassword()) == false) {
throw new RuntimeException("The master password does not match");
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void loadDBConnection() {
KeyStoreDBUtil util = new KeyStoreDBUtil();
con = util.getConnection();
storeTableName = util.getStoreTableName();
metadataTableName = util.getMetadataTableName();
}
private void safeClose(Statement stmt) {
if (stmt != null) {
try {
stmt.close();
} catch (SQLException ignore) {
}
}
}
private String getSalt() {
Statement statement = null;
try {
String selectSQL = "SELECT SALT FROM " + metadataTableName;
statement = con.createStatement();
ResultSet rs = statement.executeQuery(selectSQL);
while (rs.next()) {
return rs.getString("SALT");
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
safeClose(statement);
}
return null;
}
private String getMasterPassword() {
try {
String selectSQL = "SELECT PASS FROM " + metadataTableName;
Statement statement = con.createStatement();
ResultSet rs = statement.executeQuery(selectSQL);
while (rs.next()) {
return rs.getString("PASS");
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return null;
}
private void storeCertificateChain(String alias, Certificate[] chain) throws KeyStoreException {
PreparedStatement preparedStatement = null;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(chain);
String encodedChain = Base64.encodeBytes(baos.toByteArray());
String insertTableSQL = "INSERT INTO " + storeTableName + "(ID,CHAIN,CREATED) VALUES" + "(?,?,?)";
String thirdIndex = (new Date()).getTime() + "";
boolean rowExists = checkRowExists(alias);
if (rowExists) {
insertTableSQL = "UPDATE " + storeTableName + " SET ID= ? , CHAIN = ? WHERE ID = ?";
thirdIndex = alias;
}
preparedStatement = con.prepareStatement(insertTableSQL);
preparedStatement.setString(1, alias);
preparedStatement.setString(2, encodedChain);
preparedStatement.setString(3, thirdIndex);
preparedStatement.executeUpdate();
preparedStatement.close();
} catch (Exception e) {
throw new KeyStoreException(e);
} finally {
if (preparedStatement != null) {
safeClose(preparedStatement);
}
}
}
private boolean matchKeyPass(String alias, char[] keypass) throws KeyStoreException {
PreparedStatement preparedStatement = null;
try {
String salt = getSalt();
String selectSQL = "SELECT KEYPASS FROM " + storeTableName + " WHERE ID =?";
preparedStatement = con.prepareStatement(selectSQL);
preparedStatement.setString(1, alias);
ResultSet rs = preparedStatement.executeQuery();
while (rs.next()) {
String storedPass = rs.getString("KEYPASS");
if (storedPass == null)
return false;
return storedPass.equals(KeyStoreDBUtil.saltedHmacMD5(salt, (new String(keypass)).getBytes())); // Atleast one
// entry
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
safeClose(preparedStatement);
}
return false;
}
private void storeKeyPass(String alias, char[] keypass) throws KeyStoreException {
PreparedStatement preparedStatement = null;
try {
String salt = getSalt();
String encodedPass = KeyStoreDBUtil.saltedHmacMD5(salt, (new String(keypass).getBytes()));
String insertTableSQL = "UPDATE " + storeTableName + " SET KEYPASS = ? WHERE ID=?";
preparedStatement = con.prepareStatement(insertTableSQL);
preparedStatement.setString(1, encodedPass);
preparedStatement.setString(2, alias);
preparedStatement.executeUpdate();
preparedStatement.close();
} catch (Exception e) {
throw new KeyStoreException(e);
} finally {
if (preparedStatement != null) {
safeClose(preparedStatement);
}
}
}
private boolean checkRowExists(String alias) throws Exception {
PreparedStatement preparedStatement = null;
try {
String selectSQL = "SELECT ID FROM " + storeTableName + " WHERE ID = ?";
preparedStatement = con.prepareStatement(selectSQL);
preparedStatement.setString(1, alias);
ResultSet rs = preparedStatement.executeQuery();
while (rs.next()) {
return true; // Atleast one entry
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
safeClose(preparedStatement);
}
return false;
}
public static void main(String[] args) throws Exception {
PicketBoxDBKeyStore ks = new PicketBoxDBKeyStore();
ks.loadDBConnection();
Scanner scanner = new Scanner(System.in);
while (true) {
String cmd = "\n\n\nEnter 1: Import KeyPair \n" + "2: Create a KeyPair and Certificate \n" + "3: Create CSR \n"
+ "4: Check Master Password Exists \n" + "5: Check Master Salt Exists \n" + "6: Add Master Salt \n"
+ "7: Add Master Password \n" + "8: Exit";
System.out.println(cmd);
System.out.print("Enter Your Choice:");
int choice = scanner.nextInt();
switch (choice) {
case 1:
String keystoreurl = getNonEmptyString("Enter Keystore URL=");
String keystorePass = getNonEmptyPasswordString("Enter KeyStore Password=");
KeyStore keystore = null;
// Load Keystore
InputStream is = PicketBoxDBKeyStore.class.getClassLoader().getResourceAsStream(keystoreurl);
if (is == null) {
// try URL
try {
URL keyurl = new URL(keystoreurl);
is = keyurl.openStream();
} catch (Exception e) {
// Unable to get to the keystore
throw new RuntimeException("Unable to load keystore:" + keystoreurl);
}
}
if (is != null) {
keystore = KeyStore.getInstance(KeyStore.getDefaultType());
keystore.load(is, keystorePass.toCharArray());
System.out.println("Java JKS KeyStore loaded from " + keystoreurl);
}
String alias = getNonEmptyString("Enter alias=");
String keyPass = getNonEmptyPasswordString("Enter Key Password=");
if (keystore != null) {
KeyHolder holder = getPrivateKey(keystore, alias, keyPass.toCharArray());
System.out.println("Retrieved Private Key and Certificate from JKS Keystore:" + keystoreurl);
ks.engineSetKeyEntry(alias, holder.privateKey, keyPass.toCharArray(), null);
ks.engineSetCertificateEntry(alias, holder.cert);
}
break;
case 2:
generateCertificate(ks);
break;
case 3:
alias = getNonEmptyString("Enter alias=");
keyPass = getNonEmptyPasswordString("Enter Key Password=");
String csrFile = getNonEmptyString("Enter filename to store CSR=");
System.out.println("Storing CSR into " + csrFile);
FileOutputStream fos = new FileOutputStream(csrFile);
generateCSR(ks, alias, keyPass.toCharArray(), fos);
fos.close();
break;
case 4:
System.out.println("Master Password Exists=" + ks.existsMasterPassword());
break;
case 5:
System.out.println("Master SALT Exists=" + ks.existsSalt());
break;
case 6: // Add master password
addMasterSalt(ks);
break;
case 7: // Add master password
addMasterPassword(ks);
break;
default:
System.out.println("Good Bye!");
System.exit(0);
break;
}
}
}
private static void addMasterPassword(PicketBoxDBKeyStore ks) throws Exception {
if (ks.existsSalt() == false) {
System.out.println("WARNING :: Master Salt Does Not Exist. Please contact your DB Administrator");
return;
}
String masterPassword = getNonEmptyPasswordString("Enter Master Password=");
String masterPassword2 = getNonEmptyPasswordString("Enter Master Password Again=");
if (masterPassword.equals(masterPassword2) == false) {
System.out.print("Sorry. Master Password Values Do Not Match");
return;
}
ks.storeMasterPassword(masterPassword.toCharArray());
System.out.println("Master Password stored in DB");
}
private static void addMasterSalt(PicketBoxDBKeyStore ks) throws Exception {
if (ks.existsSalt()) {
System.out.println("WARNING :: Master Salt Already Exists. Please contact your DB Administrator");
return;
}
String masterSalt = getNonEmptyString("Enter Master Salt=");
String masterSalt2 = getNonEmptyString("Enter Master Salt Again=");
if (masterSalt.equals(masterSalt2) == false) {
System.out.print("Sorry. Master Salt Values Do Not Match");
return;
}
ks.storeMasterSalt(masterSalt);
}
private static String getNonEmptyString(String cmd) throws Exception {
String result = "";
do {
System.out.print(cmd);
result = readLine();
} while (result.isEmpty());
return result;
}
private static String getNonEmptyPasswordString(String cmd) throws Exception {
String result = "";
do {
System.out.print(cmd);
result = readPassword();
} while (result.isEmpty());
return result;
}
private static void generateCertificate(PicketBoxDBKeyStore ks) throws Exception {
CertificateUtil util = new CertificateUtil();
String alias = "";
do {
System.out.print("Enter alias=");
alias = readLine();
} while (alias.isEmpty());
String dn = "";
do {
System.out.print("Enter Subject DN=");
dn = readLine();
} while (dn.isEmpty());
String no = "";
do {
System.out.print("Enter Number Of Days Of Validity=");
no = readLine();
} while (no.isEmpty());
int numberOfDays = Integer.parseInt(no);
String keyPass = "";
do {
System.out.print("Enter Key Password=");
keyPass = readPassword();
} while (keyPass.isEmpty());
KeyPair pair = util.generateKeyPair("RSA");
Certificate cert = util.createX509V1Certificate(pair, numberOfDays, dn);
if (ks != null) {
ks.engineSetKeyEntry(alias, pair.getPrivate(), keyPass.toCharArray(), null);
ks.engineSetCertificateEntry(alias, cert);
}
System.out.println("KeyPair/Certificate created and stored as alias=" + alias);
}
private static void generateCSR(PicketBoxDBKeyStore ks, String alias, char[] keyPass, FileOutputStream fos)
throws Exception {
CertificateUtil util = new CertificateUtil();
Certificate cert = ks.engineGetCertificate(alias);
PrivateKey privateKey = (PrivateKey) ks.engineGetKey(alias, keyPass);
KeyPair keyPair = new KeyPair(cert.getPublicKey(), privateKey);
X509Certificate x509 = (X509Certificate) cert;
byte[] csr = util.createCSR(x509.getSubjectDN().getName(), keyPair);
String pem = util.getPEM(csr);
fos.write(pem.getBytes());
System.out.println("CSR stored");
}
private static KeyHolder getPrivateKey(KeyStore keystore, String alias, char[] password) {
KeyHolder holder = new KeyHolder();
try {
// Get private key
Key key = keystore.getKey(alias, password);
if (key instanceof PrivateKey) {
holder.privateKey = key;
// Get certificate of public key
holder.cert = keystore.getCertificate(alias);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return holder;
}
private static class KeyHolder {
private Key privateKey;
private Certificate cert;
}
private static String readLine() throws IOException {
if (System.console() != null) {
return System.console().readLine();
}
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
return reader.readLine();
}
private static String readPassword() throws IOException {
if (System.console() != null)
return new String(System.console().readPassword());
return readLine();
}
}