summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeremy Condra <gcondra@google.com>2014-06-11 13:38:45 -0700
committerGeremy Condra <gcondra@google.com>2014-07-08 21:36:25 -0700
commitcee5bfdf119104b8ebce56d54dfcdcca1f537075 (patch)
tree5b7cb9e9f885f7c5a197dca184fc5d473d03e901
parenta2b7dbef923dbc1652fbb71969416cdd7adb40df (diff)
downloadextras-cee5bfdf119104b8ebce56d54dfcdcca1f537075.tar.gz
verity: Add tools to help OEMs generate signed boot images.
Change-Id: Iea200def2fdd8a0d366888bb7b1ae401297063f1
-rw-r--r--verity/Android.mk31
-rw-r--r--verity/BootSignature.java124
-rw-r--r--verity/BootSignature.mf1
-rw-r--r--verity/KeystoreSigner.java137
-rw-r--r--verity/KeystoreSigner.mf1
-rw-r--r--verity/Utils.java156
-rw-r--r--verity/VeritySigner.java55
-rwxr-xr-xverity/boot_signer8
-rw-r--r--verity/keystore_signer8
9 files changed, 468 insertions, 53 deletions
diff --git a/verity/Android.mk b/verity/Android.mk
index ba986262..5d37f0c7 100644
--- a/verity/Android.mk
+++ b/verity/Android.mk
@@ -10,7 +10,7 @@ LOCAL_C_INCLUDES += external/openssl/include
include $(BUILD_HOST_EXECUTABLE)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := VeritySigner.java
+LOCAL_SRC_FILES := VeritySigner.java Utils.java
LOCAL_MODULE := VeritySigner
LOCAL_JAR_MANIFEST := VeritySigner.mf
LOCAL_MODULE_TAGS := optional
@@ -18,6 +18,22 @@ LOCAL_STATIC_JAVA_LIBRARIES := bouncycastle-host
include $(BUILD_HOST_JAVA_LIBRARY)
include $(CLEAR_VARS)
+LOCAL_SRC_FILES := BootSignature.java VeritySigner.java Utils.java
+LOCAL_MODULE := BootSignature
+LOCAL_JAR_MANIFEST := BootSignature.mf
+LOCAL_MODULE_TAGS := optional
+LOCAL_STATIC_JAVA_LIBRARIES := bouncycastle-host
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := BootSignature.java KeystoreSigner.java Utils.java
+LOCAL_MODULE := BootKeystoreSigner
+LOCAL_JAR_MANIFEST := KeystoreSigner.mf
+LOCAL_MODULE_TAGS := optional
+LOCAL_STATIC_JAVA_LIBRARIES := bouncycastle-host
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+include $(CLEAR_VARS)
LOCAL_SRC_FILES := verity_signer
LOCAL_MODULE := verity_signer
LOCAL_MODULE_CLASS := EXECUTABLES
@@ -27,12 +43,21 @@ LOCAL_REQUIRED_MODULES := VeritySigner
include $(BUILD_PREBUILT)
include $(CLEAR_VARS)
-LOCAL_MODULE := build_verity_tree.py
+LOCAL_SRC_FILES := boot_signer
+LOCAL_MODULE := boot_signer
LOCAL_MODULE_CLASS := EXECUTABLES
-LOCAL_SRC_FILES := build_verity_tree.py
+LOCAL_IS_HOST_MODULE := true
+LOCAL_MODULE_TAGS := optional
+LOCAL_REQUIRED_MODULES := BootSigner
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := keystore_signer
+LOCAL_MODULE := keystore_signer
LOCAL_MODULE_CLASS := EXECUTABLES
LOCAL_IS_HOST_MODULE := true
LOCAL_MODULE_TAGS := optional
+LOCAL_REQUIRED_MODULES := KeystoreSigner
include $(BUILD_PREBUILT)
include $(CLEAR_VARS)
diff --git a/verity/BootSignature.java b/verity/BootSignature.java
new file mode 100644
index 00000000..f5ceb304
--- /dev/null
+++ b/verity/BootSignature.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.verity;
+
+import java.io.IOException;
+import java.security.PrivateKey;
+import java.util.Arrays;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERPrintableString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.util.ASN1Dump;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+/**
+ * AndroidVerifiedBootSignature DEFINITIONS ::=
+ * BEGIN
+ * FormatVersion ::= INTEGER
+ * AlgorithmIdentifier ::= SEQUENCE {
+ * algorithm OBJECT IDENTIFIER,
+ * parameters ANY DEFINED BY algorithm OPTIONAL
+ * }
+ * AuthenticatedAttributes ::= SEQUENCE {
+ * target CHARACTER STRING,
+ * length INTEGER
+ * }
+ * Signature ::= OCTET STRING
+ * END
+ */
+
+public class BootSignature extends ASN1Object
+{
+ private ASN1Integer formatVersion;
+ private AlgorithmIdentifier algorithmIdentifier;
+ private DERPrintableString target;
+ private ASN1Integer length;
+ private DEROctetString signature;
+
+ public BootSignature(String target, int length) {
+ this.formatVersion = new ASN1Integer(0);
+ this.target = new DERPrintableString(target);
+ this.length = new ASN1Integer(length);
+ this.algorithmIdentifier = new AlgorithmIdentifier(
+ PKCSObjectIdentifiers.sha256WithRSAEncryption);
+ }
+
+ public ASN1Object getAuthenticatedAttributes() {
+ ASN1EncodableVector attrs = new ASN1EncodableVector();
+ attrs.add(target);
+ attrs.add(length);
+ return new DERSequence(attrs);
+ }
+
+ public byte[] getEncodedAuthenticatedAttributes() throws IOException {
+ return getAuthenticatedAttributes().getEncoded();
+ }
+
+ public void setSignature(byte[] sig) {
+ signature = new DEROctetString(sig);
+ }
+
+ public byte[] generateSignableImage(byte[] image) throws IOException {
+ byte[] attrs = getEncodedAuthenticatedAttributes();
+ byte[] signable = Arrays.copyOf(image, image.length + attrs.length);
+ for (int i=0; i < attrs.length; i++) {
+ signable[i+image.length] = attrs[i];
+ }
+ return signable;
+ }
+
+ public byte[] sign(byte[] image, PrivateKey key) throws Exception {
+ byte[] signable = generateSignableImage(image);
+ byte[] signature = Utils.sign(key, signable);
+ byte[] signed = Arrays.copyOf(image, image.length + signature.length);
+ for (int i=0; i < signature.length; i++) {
+ signed[i+image.length] = signature[i];
+ }
+ return signed;
+ }
+
+ public ASN1Primitive toASN1Primitive() {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ v.add(formatVersion);
+ v.add(algorithmIdentifier);
+ v.add(getAuthenticatedAttributes());
+ v.add(signature);
+ return new DERSequence(v);
+ }
+
+ public static void doSignature( String target,
+ String imagePath,
+ String keyPath,
+ String outPath) throws Exception {
+ byte[] image = Utils.read(imagePath);
+ BootSignature bootsig = new BootSignature(target, image.length);
+ PrivateKey key = Utils.loadPEMPrivateKeyFromFile(keyPath);
+ byte[] signature = bootsig.sign(image, key);
+ Utils.write(signature, outPath);
+ }
+
+ // java -cp ../../../out/host/common/obj/JAVA_LIBRARIES/AndroidVerifiedBootSigner_intermediates/classes/ com.android.verity.AndroidVerifiedBootSigner boot ../../../out/target/product/flounder/boot.img ../../../build/target/product/security/verity_private_dev_key /tmp/boot.img.signed
+ public static void main(String[] args) throws Exception {
+ doSignature(args[0], args[1], args[2], args[3]);
+ }
+} \ No newline at end of file
diff --git a/verity/BootSignature.mf b/verity/BootSignature.mf
new file mode 100644
index 00000000..c1868b6c
--- /dev/null
+++ b/verity/BootSignature.mf
@@ -0,0 +1 @@
+Main-Class: com.android.verity.BootSignature
diff --git a/verity/KeystoreSigner.java b/verity/KeystoreSigner.java
new file mode 100644
index 00000000..d57f328f
--- /dev/null
+++ b/verity/KeystoreSigner.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.verity;
+
+import java.io.IOException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.Signature;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERPrintableString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.RSAPublicKey;
+import org.bouncycastle.asn1.util.ASN1Dump;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+/**
+ * AndroidVerifiedBootKeystore DEFINITIONS ::=
+ * BEGIN
+ * FormatVersion ::= INTEGER
+ * KeyBag ::= SEQUENCE {
+ * Key ::= SEQUENCE {
+ * AlgorithmIdentifier ::= SEQUENCE {
+ * algorithm OBJECT IDENTIFIER,
+ * parameters ANY DEFINED BY algorithm OPTIONAL
+ * }
+ * KeyMaterial ::= RSAPublicKey
+ * }
+ * }
+ * Signature ::= AndroidVerifiedBootSignature
+ * END
+ */
+
+class BootKey extends ASN1Object
+{
+ private AlgorithmIdentifier algorithmIdentifier;
+ private RSAPublicKey keyMaterial;
+
+ public BootKey(PublicKey key) throws Exception {
+ java.security.interfaces.RSAPublicKey k =
+ (java.security.interfaces.RSAPublicKey) key;
+ this.keyMaterial = new RSAPublicKey(
+ k.getModulus(),
+ k.getPublicExponent());
+ this.algorithmIdentifier = new AlgorithmIdentifier(
+ PKCSObjectIdentifiers.sha256WithRSAEncryption);
+ }
+
+ public ASN1Primitive toASN1Primitive() {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ v.add(algorithmIdentifier);
+ v.add(keyMaterial);
+ return new DERSequence(v);
+ }
+
+ public void dump() throws Exception {
+ System.out.println(ASN1Dump.dumpAsString(toASN1Primitive()));
+ }
+}
+
+class BootKeystore extends ASN1Object
+{
+ private ASN1Integer formatVersion;
+ private ASN1EncodableVector keyBag;
+ private BootSignature signature;
+
+ public BootKeystore() {
+ this.formatVersion = new ASN1Integer(0);
+ this.keyBag = new ASN1EncodableVector();
+ }
+
+ public void addPublicKey(byte[] der) throws Exception {
+ PublicKey pubkey = Utils.loadDERPublicKey(der);
+ BootKey k = new BootKey(pubkey);
+ keyBag.add(k);
+ }
+
+ public byte[] getInnerKeystore() throws Exception {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ v.add(formatVersion);
+ v.add(new DERSequence(keyBag));
+ return new DERSequence(v).getEncoded();
+ }
+
+ public ASN1Primitive toASN1Primitive() {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ v.add(formatVersion);
+ v.add(new DERSequence(keyBag));
+ v.add(signature);
+ return new DERSequence(v);
+ }
+
+ public void sign(PrivateKey privateKey) throws Exception {
+ byte[] innerKeystore = getInnerKeystore();
+ byte[] rawSignature = Utils.sign(privateKey, innerKeystore);
+ signature = new BootSignature("keystore", innerKeystore.length);
+ signature.setSignature(rawSignature);
+ }
+
+ public void dump() throws Exception {
+ System.out.println(ASN1Dump.dumpAsString(toASN1Primitive()));
+ }
+
+ // USAGE:
+ // AndroidVerifiedBootKeystoreSigner <privkeyFile> <outfile> <pubkeyFile0> ... <pubkeyFileN-1>
+ // EG:
+ // java -cp ../../../out/host/common/obj/JAVA_LIBRARIES/AndroidVerifiedBootKeystoreSigner_intermediates/classes/ com.android.verity.AndroidVerifiedBootKeystoreSigner ../../../build/target/product/security/verity_private_dev_key /tmp/keystore.out /tmp/k
+ public static void main(String[] args) throws Exception {
+ String privkeyFname = args[0];
+ String outfileFname = args[1];
+ BootKeystore ks = new BootKeystore();
+ for (int i=2; i < args.length; i++) {
+ ks.addPublicKey(Utils.read(args[i]));
+ }
+ ks.sign(Utils.loadPEMPrivateKeyFromFile(privkeyFname));
+ Utils.write(ks.getEncoded(), outfileFname);
+ }
+} \ No newline at end of file
diff --git a/verity/KeystoreSigner.mf b/verity/KeystoreSigner.mf
new file mode 100644
index 00000000..a4fee27f
--- /dev/null
+++ b/verity/KeystoreSigner.mf
@@ -0,0 +1 @@
+Main-Class: com.android.verity.KeystoreSigner \ No newline at end of file
diff --git a/verity/Utils.java b/verity/Utils.java
new file mode 100644
index 00000000..2c1e7bb4
--- /dev/null
+++ b/verity/Utils.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.verity;
+
+import java.lang.reflect.Constructor;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.KeyFactory;
+import java.security.Provider;
+import java.security.Security;
+import java.security.Signature;
+import java.security.spec.X509EncodedKeySpec;
+import java.security.spec.PKCS8EncodedKeySpec;
+
+import org.bouncycastle.util.encoders.Base64;
+
+public class Utils {
+
+ private static void loadProviderIfNecessary(String providerClassName) {
+ if (providerClassName == null) {
+ return;
+ }
+
+ final Class<?> klass;
+ try {
+ final ClassLoader sysLoader = ClassLoader.getSystemClassLoader();
+ if (sysLoader != null) {
+ klass = sysLoader.loadClass(providerClassName);
+ } else {
+ klass = Class.forName(providerClassName);
+ }
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ System.exit(1);
+ return;
+ }
+
+ Constructor<?> constructor = null;
+ for (Constructor<?> c : klass.getConstructors()) {
+ if (c.getParameterTypes().length == 0) {
+ constructor = c;
+ break;
+ }
+ }
+ if (constructor == null) {
+ System.err.println("No zero-arg constructor found for " + providerClassName);
+ System.exit(1);
+ return;
+ }
+
+ final Object o;
+ try {
+ o = constructor.newInstance();
+ } catch (Exception e) {
+ e.printStackTrace();
+ System.exit(1);
+ return;
+ }
+ if (!(o instanceof Provider)) {
+ System.err.println("Not a Provider class: " + providerClassName);
+ System.exit(1);
+ }
+
+ Security.insertProviderAt((Provider) o, 1);
+ }
+
+ static byte[] pemToDer(String pem) throws Exception {
+ pem = pem.replaceAll("^-.*", "");
+ String base64_der = pem.replaceAll("-.*$", "");
+ return Base64.decode(base64_der);
+ }
+
+ static PrivateKey loadDERPrivateKey(byte[] der) throws Exception {
+ PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(der);
+ KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+ return (PrivateKey) keyFactory.generatePrivate(keySpec);
+ }
+
+ static PrivateKey loadPEMPrivateKey(byte[] pem) throws Exception {
+ byte[] der = pemToDer(new String(pem));
+ return loadDERPrivateKey(der);
+ }
+
+ static PrivateKey loadPEMPrivateKeyFromFile(String keyFname) throws Exception {
+ return loadPEMPrivateKey(read(keyFname));
+ }
+
+ static PrivateKey loadDERPrivateKeyFromFile(String keyFname) throws Exception {
+ return loadDERPrivateKey(read(keyFname));
+ }
+
+ static PublicKey loadDERPublicKey(byte[] der) throws Exception {
+ X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(der);
+ KeyFactory factory = KeyFactory.getInstance("RSA");
+ return factory.generatePublic(publicKeySpec);
+ }
+
+ static PublicKey loadPEMPublicKey(byte[] pem) throws Exception {
+ byte[] der = pemToDer(new String(pem));
+ X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(der);
+ KeyFactory factory = KeyFactory.getInstance("RSA");
+ return factory.generatePublic(publicKeySpec);
+ }
+
+ static PublicKey loadPEMPublicKeyFromFile(String keyFname) throws Exception {
+ return loadPEMPublicKey(read(keyFname));
+ }
+
+ static PublicKey loadDERPublicKeyFromFile(String keyFname) throws Exception {
+ return loadDERPublicKey(read(keyFname));
+ }
+
+ static byte[] sign(PrivateKey privateKey, byte[] input) throws Exception {
+ Signature signer = Signature.getInstance("SHA1withRSA");
+ signer.initSign(privateKey);
+ signer.update(input);
+ return signer.sign();
+ }
+
+ static byte[] read(String fname) throws Exception {
+ long offset = 0;
+ File f = new File(fname);
+ long length = f.length();
+ byte[] image = new byte[(int)length];
+ FileInputStream fis = new FileInputStream(f);
+ while (offset < length) {
+ offset += fis.read(image, (int)offset, (int)(length - offset));
+ }
+ fis.close();
+ return image;
+ }
+
+ static void write(byte[] data, String fname) throws Exception{
+ FileOutputStream out = new FileOutputStream(fname);
+ out.write(data);
+ out.close();
+ }
+} \ No newline at end of file
diff --git a/verity/VeritySigner.java b/verity/VeritySigner.java
index 2ab94cb4..44c56028 100644
--- a/verity/VeritySigner.java
+++ b/verity/VeritySigner.java
@@ -16,63 +16,18 @@
package com.android.verity;
-import org.bouncycastle.util.encoders.Base64;
-
-import java.io.DataInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.InputStream;
-import java.security.KeyFactory;
import java.security.PrivateKey;
-import java.security.Signature;
-import java.security.spec.PKCS8EncodedKeySpec;
-
-class VeritySigner {
-
- private static byte[] sign(PrivateKey privateKey, byte[] input) throws Exception {
- Signature signer = Signature.getInstance("SHA1withRSA");
- signer.initSign(privateKey);
- signer.update(input);
- return signer.sign();
- }
-
- private static PKCS8EncodedKeySpec pemToDer(String pem) throws Exception {
- pem = pem.replaceAll("^-.*", "");
- String base64_der = pem.replaceAll("-.*$", "");
- byte[] der = Base64.decode(base64_der);
- return new PKCS8EncodedKeySpec(der);
- }
- private static PrivateKey loadPrivateKey(String pem) throws Exception {
- PKCS8EncodedKeySpec keySpec = pemToDer(pem);
- KeyFactory keyFactory = KeyFactory.getInstance("RSA");
- return (PrivateKey) keyFactory.generatePrivate(keySpec);
- }
-
- private static byte[] read(String path) throws Exception {
- File contentFile = new File(path);
- byte[] content = new byte[(int)contentFile.length()];
- FileInputStream fis = new FileInputStream(contentFile);
- fis.read(content);
- fis.close();
- return content;
- }
-
- private static void writeOutput(String path, byte[] output) throws Exception {
- FileOutputStream fos = new FileOutputStream(path);
- fos.write(output);
- fos.close();
- }
+public class VeritySigner {
// USAGE:
// VeritySigner <contentfile> <key.pem> <sigfile>
// To verify that this has correct output:
// openssl rsautl -raw -inkey <key.pem> -encrypt -in <sigfile> > /tmp/dump
public static void main(String[] args) throws Exception {
- byte[] content = read(args[0]);
- PrivateKey privateKey = loadPrivateKey(new String(read(args[1])));
- byte[] signature = sign(privateKey, content);
- writeOutput(args[2], signature);
+ byte[] content = Utils.read(args[0]);
+ PrivateKey privateKey = Utils.loadPEMPrivateKey(Utils.read(args[1]));
+ byte[] signature = Utils.sign(privateKey, content);
+ Utils.write(signature, args[2]);
}
}
diff --git a/verity/boot_signer b/verity/boot_signer
new file mode 100755
index 00000000..e2ee42bf
--- /dev/null
+++ b/verity/boot_signer
@@ -0,0 +1,8 @@
+#! /bin/sh
+
+# Start-up script for BootSigner
+
+BOOTSIGNER_HOME=`dirname "$0"`
+BOOTSIGNER_HOME=`dirname "$BOOTSIGNER_HOME"`
+
+java -Xmx512M -jar "$BOOTSIGNER_HOME"/framework/BootSignature.jar "$@" \ No newline at end of file
diff --git a/verity/keystore_signer b/verity/keystore_signer
new file mode 100644
index 00000000..7619c546
--- /dev/null
+++ b/verity/keystore_signer
@@ -0,0 +1,8 @@
+#! /bin/sh
+
+# Start-up script for KeystoreSigner
+
+KEYSTORESIGNER_HOME=`dirname "$0"`
+KEYSTORESIGNER_HOME=`dirname "$KEYSTORESIGNER_HOME"`
+
+java -Xmx512M -jar "$KEYSTORESIGNER_HOME"/framework/KeystoreSigner.jar "$@" \ No newline at end of file