MEKeyToolpublic class MEKeyTool extends Object Manages the initial public keystore needed to bootstrap this MIDP
security implementation. It provides both a Java and a command line interface.
The anchor of trust on an ME (mobile equipment) are the public keys
loaded on it by the manufacturer, in MIDP implementation this is known
as the ME keystore. This tool does for the MIDP implementation
what the manufacturer must do for the ME so that trusted MIDP
applications can be authenticated. |
Fields Summary |
---|
private static final String | defaultAppDirdefault MIDP application directory, see Utility.c getStorageRoot() | private static final String | defaultKeystoreFilenamedefault ME keystore filename, see com.sun.midp.Main.java | private static final String[] | AttrLabelMaps byte codes that follow id-at (0x55 0x04) to corresponding name
component tags (e.g. Common Name, or CN, is 0x55, 0x04, 0x03 and
Country, or C, is 0x55, 0x04, 0x06). See getName. See X.520 for
the OIDs and RFC 1779 for the printable labels. Place holders for
unknown labels have a -1 as the first byte. | private static final String | EMAIL_ATTR_LABELEmail attribute label. | private static final byte[] | EMAIL_ATTR_OIDEmail attribute object identifier. | private PublicKeyStoreBuilderBase | keystoreread-writable ME keystore that does not depend on SSL | private int | nextKeyToGetthe state for getFirstKey and getNextKey |
Constructors Summary |
---|
public MEKeyTool()Constructs a MEKeyTool with an empty keystore.
keystore = new PublicKeyStoreBuilderBase();
| public MEKeyTool(String meKeystoreFilename)Constructs a MEKeyTool and loads its keystore using a filename.
FileInputStream input;
input = new FileInputStream(new File(meKeystoreFilename));
try {
keystore = new PublicKeyStoreBuilderBase(input);
} finally {
input.close();
}
| public MEKeyTool(File meKeystoreFile)Constructs a MEKeyTool and loads its keystore from a file.
FileInputStream input;
input = new FileInputStream(meKeystoreFile);
try {
keystore = new PublicKeyStoreBuilderBase(input);
} finally {
input.close();
}
| public MEKeyTool(InputStream meKeystoreStream)Constructs a MEKeyTool and loads its keystore from a stream.
keystore = new PublicKeyStoreBuilderBase(meKeystoreStream);
|
Methods Summary |
---|
private static void | deleteCommand(java.io.File meKeystoreFile, java.lang.String[] args)Process the command line arguments for the delete command and
then delete a public key from a ME keystore.
This method assumes the first argument is the delete command
and skips it.
String owner = null;
int keyNumber = -1;
boolean keyNumberGiven = false;
MEKeyTool keyTool;
for (int i = 1; i < args.length; i++) {
try {
if (args[i].equals("-MEkeystore")) {
i++;
meKeystoreFile = new File(args[i]);
} else if (args[i].equals("-owner")) {
i++;
owner = args[i];
} else if (args[i].equals("-number")) {
keyNumberGiven = true;
i++;
try {
keyNumber = Integer.parseInt(args[i]);
} catch (NumberFormatException e) {
throw new UsageException(
"Invalid number for the -number argument: " +
args[i]);
}
} else {
throw new UsageException(
"Invalid argument for the delete command: " + args[i]);
}
} catch (ArrayIndexOutOfBoundsException e) {
throw new UsageException("Missing value for " + args[--i]);
}
}
if (owner == null && !keyNumberGiven) {
throw new UsageException(
"Neither key -owner or -number was not given");
}
if (owner != null && keyNumberGiven) {
throw new UsageException("-owner and -number cannot be used " +
"together");
}
keyTool = new MEKeyTool(meKeystoreFile);
if (owner != null) {
if (!keyTool.deleteKey(owner)) {
throw new UsageException("Key not found for: " + owner);
}
} else {
try {
keyTool.deleteKey(keyNumber - 1);
} catch (ArrayIndexOutOfBoundsException e) {
throw new UsageException("Invalid number for the -number " +
"delete option: " + keyNumber);
}
}
keyTool.saveKeystore(meKeystoreFile);
| public boolean | deleteKey(java.lang.String owner)Deletes the first public key matching the owner's distinguished name.
PublicKeyInfo key;
for (int i = 0; i < keystore.numberOfKeys(); i++) {
key = keystore.getKey(i);
if (key.getOwner().equals(owner)) {
keystore.deleteKey(i);
return true;
}
}
return false;
| public void | deleteKey(int number)Deletes a key by key number, 0 being the first public key.
keystore.deleteKey(number);
| private static void | displayUsage()Display the usage text to standard output.
System.out.println("\n MEKeyTool argument combinations:\n\n" +
" -help\n" +
" -import [-MEkeystore <filename>] " +
"[-keystore <filename>]\n" +
" [-storepass <password>] -alias <key alias> " +
"[-domain <domain>]\n" +
" -list [-MEkeystore <filename>]\n" +
" -delete [-MEkeystore <filename>]\n" +
" (-owner <owner name> | -number <key number>)\n" +
"\n" +
" The default for -MEkeystore is \"appdb/_main.ks\".\n" +
" The default for -keystore is \"$HOME/.keystore\".\n");
| public static java.lang.String | formatKeyInfo(PublicKeyInfo keyInfo)Creates a string representation of a key that is displayed to a
user during a list command. The string does not include the modulus
and exponent.
return " Owner: " + keyInfo.getOwner() +
"\n Valid from " +
(new Date(keyInfo.getNotBefore())).toString() +
" to " + (new Date(keyInfo.getNotAfter())).toString() +
"\n Security Domain: " + keyInfo.getDomain() +
"\n Enabled: " + keyInfo.isEnabled();
| protected PublicKeyInfo | getFirstKey()Gets the first key in the keystore.
nextKeyToGet = 0;
return getNextKey();
| public PublicKeyStoreBuilderBase | getKeystore()Gets the read-write keystore this tool is manipulating.
For advanced users.
return keystore;
| protected PublicKeyInfo | getNextKey()Gets the next key after the previous one returned by
{@link #getFirstKey} or this method. If getFirstKey is not called
before the first call to this method, null will be returned.
PublicKeyInfo key;
try {
key = keystore.getKey(nextKeyToGet);
} catch (ArrayIndexOutOfBoundsException e) {
return null;
}
nextKeyToGet++;
return key;
| private static void | importCommand(java.io.File meKeystoreFile, java.lang.String[] args)Process the command line arguments for the import command and
then imports a public key from a JCA keystore to ME keystore.
This method assumes the first argument is the import command
and skips it.
String jcaKeystoreFilename = null;
String keystorePassword = null;
String alias = null;
String domain = "identified";
MEKeyTool keyTool;
for (int i = 1; i < args.length; i++) {
try {
if (args[i].equals("-MEkeystore")) {
i++;
meKeystoreFile = new File(args[i]);
} else if (args[i].equals("-keystore")) {
i++;
jcaKeystoreFilename = args[i];
} else if (args[i].equals("-storepass")) {
i++;
keystorePassword = args[i];
} else if (args[i].equals("-alias")) {
i++;
alias = args[i];
} else if (args[i].equals("-domain")) {
i++;
domain = args[i];
} else {
throw new UsageException(
"Invalid argument for import command: " + args[i]);
}
} catch (ArrayIndexOutOfBoundsException e) {
throw new UsageException("Missing value for " + args[--i]);
}
}
if (jcaKeystoreFilename == null) {
jcaKeystoreFilename = System.getProperty("user.home") +
File.separator + ".keystore";
}
if (alias == null) {
throw new Exception("J2SE key alias was not given");
}
try {
keyTool = new MEKeyTool(meKeystoreFile);
} catch (FileNotFoundException fnfe) {
keyTool = new MEKeyTool();
}
keyTool.importKeyFromJcaKeystore(jcaKeystoreFilename,
keystorePassword,
alias, domain);
keyTool.saveKeystore(meKeystoreFile);
| public void | importKeyFromJcaKeystore(java.lang.String jcakeystoreFilename, java.lang.String keystorePassword, java.lang.String alias, java.lang.String domain)Copies a key from a Standard Edition keystore into the ME keystore.
FileInputStream keystoreStream;
KeyStore jcaKeystore;
// Load the keystore
keystoreStream = new FileInputStream(new File(jcakeystoreFilename));
try {
jcaKeystore = KeyStore.getInstance(KeyStore.getDefaultType());
if (keystorePassword == null) {
jcaKeystore.load(keystoreStream, null);
} else {
jcaKeystore.load(keystoreStream,
keystorePassword.toCharArray());
}
} finally {
keystoreStream.close();
}
importKeyFromJcaKeystore(jcaKeystore,
alias,
domain);
| public void | importKeyFromJcaKeystore(java.security.KeyStore jcaKeystore, java.lang.String alias, java.lang.String domain)Copies a key from a Standard Edition keystore into the ME keystore.
X509Certificate cert;
byte[] der;
TLV tbsCert;
TLV subjectName;
RSAPublicKey rsaKey;
String owner;
long notBefore;
long notAfter;
byte[] rawModulus;
int i;
int keyLen;
byte[] modulus;
byte[] exponent;
Vector keys;
// get the cert from the keystore
try {
cert = (X509Certificate)jcaKeystore.getCertificate(alias);
} catch (ClassCastException cce) {
throw new CertificateException("Certificate not X.509 type");
}
if (cert == null) {
throw new CertificateException("Certificate not found");
}
/*
* J2SE reorders the attributes when building a printable name
* so we must build a printable name on our own.
*/
/*
* TBSCertificate ::= SEQUENCE {
* version [0] EXPLICIT Version DEFAULT v1,
* serialNumber CertificateSerialNumber,
* signature AlgorithmIdentifier,
* issuer Name,
* validity Validity,
* subject Name,
* subjectPublicKeyInfo SubjectPublicKeyInfo,
* issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
* -- If present, version shall be v2 or v3
* subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
* -- If present, version shall be v2 or v3
* extensions [3] EXPLICIT Extensions OPTIONAL
* -- If present, version shall be v3
* }
*/
der = cert.getTBSCertificate();
tbsCert = new TLV(der, 0);
// walk down the tree of TLVs to find the subject name
try {
// Top level a is Sequence, drop down to the first child
subjectName = tbsCert.child;
// skip the version if present.
if (subjectName.type == TLV.VERSION_TYPE) {
subjectName = subjectName.next;
}
// skip the serial number
subjectName = subjectName.next;
// skip the signature alg. id.
subjectName = subjectName.next;
// skip the issuer
subjectName = subjectName.next;
// skip the validity
subjectName = subjectName.next;
owner = parseDN(der, subjectName);
} catch (NullPointerException e) {
throw new CertificateException("TBSCertificate corrupt 1");
} catch (IndexOutOfBoundsException e) {
throw new CertificateException("TBSCertificate corrupt 2");
}
notBefore = cert.getNotBefore().getTime();
notAfter = cert.getNotAfter().getTime();
// get the key from the cert
try {
rsaKey = (RSAPublicKey)cert.getPublicKey();
} catch (ClassCastException cce) {
throw new RuntimeException("Key in certificate is not an RSA key");
}
// get the key parameters from the key
rawModulus = rsaKey.getModulus().toByteArray();
/*
* the modulus is given as the minimum positive integer,
* will not padded to the bit size of the key, or may have a extra
* pad to make it positive. SSL expects the key to be signature
* bit size. but we cannot get that from the key, so we should
* remove any zero pad bytes and then pad out to a multiple of 8 bytes
*/
for (i = 0; i < rawModulus.length && rawModulus[i] == 0; i++);
keyLen = rawModulus.length - i;
keyLen = (keyLen + 7) / 8 * 8;
modulus = new byte[keyLen];
int k, j;
for (k = rawModulus.length - 1, j = keyLen - 1;
k >= 0 && j >= 0; k--, j--) {
modulus[j] = rawModulus[k];
}
exponent = rsaKey.getPublicExponent().toByteArray();
// add the key
keys = keystore.findKeys(owner);
if (keys != null) {
boolean duplicateKey = false;
for (int n = 0; !duplicateKey && n < keys.size(); n++) {
PublicKeyInfo key = (PublicKeyInfo)keys.elementAt(n);
if (key.getOwner().equals(owner)) {
byte[] temp = key.getModulus();
if (modulus.length == temp.length) {
duplicateKey = true;
for (int m = 0; j < modulus.length && m < temp.length;
m++) {
if (modulus[m] != temp[m]) {
duplicateKey = false;
break;
}
}
}
}
}
if (duplicateKey) {
throw new CertificateException(
"Owner already has this key in the ME keystore");
}
}
keystore.addKey(new PublicKeyInfo(owner, notBefore, notAfter,
modulus, exponent, domain));
| private static void | listCommand(java.io.File meKeystoreFile, java.lang.String[] args)Process the command line arguments for the list command and
then list the public keys of a ME keystore.
This method assumes the first argument is the list command
and skips it.
MEKeyTool keyTool;
PublicKeyInfo key;
for (int i = 1; i < args.length; i++) {
try {
if (args[i].equals("-MEkeystore")) {
i++;
meKeystoreFile = new File(args[i]);
} else {
throw new UsageException("Invalid argument for the list " +
"command: " + args[i]);
}
} catch (ArrayIndexOutOfBoundsException e) {
throw new UsageException("Missing value for " + args[--i]);
}
}
keyTool = new MEKeyTool(meKeystoreFile);
key = keyTool.getFirstKey();
for (int i = 1; key != null; i++) {
System.out.println("Key " + Integer.toString(i));
System.out.println(formatKeyInfo(key));
key = keyTool.getNextKey();
}
System.out.println("");
| public static void | main(java.lang.String[] args)Performs the command specified in the first argument.
Exits with a 0 status if the command was successful.
Exits and prints out an error message with a -1 status if the command
failed.
MEKeyTool supports the following commands:
no args - same has -help
-import - import a public key from a JCE keystore
into a ME keystore
-delete - delete a key from a ME keystore
-help - print a usage summary
-list - list the owner and validity period of each
key in a ME keystore
Parameters for (commands):
-MEkeystore <filename of the ME keystore> (optional for all)
-keystore <filename of the JCA keystore> (optional import)
-storepass <password for the JCA keystore> (optional import)
-alias <short string ID of a key in a JCA keystore> (import)
-domain <security domain of the ME key> (optional import)
-owner <name of the owner of a ME key> (delete)
-number <key number starting a 1 of a ME key> (delete)
Defaults:
-MEkeystore appdir/main.ks
-keystore <user's home dir>/.keystore
-domain untrusted
File meKeystoreFile = null;
if (args.length == 0) {
System.out.println("\n Error: No command given");
displayUsage();
System.exit(-1);
}
if (args[0].equals("-help")) {
// user just needs help with the arguments
displayUsage();
System.exit(0);
}
// start with the default keystore file
meKeystoreFile = new File(defaultAppDir, defaultKeystoreFilename);
try {
if (args[0].equals("-import")) {
importCommand(meKeystoreFile, args);
System.exit(0);
}
if (args[0].equals("-delete")) {
deleteCommand(meKeystoreFile, args);
System.exit(0);
}
if (args[0].equals("-list")) {
listCommand(meKeystoreFile, args);
System.exit(0);
}
throw new UsageException(" Invalid command: " + args[0]);
} catch (Exception e) {
System.out.println("\n Error: " + e.getMessage());
if (e instanceof UsageException) {
displayUsage();
}
System.exit(-1);
}
| private java.lang.String | parseDN(byte[] buffer, com.sun.midp.mekeytool.TLV dn)Parses a DER TLV tree into a printable distinguished name.
TLV attribute;
TLV type;
TLV value;
StringBuffer name = new StringBuffer(256);
/*
* Name ::= CHOICE { RDNSequence } # CHOICE does not encoded
*
* RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
*
* RelativeDistinguishedName ::= SET OF AttributeTypeAndValue
*
* AttributeTypeAndValue ::= SEQUENCE {
* type AttributeType,
* value AttributeValue }
*
* AttributeType ::= OBJECT IDENTIFIER
*
* AttributeValue ::= ANY DEFINED BY AttributeType
*
* basically this means that each attribute value is 3 levels down
*/
// sequence drop down a level
attribute = dn.child;
while (attribute != null) {
if (attribute != dn.child) {
name.append(";");
}
/*
* we do not handle relative distinguished names yet
* which should not be used by CAs anyway
* so only take the first element of the sequence
*/
type = attribute.child.child;
/*
* At this point we tag the name component, e.g. C= or hex
* if unknown.
*/
if ((type.length == 3) && (buffer[type.valueOffset] == 0x55) &&
(buffer[type.valueOffset + 1] == 0x04)) {
// begins with id-at, so try to see if we have a label
int temp = buffer[type.valueOffset + 2] & 0xFF;
if ((temp < AttrLabel.length) &&
(AttrLabel[temp] != null)) {
name.append(AttrLabel[temp]);
} else {
name.append(TLV.hexEncode(buffer, type.valueOffset,
type.length, -1));
}
} else if (TLV.byteMatch(buffer, type.valueOffset,
type.length, EMAIL_ATTR_OID,
0, EMAIL_ATTR_OID.length)) {
name.append(EMAIL_ATTR_LABEL);
} else {
name.append(TLV.hexEncode(buffer, type.valueOffset,
type.length, -1));
}
name.append("=");
value = attribute.child.child.next;
if (value.type == TLV.PRINTSTR_TYPE ||
value.type == TLV.TELETEXSTR_TYPE ||
value.type == TLV.UTF8STR_TYPE ||
value.type == TLV.IA5STR_TYPE ||
value.type == TLV.UNIVSTR_TYPE) {
try {
name.append(new String(buffer, value.valueOffset,
value.length, "UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e.toString());
}
} else {
name.append(TLV.hexEncode(buffer, value.valueOffset,
value.length, -1));
}
attribute = attribute.next;
}
return name.toString();
| public void | saveKeystore(java.io.File meKeystoreFile)Saves the keystore to a file.
FileOutputStream output;
output = new FileOutputStream(meKeystoreFile);
keystore.serialize(output);
output.close();
|
|