From 4e4dd2385b89ce44ffee495059d201f0e23c1700 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Thu, 19 Apr 2018 16:18:24 +0200 Subject: [PATCH] reformat all zipsigner code with Android Studio Ctrl-Alt-L --- .../kellinwood/logging/AbstractLogger.java | 109 ++-- .../logging/ConsoleLoggerFactory.java | 7 +- .../kellinwood/logging/LoggerFactory.java | 3 +- .../kellinwood/logging/LoggerInterface.java | 55 +- .../kellinwood/logging/LoggerManager.java | 35 +- .../kellinwood/logging/NullLoggerFactory.java | 71 +-- .../java/kellinwood/logging/StreamLogger.java | 26 +- .../security/zipsigner/AutoKeyException.java | 9 +- .../kellinwood/security/zipsigner/Base64.java | 75 ++- .../zipsigner/DefaultResourceAdapter.java | 1 + .../security/zipsigner/HexDumpEncoder.java | 44 +- .../security/zipsigner/HexEncoder.java | 148 +++-- .../kellinwood/security/zipsigner/KeySet.java | 19 +- .../security/zipsigner/ProgressEvent.java | 12 +- .../security/zipsigner/ProgressHelper.java | 26 +- .../security/zipsigner/ProgressListener.java | 8 +- .../security/zipsigner/ResourceAdapter.java | 7 +- .../security/zipsigner/ZipSignature.java | 39 +- .../security/zipsigner/ZipSigner.java | 511 +++++++++--------- .../zipsigner/optional/CertCreator.java | 111 ++-- .../zipsigner/optional/CustomKeySigner.java | 34 +- .../optional/DistinguishedNameValues.java | 74 +-- .../zipsigner/optional/Fingerprint.java | 21 +- .../security/zipsigner/optional/JKS.java | 263 ++++----- .../zipsigner/optional/JksKeyStore.java | 1 + .../optional/KeyNameConflictException.java | 1 + .../optional/KeyStoreFileManager.java | 161 +++--- .../optional/LoadKeystoreException.java | 1 + .../optional/PasswordObfuscator.java | 74 +-- .../optional/SignatureBlockGenerator.java | 11 +- .../java/kellinwood/zipio/CentralEnd.java | 55 +- .../main/java/kellinwood/zipio/ZioEntry.java | 349 ++++++------ .../kellinwood/zipio/ZioEntryInputStream.java | 53 +- .../zipio/ZioEntryOutputStream.java | 28 +- .../main/java/kellinwood/zipio/ZipInput.java | 136 +++-- .../kellinwood/zipio/ZipListingHelper.java | 34 +- .../main/java/kellinwood/zipio/ZipOutput.java | 112 ++-- 37 files changed, 1339 insertions(+), 1385 deletions(-) diff --git a/app/src/main/java/kellinwood/logging/AbstractLogger.java b/app/src/main/java/kellinwood/logging/AbstractLogger.java index b12146b90..6a33b46c5 100644 --- a/app/src/main/java/kellinwood/logging/AbstractLogger.java +++ b/app/src/main/java/kellinwood/logging/AbstractLogger.java @@ -13,84 +13,81 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package kellinwood.logging; -import java.io.PrintStream; import java.text.SimpleDateFormat; import java.util.Date; -public abstract class AbstractLogger implements LoggerInterface -{ +public abstract class AbstractLogger implements LoggerInterface { - protected String category; - - SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); - - public AbstractLogger( String category) { - this.category = category; - } - - protected String format( String level, String message) { - return String.format( "%s %s %s: %s\n", dateFormat.format(new Date()), level, category, message); - } - - protected abstract void write( String level, String message, Throwable t); + protected String category; - protected void writeFixNullMessage( String level, String message, Throwable t) { + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); + + public AbstractLogger(String category) { + this.category = category; + } + + protected String format(String level, String message) { + return String.format("%s %s %s: %s\n", dateFormat.format(new Date()), level, category, message); + } + + protected abstract void write(String level, String message, Throwable t); + + protected void writeFixNullMessage(String level, String message, Throwable t) { if (message == null) { if (t != null) message = t.getClass().getName(); else message = "null"; } - write( level, message, t); + write(level, message, t); } - public void debug(String message, Throwable t) { - writeFixNullMessage( DEBUG, message, t); - } + public void debug(String message, Throwable t) { + writeFixNullMessage(DEBUG, message, t); + } - public void debug(String message) { - writeFixNullMessage( DEBUG, message, null); - } + public void debug(String message) { + writeFixNullMessage(DEBUG, message, null); + } - public void error(String message, Throwable t) { - writeFixNullMessage( ERROR, message, t); - } + public void error(String message, Throwable t) { + writeFixNullMessage(ERROR, message, t); + } - public void error(String message) { - writeFixNullMessage( ERROR, message, null); - } + public void error(String message) { + writeFixNullMessage(ERROR, message, null); + } - public void info(String message, Throwable t) { - writeFixNullMessage( INFO, message, t); - } + public void info(String message, Throwable t) { + writeFixNullMessage(INFO, message, t); + } - public void info(String message) { - writeFixNullMessage( INFO, message, null); - } + public void info(String message) { + writeFixNullMessage(INFO, message, null); + } - public void warning(String message, Throwable t) { - writeFixNullMessage( WARNING, message, t); - } + public void warning(String message, Throwable t) { + writeFixNullMessage(WARNING, message, t); + } - public void warning(String message) { - writeFixNullMessage( WARNING, message, null); - } + public void warning(String message) { + writeFixNullMessage(WARNING, message, null); + } - public boolean isDebugEnabled() { - return true; - } + public boolean isDebugEnabled() { + return true; + } - public boolean isErrorEnabled() { - return true; - } - - public boolean isInfoEnabled() { - return true; - } - - public boolean isWarningEnabled() { - return true; - } + public boolean isErrorEnabled() { + return true; + } + public boolean isInfoEnabled() { + return true; + } + public boolean isWarningEnabled() { + return true; + } } diff --git a/app/src/main/java/kellinwood/logging/ConsoleLoggerFactory.java b/app/src/main/java/kellinwood/logging/ConsoleLoggerFactory.java index 0e70797e3..1d4b31aa2 100644 --- a/app/src/main/java/kellinwood/logging/ConsoleLoggerFactory.java +++ b/app/src/main/java/kellinwood/logging/ConsoleLoggerFactory.java @@ -13,11 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package kellinwood.logging; public class ConsoleLoggerFactory implements LoggerFactory { - public LoggerInterface getLogger(String category) { - return new StreamLogger( category, System.out); - } + public LoggerInterface getLogger(String category) { + return new StreamLogger(category, System.out); + } } diff --git a/app/src/main/java/kellinwood/logging/LoggerFactory.java b/app/src/main/java/kellinwood/logging/LoggerFactory.java index 2e9f2fe19..fe15cab94 100644 --- a/app/src/main/java/kellinwood/logging/LoggerFactory.java +++ b/app/src/main/java/kellinwood/logging/LoggerFactory.java @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package kellinwood.logging; public interface LoggerFactory { - public LoggerInterface getLogger( String category); + public LoggerInterface getLogger(String category); } diff --git a/app/src/main/java/kellinwood/logging/LoggerInterface.java b/app/src/main/java/kellinwood/logging/LoggerInterface.java index 6bda9a77b..5612d199f 100644 --- a/app/src/main/java/kellinwood/logging/LoggerInterface.java +++ b/app/src/main/java/kellinwood/logging/LoggerInterface.java @@ -13,31 +13,40 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package kellinwood.logging; public interface LoggerInterface { - public static final String ERROR = "ERROR"; - public static final String WARNING = "WARNING"; - public static final String INFO = "INFO"; - public static final String DEBUG = "DEBUG"; - - public boolean isErrorEnabled(); - public void error( String message); - public void error( String message, Throwable t); - - - public boolean isWarningEnabled(); - public void warning( String message); - public void warning( String message, Throwable t); - - public boolean isInfoEnabled(); - public void info( String message); - public void info( String message, Throwable t); - - public boolean isDebugEnabled(); - public void debug( String message); - public void debug( String message, Throwable t); - - + public static final String ERROR = "ERROR"; + public static final String WARNING = "WARNING"; + public static final String INFO = "INFO"; + public static final String DEBUG = "DEBUG"; + + public boolean isErrorEnabled(); + + public void error(String message); + + public void error(String message, Throwable t); + + + public boolean isWarningEnabled(); + + public void warning(String message); + + public void warning(String message, Throwable t); + + public boolean isInfoEnabled(); + + public void info(String message); + + public void info(String message, Throwable t); + + public boolean isDebugEnabled(); + + public void debug(String message); + + public void debug(String message, Throwable t); + + } diff --git a/app/src/main/java/kellinwood/logging/LoggerManager.java b/app/src/main/java/kellinwood/logging/LoggerManager.java index 4ac8dcba6..c71987cde 100644 --- a/app/src/main/java/kellinwood/logging/LoggerManager.java +++ b/app/src/main/java/kellinwood/logging/LoggerManager.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package kellinwood.logging; import java.util.Map; @@ -20,21 +21,21 @@ import java.util.TreeMap; public class LoggerManager { - static LoggerFactory factory = new NullLoggerFactory(); - - static Map loggers = new TreeMap(); - - public static void setLoggerFactory( LoggerFactory f) { - factory = f; - } - - public static LoggerInterface getLogger(String category) { - - LoggerInterface logger = loggers.get( category); - if (logger == null) { - logger = factory.getLogger(category); - loggers.put( category, logger); - } - return logger; - } + static LoggerFactory factory = new NullLoggerFactory(); + + static Map loggers = new TreeMap(); + + public static void setLoggerFactory(LoggerFactory f) { + factory = f; + } + + public static LoggerInterface getLogger(String category) { + + LoggerInterface logger = loggers.get(category); + if (logger == null) { + logger = factory.getLogger(category); + loggers.put(category, logger); + } + return logger; + } } diff --git a/app/src/main/java/kellinwood/logging/NullLoggerFactory.java b/app/src/main/java/kellinwood/logging/NullLoggerFactory.java index 4fdcf3edf..e24cd7350 100644 --- a/app/src/main/java/kellinwood/logging/NullLoggerFactory.java +++ b/app/src/main/java/kellinwood/logging/NullLoggerFactory.java @@ -13,57 +13,58 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package kellinwood.logging; public class NullLoggerFactory implements LoggerFactory { - static LoggerInterface logger = new LoggerInterface() { + static LoggerInterface logger = new LoggerInterface() { - public void debug(String message) { - } + public void debug(String message) { + } - public void debug(String message, Throwable t) { - } + public void debug(String message, Throwable t) { + } - public void error(String message) { - } + public void error(String message) { + } - public void error(String message, Throwable t) { - } + public void error(String message, Throwable t) { + } - public void info(String message) { - } + public void info(String message) { + } - public void info(String message, Throwable t) { - } + public void info(String message, Throwable t) { + } - public boolean isDebugEnabled() { - return false; - } + public boolean isDebugEnabled() { + return false; + } - public boolean isErrorEnabled() { - return false; - } + public boolean isErrorEnabled() { + return false; + } - public boolean isInfoEnabled() { - return false; - } + public boolean isInfoEnabled() { + return false; + } - public boolean isWarningEnabled() { - return false; - } + public boolean isWarningEnabled() { + return false; + } - public void warning(String message) { - } + public void warning(String message) { + } - public void warning(String message, Throwable t) { - } - - }; - + public void warning(String message, Throwable t) { + } - public LoggerInterface getLogger(String category) { - return logger; - } + }; + + + public LoggerInterface getLogger(String category) { + return logger; + } } diff --git a/app/src/main/java/kellinwood/logging/StreamLogger.java b/app/src/main/java/kellinwood/logging/StreamLogger.java index 511755608..987cb0143 100644 --- a/app/src/main/java/kellinwood/logging/StreamLogger.java +++ b/app/src/main/java/kellinwood/logging/StreamLogger.java @@ -13,24 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package kellinwood.logging; import java.io.PrintStream; public class StreamLogger extends AbstractLogger { - PrintStream out; - - public StreamLogger( String category, PrintStream out) - { - super( category); - this.out = out; - } - - @Override - protected void write(String level, String message, Throwable t) { - out.print( format( level, message)); - if (t != null) t.printStackTrace(out); - } + PrintStream out; + + public StreamLogger(String category, PrintStream out) { + super(category); + this.out = out; + } + + @Override + protected void write(String level, String message, Throwable t) { + out.print(format(level, message)); + if (t != null) t.printStackTrace(out); + } } diff --git a/app/src/main/java/kellinwood/security/zipsigner/AutoKeyException.java b/app/src/main/java/kellinwood/security/zipsigner/AutoKeyException.java index 6223d32eb..769383991 100644 --- a/app/src/main/java/kellinwood/security/zipsigner/AutoKeyException.java +++ b/app/src/main/java/kellinwood/security/zipsigner/AutoKeyException.java @@ -1,14 +1,15 @@ + package kellinwood.security.zipsigner; public class AutoKeyException extends RuntimeException { private static final long serialVersionUID = 1L; - public AutoKeyException( String message) { + public AutoKeyException(String message) { super(message); } - - public AutoKeyException( String message, Throwable cause) { + + public AutoKeyException(String message, Throwable cause) { super(message, cause); - } + } } diff --git a/app/src/main/java/kellinwood/security/zipsigner/Base64.java b/app/src/main/java/kellinwood/security/zipsigner/Base64.java index f178a026c..142d546a2 100644 --- a/app/src/main/java/kellinwood/security/zipsigner/Base64.java +++ b/app/src/main/java/kellinwood/security/zipsigner/Base64.java @@ -15,15 +15,16 @@ */ + package kellinwood.security.zipsigner; +import kellinwood.logging.LoggerInterface; +import kellinwood.logging.LoggerManager; + import java.io.ByteArrayOutputStream; import java.io.OutputStream; import java.lang.reflect.Method; -import kellinwood.logging.LoggerInterface; -import kellinwood.logging.LoggerManager; - /* * This class provides Base64 encoding services using one of several possible * implementations available elsewhere in the classpath. Supported implementations @@ -42,92 +43,88 @@ public class Base64 { static Method aEncodeMethod = null; // Reference to the android.util.Base64.encode() method, if available static Method aDecodeMethod = null; // Reference to the android.util.Base64.decode() method, if available - + static Object bEncoder = null; // Reference to an org.bouncycastle.util.encoders.Base64Encoder instance, if available static Method bEncodeMethod = null; // Reference to the bEncoder.encode() method, if available - + static Object bDecoder = null; // Reference to an org.bouncycastle.util.encoders.Base64Encoder instance, if available static Method bDecodeMethod = null; // Reference to the bEncoder.encode() method, if available - + static LoggerInterface logger = null; - + static { - + Class clazz; - - logger = LoggerManager.getLogger( Base64.class.getName()); - + + logger = LoggerManager.getLogger(Base64.class.getName()); + try { clazz = (Class) Class.forName("android.util.Base64"); // Looking for encode( byte[] input, int flags) aEncodeMethod = clazz.getMethod("encode", byte[].class, Integer.TYPE); aDecodeMethod = clazz.getMethod("decode", byte[].class, Integer.TYPE); - logger.info( clazz.getName() + " is available."); - } - catch (ClassNotFoundException x) {} // Ignore + logger.info(clazz.getName() + " is available."); + } catch (ClassNotFoundException x) { + } // Ignore catch (Exception x) { logger.error("Failed to initialize use of android.util.Base64", x); } - + try { clazz = (Class) Class.forName("org.bouncycastle.util.encoders.Base64Encoder"); bEncoder = clazz.newInstance(); // Looking for encode( byte[] input, int offset, int length, OutputStream output) bEncodeMethod = clazz.getMethod("encode", byte[].class, Integer.TYPE, Integer.TYPE, OutputStream.class); - logger.info( clazz.getName() + " is available."); + logger.info(clazz.getName() + " is available."); // Looking for decode( byte[] input, int offset, int length, OutputStream output) bDecodeMethod = clazz.getMethod("decode", byte[].class, Integer.TYPE, Integer.TYPE, OutputStream.class); - } - catch (ClassNotFoundException x) {} // Ignore + } catch (ClassNotFoundException x) { + } // Ignore catch (Exception x) { logger.error("Failed to initialize use of org.bouncycastle.util.encoders.Base64Encoder", x); } - + if (aEncodeMethod == null && bEncodeMethod == null) throw new IllegalStateException("No base64 encoder implementation is available."); } - - public static String encode( byte[] data) { + + public static String encode(byte[] data) { try { if (aEncodeMethod != null) { // Invoking a static method call, using null for the instance value - byte[] encodedBytes = (byte[])aEncodeMethod.invoke(null, data, 2); - return new String( encodedBytes); - } - else if (bEncodeMethod != null) { + byte[] encodedBytes = (byte[]) aEncodeMethod.invoke(null, data, 2); + return new String(encodedBytes); + } else if (bEncodeMethod != null) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); bEncodeMethod.invoke(bEncoder, data, 0, data.length, baos); - return new String( baos.toByteArray()); + return new String(baos.toByteArray()); } - } - catch (Exception x) { - throw new IllegalStateException( x.getClass().getName() + ": " + x.getMessage()); + } catch (Exception x) { + throw new IllegalStateException(x.getClass().getName() + ": " + x.getMessage()); } - + throw new IllegalStateException("No base64 encoder implementation is available."); } - - public static byte[] decode( byte[] data) { + + public static byte[] decode(byte[] data) { try { if (aDecodeMethod != null) { // Invoking a static method call, using null for the instance value - byte[] decodedBytes = (byte[])aDecodeMethod.invoke(null, data, 2); + byte[] decodedBytes = (byte[]) aDecodeMethod.invoke(null, data, 2); return decodedBytes; - } - else if (bDecodeMethod != null) { + } else if (bDecodeMethod != null) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); bDecodeMethod.invoke(bEncoder, data, 0, data.length, baos); return baos.toByteArray(); } - } - catch (Exception x) { - throw new IllegalStateException( x.getClass().getName() + ": " + x.getMessage()); + } catch (Exception x) { + throw new IllegalStateException(x.getClass().getName() + ": " + x.getMessage()); } - + throw new IllegalStateException("No base64 encoder implementation is available."); } } \ No newline at end of file diff --git a/app/src/main/java/kellinwood/security/zipsigner/DefaultResourceAdapter.java b/app/src/main/java/kellinwood/security/zipsigner/DefaultResourceAdapter.java index 3ecc1be8e..35a82532a 100644 --- a/app/src/main/java/kellinwood/security/zipsigner/DefaultResourceAdapter.java +++ b/app/src/main/java/kellinwood/security/zipsigner/DefaultResourceAdapter.java @@ -1,3 +1,4 @@ + package kellinwood.security.zipsigner; /** diff --git a/app/src/main/java/kellinwood/security/zipsigner/HexDumpEncoder.java b/app/src/main/java/kellinwood/security/zipsigner/HexDumpEncoder.java index ad4ca23c3..d0f49af1a 100644 --- a/app/src/main/java/kellinwood/security/zipsigner/HexDumpEncoder.java +++ b/app/src/main/java/kellinwood/security/zipsigner/HexDumpEncoder.java @@ -15,58 +15,58 @@ */ + package kellinwood.security.zipsigner; -import java.io.IOException; import java.io.ByteArrayOutputStream; +import java.io.IOException; -/** Produces the classic hex dump with an address column, hex data +/** + * Produces the classic hex dump with an address column, hex data * section (16 bytes per row) and right-column printable character dislpay. - */ -public class HexDumpEncoder -{ + */ +public class HexDumpEncoder { static HexEncoder encoder = new HexEncoder(); - public static String encode( byte[] data) { + public static String encode(byte[] data) { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); - encoder.encode( data, 0, data.length, baos); + encoder.encode(data, 0, data.length, baos); byte[] hex = baos.toByteArray(); StringBuilder hexDumpOut = new StringBuilder(); for (int i = 0; i < hex.length; i += 32) { - int max = Math.min(i+32, hex.length); + int max = Math.min(i + 32, hex.length); StringBuilder hexOut = new StringBuilder(); StringBuilder chrOut = new StringBuilder(); - hexOut.append( String.format("%08x: ", (i/2))); + hexOut.append(String.format("%08x: ", (i / 2))); - for (int j = i; j < max; j+= 2) { - hexOut.append( Character.valueOf( (char)hex[j])); - hexOut.append( Character.valueOf( (char)hex[j+1])); - if ((j+2) % 4 == 0) hexOut.append( ' '); + for (int j = i; j < max; j += 2) { + hexOut.append(Character.valueOf((char) hex[j])); + hexOut.append(Character.valueOf((char) hex[j + 1])); + if ((j + 2) % 4 == 0) hexOut.append(' '); - int dataChar = data[j/2]; - if (dataChar >= 32 && dataChar < 127) chrOut.append( Character.valueOf( (char)dataChar)); - else chrOut.append( '.'); + int dataChar = data[j / 2]; + if (dataChar >= 32 && dataChar < 127) chrOut.append(Character.valueOf((char) dataChar)); + else chrOut.append('.'); } - hexDumpOut.append( hexOut.toString()); + hexDumpOut.append(hexOut.toString()); for (int k = hexOut.length(); k < 50; k++) hexDumpOut.append(' '); - hexDumpOut.append( " "); - hexDumpOut.append( chrOut); + hexDumpOut.append(" "); + hexDumpOut.append(chrOut); hexDumpOut.append("\n"); } return hexDumpOut.toString(); - } - catch (IOException x) { - throw new IllegalStateException( x.getClass().getName() + ": " + x.getMessage()); + } catch (IOException x) { + throw new IllegalStateException(x.getClass().getName() + ": " + x.getMessage()); } } diff --git a/app/src/main/java/kellinwood/security/zipsigner/HexEncoder.java b/app/src/main/java/kellinwood/security/zipsigner/HexEncoder.java index e0c3102e4..b88744243 100644 --- a/app/src/main/java/kellinwood/security/zipsigner/HexEncoder.java +++ b/app/src/main/java/kellinwood/security/zipsigner/HexEncoder.java @@ -1,3 +1,4 @@ + package kellinwood.security.zipsigner; /* @@ -32,26 +33,23 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import java.io.IOException; import java.io.OutputStream; -public class HexEncoder -{ +public class HexEncoder { protected final byte[] encodingTable = - { - (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', (byte)'7', - (byte)'8', (byte)'9', (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f' - }; - + { + (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7', + (byte) '8', (byte) '9', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f' + }; + /* * set up the decoding table. */ protected final byte[] decodingTable = new byte[128]; - protected void initialiseDecodingTable() - { - for (int i = 0; i < encodingTable.length; i++) - { - decodingTable[encodingTable[i]] = (byte)i; + protected void initialiseDecodingTable() { + for (int i = 0; i < encodingTable.length; i++) { + decodingTable[encodingTable[i]] = (byte) i; } - + decodingTable['A'] = decodingTable['a']; decodingTable['B'] = decodingTable['b']; decodingTable['C'] = decodingTable['c']; @@ -59,27 +57,24 @@ public class HexEncoder decodingTable['E'] = decodingTable['e']; decodingTable['F'] = decodingTable['f']; } - - public HexEncoder() - { + + public HexEncoder() { initialiseDecodingTable(); } - + /** * encode the input data producing a Hex output stream. * * @return the number of bytes produced. */ public int encode( - byte[] data, - int off, - int length, - OutputStream out) - throws IOException - { - for (int i = off; i < (off + length); i++) - { - int v = data[i] & 0xff; + byte[] data, + int off, + int length, + OutputStream out) + throws IOException { + for (int i = off; i < (off + length); i++) { + int v = data[i] & 0xff; out.write(encodingTable[(v >>> 4)]); out.write(encodingTable[v & 0xf]); @@ -89,11 +84,10 @@ public class HexEncoder } private boolean ignore( - char c) - { - return (c == '\n' || c =='\r' || c == '\t' || c == ' '); + char c) { + return (c == '\n' || c == '\r' || c == '\t' || c == ' '); } - + /** * decode the Hex encoded byte data writing it to the given output stream, * whitespace characters will be ignored. @@ -101,52 +95,46 @@ public class HexEncoder * @return the number of bytes produced. */ public int decode( - byte[] data, - int off, - int length, - OutputStream out) - throws IOException - { - byte b1, b2; - int outLen = 0; - - int end = off + length; - - while (end > off) - { - if (!ignore((char)data[end - 1])) - { + byte[] data, + int off, + int length, + OutputStream out) + throws IOException { + byte b1, b2; + int outLen = 0; + + int end = off + length; + + while (end > off) { + if (!ignore((char) data[end - 1])) { break; } - + end--; } - + int i = off; - while (i < end) - { - while (i < end && ignore((char)data[i])) - { + while (i < end) { + while (i < end && ignore((char) data[i])) { i++; } - + b1 = decodingTable[data[i++]]; - - while (i < end && ignore((char)data[i])) - { + + while (i < end && ignore((char) data[i])) { i++; } - + b2 = decodingTable[data[i++]]; out.write((b1 << 4) | b2); - + outLen++; } return outLen; } - + /** * decode the Hex encoded String data writing it to the given output stream, * whitespace characters will be ignored. @@ -154,44 +142,38 @@ public class HexEncoder * @return the number of bytes produced. */ public int decode( - String data, - OutputStream out) - throws IOException - { - byte b1, b2; - int length = 0; - - int end = data.length(); - - while (end > 0) - { - if (!ignore(data.charAt(end - 1))) - { + String data, + OutputStream out) + throws IOException { + byte b1, b2; + int length = 0; + + int end = data.length(); + + while (end > 0) { + if (!ignore(data.charAt(end - 1))) { break; } - + end--; } - + int i = 0; - while (i < end) - { - while (i < end && ignore(data.charAt(i))) - { + while (i < end) { + while (i < end && ignore(data.charAt(i))) { i++; } - + b1 = decodingTable[data.charAt(i++)]; - - while (i < end && ignore(data.charAt(i))) - { + + while (i < end && ignore(data.charAt(i))) { i++; } - + b2 = decodingTable[data.charAt(i++)]; out.write((b1 << 4) | b2); - + length++; } diff --git a/app/src/main/java/kellinwood/security/zipsigner/KeySet.java b/app/src/main/java/kellinwood/security/zipsigner/KeySet.java index 9b08f8826..cfdeb180f 100644 --- a/app/src/main/java/kellinwood/security/zipsigner/KeySet.java +++ b/app/src/main/java/kellinwood/security/zipsigner/KeySet.java @@ -1,5 +1,3 @@ - - /* * Copyright (C) 2010 Ken Ellinwood * @@ -15,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package kellinwood.security.zipsigner; import java.security.PrivateKey; @@ -23,31 +22,29 @@ import java.security.cert.X509Certificate; public class KeySet { String name; - + // certificate X509Certificate publicKey = null; - + // private key - PrivateKey privateKey = null; + PrivateKey privateKey = null; // signature block template byte[] sigBlockTemplate = null; String signatureAlgorithm = "SHA1withRSA"; - + public KeySet() { } - - public KeySet( String name, X509Certificate publicKey, PrivateKey privateKey, byte[] sigBlockTemplate) - { + + public KeySet(String name, X509Certificate publicKey, PrivateKey privateKey, byte[] sigBlockTemplate) { this.name = name; this.publicKey = publicKey; this.privateKey = privateKey; this.sigBlockTemplate = sigBlockTemplate; } - public KeySet( String name, X509Certificate publicKey, PrivateKey privateKey, String signatureAlgorithm, byte[] sigBlockTemplate) - { + public KeySet(String name, X509Certificate publicKey, PrivateKey privateKey, String signatureAlgorithm, byte[] sigBlockTemplate) { this.name = name; this.publicKey = publicKey; this.privateKey = privateKey; diff --git a/app/src/main/java/kellinwood/security/zipsigner/ProgressEvent.java b/app/src/main/java/kellinwood/security/zipsigner/ProgressEvent.java index c862e55c3..46feb6b44 100644 --- a/app/src/main/java/kellinwood/security/zipsigner/ProgressEvent.java +++ b/app/src/main/java/kellinwood/security/zipsigner/ProgressEvent.java @@ -13,34 +13,40 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package kellinwood.security.zipsigner; public class ProgressEvent { public static final int PRORITY_NORMAL = 0; public static final int PRORITY_IMPORTANT = 1; - + private String message; private int percentDone; private int priority; - + public String getMessage() { return message; } + public void setMessage(String message) { this.message = message; } + public int getPercentDone() { return percentDone; } + public void setPercentDone(int percentDone) { this.percentDone = percentDone; } + public int getPriority() { return priority; } + public void setPriority(int priority) { this.priority = priority; } - + } diff --git a/app/src/main/java/kellinwood/security/zipsigner/ProgressHelper.java b/app/src/main/java/kellinwood/security/zipsigner/ProgressHelper.java index be472c003..41b09f264 100644 --- a/app/src/main/java/kellinwood/security/zipsigner/ProgressHelper.java +++ b/app/src/main/java/kellinwood/security/zipsigner/ProgressHelper.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package kellinwood.security.zipsigner; import java.util.ArrayList; @@ -22,13 +23,12 @@ public class ProgressHelper { private int progressTotalItems = 0; private int progressCurrentItem = 0; private ProgressEvent progressEvent = new ProgressEvent(); - - public void initProgress() - { + + public void initProgress() { progressTotalItems = 10000; - progressCurrentItem = 0; + progressCurrentItem = 0; } - + public int getProgressTotalItems() { return progressTotalItems; } @@ -45,7 +45,7 @@ public class ProgressHelper { this.progressCurrentItem = progressCurrentItem; } - public void progress( int priority, String message) { + public void progress(int priority, String message) { progressCurrentItem += 1; @@ -58,25 +58,23 @@ public class ProgressHelper { progressEvent.setMessage(message); progressEvent.setPercentDone(percentDone); progressEvent.setPriority(priority); - listener.onProgress( progressEvent); + listener.onProgress(progressEvent); } } private ArrayList listeners = new ArrayList(); @SuppressWarnings("unchecked") - public synchronized void addProgressListener( ProgressListener l) - { - ArrayList list = (ArrayList)listeners.clone(); + public synchronized void addProgressListener(ProgressListener l) { + ArrayList list = (ArrayList) listeners.clone(); list.add(l); listeners = list; } @SuppressWarnings("unchecked") - public synchronized void removeProgressListener( ProgressListener l) - { - ArrayList list = (ArrayList)listeners.clone(); + public synchronized void removeProgressListener(ProgressListener l) { + ArrayList list = (ArrayList) listeners.clone(); list.remove(l); listeners = list; - } + } } diff --git a/app/src/main/java/kellinwood/security/zipsigner/ProgressListener.java b/app/src/main/java/kellinwood/security/zipsigner/ProgressListener.java index 5bd0b5670..3cb62060a 100644 --- a/app/src/main/java/kellinwood/security/zipsigner/ProgressListener.java +++ b/app/src/main/java/kellinwood/security/zipsigner/ProgressListener.java @@ -13,12 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package kellinwood.security.zipsigner; public interface ProgressListener { - /** Called to notify the listener that progress has been made during - the zip signing operation. + /** + * Called to notify the listener that progress has been made during + * the zip signing operation. */ - public void onProgress( ProgressEvent event); + public void onProgress(ProgressEvent event); } \ No newline at end of file diff --git a/app/src/main/java/kellinwood/security/zipsigner/ResourceAdapter.java b/app/src/main/java/kellinwood/security/zipsigner/ResourceAdapter.java index a347cf0db..4377b7813 100644 --- a/app/src/main/java/kellinwood/security/zipsigner/ResourceAdapter.java +++ b/app/src/main/java/kellinwood/security/zipsigner/ResourceAdapter.java @@ -1,3 +1,4 @@ + package kellinwood.security.zipsigner; /** @@ -14,7 +15,9 @@ public interface ResourceAdapter { GENERATING_SIGNATURE_FILE, GENERATING_SIGNATURE_BLOCK, COPYING_ZIP_ENTRY - }; + } - public String getString( Item item, Object... args); + ; + + public String getString(Item item, Object... args); } diff --git a/app/src/main/java/kellinwood/security/zipsigner/ZipSignature.java b/app/src/main/java/kellinwood/security/zipsigner/ZipSignature.java index ac03a53b6..93698a2c7 100644 --- a/app/src/main/java/kellinwood/security/zipsigner/ZipSignature.java +++ b/app/src/main/java/kellinwood/security/zipsigner/ZipSignature.java @@ -13,59 +13,56 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package kellinwood.security.zipsigner; +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; import java.io.IOException; import java.security.GeneralSecurityException; import java.security.InvalidKeyException; import java.security.MessageDigest; import java.security.PrivateKey; -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; - @SuppressWarnings("restriction") public class ZipSignature { - byte[] beforeAlgorithmIdBytes = { 0x30, 0x21 }; + byte[] beforeAlgorithmIdBytes = {0x30, 0x21}; // byte[] algorithmIdBytes; // algorithmIdBytes = sun.security.x509.AlgorithmId.get("SHA1").encode(); - byte[] algorithmIdBytes = {0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00 }; + byte[] algorithmIdBytes = {0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00}; - byte[] afterAlgorithmIdBytes = { 0x04, 0x14 }; + byte[] afterAlgorithmIdBytes = {0x04, 0x14}; Cipher cipher; MessageDigest md; - public ZipSignature() throws IOException, GeneralSecurityException - { + public ZipSignature() throws IOException, GeneralSecurityException { md = MessageDigest.getInstance("SHA1"); cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); } - public void initSign( PrivateKey privateKey) throws InvalidKeyException - { + public void initSign(PrivateKey privateKey) throws InvalidKeyException { cipher.init(Cipher.ENCRYPT_MODE, privateKey); } - public void update( byte[] data) { - md.update( data); + public void update(byte[] data) { + md.update(data); } - public void update( byte[] data, int offset, int count) { - md.update( data, offset, count); + public void update(byte[] data, int offset, int count) { + md.update(data, offset, count); } - public byte[] sign() throws BadPaddingException, IllegalBlockSizeException - { - cipher.update( beforeAlgorithmIdBytes); - cipher.update( algorithmIdBytes); - cipher.update( afterAlgorithmIdBytes); - cipher.update( md.digest()); + public byte[] sign() throws BadPaddingException, IllegalBlockSizeException { + cipher.update(beforeAlgorithmIdBytes); + cipher.update(algorithmIdBytes); + cipher.update(afterAlgorithmIdBytes); + cipher.update(md.digest()); return cipher.doFinal(); } } diff --git a/app/src/main/java/kellinwood/security/zipsigner/ZipSigner.java b/app/src/main/java/kellinwood/security/zipsigner/ZipSigner.java index a572b72e5..ca73c8d59 100644 --- a/app/src/main/java/kellinwood/security/zipsigner/ZipSigner.java +++ b/app/src/main/java/kellinwood/security/zipsigner/ZipSigner.java @@ -26,6 +26,7 @@ * using signature block template files. */ + package kellinwood.security.zipsigner; import kellinwood.logging.LoggerInterface; @@ -38,17 +39,40 @@ import javax.crypto.Cipher; import javax.crypto.EncryptedPrivateKeyInfo; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; -import java.io.*; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintStream; import java.lang.reflect.Method; import java.net.URL; -import java.security.*; +import java.security.DigestOutputStream; +import java.security.GeneralSecurityException; +import java.security.Key; +import java.security.KeyFactory; +import java.security.KeyStore; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.Security; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.security.spec.InvalidKeySpecException; import java.security.spec.KeySpec; import java.security.spec.PKCS8EncodedKeySpec; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Observable; +import java.util.Observer; +import java.util.TreeMap; import java.util.jar.Attributes; import java.util.jar.JarFile; import java.util.jar.Manifest; @@ -58,17 +82,16 @@ import java.util.regex.Pattern; * This is a modified copy of com.android.signapk.SignApk.java. It provides an * API to sign JAR files (including APKs and Zip/OTA updates) in * a way compatible with the mincrypt verifier, using SHA1 and RSA keys. - * + *

* Please see the README.txt file in the root of this project for usage instructions. */ -public class ZipSigner -{ +public class ZipSigner { private boolean canceled = false; private ProgressHelper progressHelper = new ProgressHelper(); private ResourceAdapter resourceAdapter = new DefaultResourceAdapter(); - + static LoggerInterface log = null; private static final String CERT_SF_NAME = "META-INF/CERT.SF"; @@ -76,13 +99,13 @@ public class ZipSigner // Files matching this pattern are not copied to the output. private static Pattern stripPattern = - Pattern.compile("^META-INF/(.*)[.](SF|RSA|DSA)$"); + Pattern.compile("^META-INF/(.*)[.](SF|RSA|DSA)$"); - Map loadedKeys = new HashMap(); + Map loadedKeys = new HashMap(); KeySet keySet = null; - + public static LoggerInterface getLogger() { - if (log == null) log = LoggerManager.getLogger( ZipSigner.class.getName()); + if (log == null) log = LoggerManager.getLogger(ZipSigner.class.getName()); return log; } @@ -91,25 +114,24 @@ public class ZipSigner public static final String MODE_AUTO = "auto"; public static final String KEY_NONE = "none"; public static final String KEY_TESTKEY = "testkey"; - + // Allowable key modes. public static final String[] SUPPORTED_KEY_MODES = - new String[] { MODE_AUTO_TESTKEY, MODE_AUTO, MODE_AUTO_NONE, "media", "platform", "shared", KEY_TESTKEY, KEY_NONE}; - + new String[]{MODE_AUTO_TESTKEY, MODE_AUTO, MODE_AUTO_NONE, "media", "platform", "shared", KEY_TESTKEY, KEY_NONE}; + String keymode = KEY_TESTKEY; // backwards compatible with versions that only signed with this key - - Map autoKeyDetect = new HashMap(); - + + Map autoKeyDetect = new HashMap(); + AutoKeyObservable autoKeyObservable = new AutoKeyObservable(); - - public ZipSigner() throws ClassNotFoundException, IllegalAccessException, InstantiationException - { + + public ZipSigner() throws ClassNotFoundException, IllegalAccessException, InstantiationException { // MD5 of the first 1458 bytes of the signature block generated by the key, mapped to the key name - autoKeyDetect.put( "aa9852bc5a53272ac8031d49b65e4b0e", "media"); - autoKeyDetect.put( "e60418c4b638f20d0721e115674ca11f", "platform"); - autoKeyDetect.put( "3e24e49741b60c215c010dc6048fca7d", "shared"); - autoKeyDetect.put( "dab2cead827ef5313f28e22b6fa8479f", "testkey"); - + autoKeyDetect.put("aa9852bc5a53272ac8031d49b65e4b0e", "media"); + autoKeyDetect.put("e60418c4b638f20d0721e115674ca11f", "platform"); + autoKeyDetect.put("3e24e49741b60c215c010dc6048fca7d", "shared"); + autoKeyDetect.put("dab2cead827ef5313f28e22b6fa8479f", "testkey"); + } public ResourceAdapter getResourceAdapter() { @@ -121,24 +143,22 @@ public class ZipSigner } // when the key mode is automatic, the observers are called when the key is determined - public void addAutoKeyObserver( Observer o) { + public void addAutoKeyObserver(Observer o) { autoKeyObservable.addObserver(o); } - + public String getKeymode() { return keymode; } - public void setKeymode(String km) throws IOException, GeneralSecurityException - { + public void setKeymode(String km) throws IOException, GeneralSecurityException { if (getLogger().isDebugEnabled()) getLogger().debug("setKeymode: " + km); keymode = km; if (keymode.startsWith(MODE_AUTO)) { keySet = null; - } - else { - progressHelper.initProgress(); - loadKeys( keymode); + } else { + progressHelper.initProgress(); + loadKeys(keymode); } } @@ -146,41 +166,40 @@ public class ZipSigner return SUPPORTED_KEY_MODES; } - - protected String autoDetectKey( String mode, Map zioEntries) - throws NoSuchAlgorithmException, IOException - { + + protected String autoDetectKey(String mode, Map zioEntries) + throws NoSuchAlgorithmException, IOException { boolean debug = getLogger().isDebugEnabled(); - + if (!mode.startsWith(MODE_AUTO)) return mode; - + // Auto-determine which keys to use String keyName = null; // Start by finding the signature block file in the input. - for (Map.Entry entry : zioEntries.entrySet()) { + for (Map.Entry entry : zioEntries.entrySet()) { String entryName = entry.getKey(); if (entryName.startsWith("META-INF/") && entryName.endsWith(".RSA")) { - + // Compute MD5 of the first 1458 bytes, which is the size of our signature block templates -- // e.g., the portion of the sig block file that is the same for a given certificate. MessageDigest md5 = MessageDigest.getInstance("MD5"); byte[] entryData = entry.getValue().getData(); if (entryData.length < 1458) break; // sig block too short to be a supported key - md5.update( entryData, 0, 1458); + md5.update(entryData, 0, 1458); byte[] rawDigest = md5.digest(); - + // Create the hex representation of the digest value StringBuilder builder = new StringBuilder(); - for( byte b : rawDigest) { - builder.append( String.format("%02x", b)); + for (byte b : rawDigest) { + builder.append(String.format("%02x", b)); } - + String md5String = builder.toString(); // Lookup the key name - keyName = autoKeyDetect.get( md5String); - - + keyName = autoKeyDetect.get(md5String); + + if (debug) { if (keyName != null) { getLogger().debug(String.format("Auto-determined key=%s using md5=%s", keyName, md5String)); @@ -191,19 +210,18 @@ public class ZipSigner if (keyName != null) return keyName; } } - - if (mode.equals( MODE_AUTO_TESTKEY)) { + + if (mode.equals(MODE_AUTO_TESTKEY)) { // in auto-testkey mode, fallback to the testkey if it couldn't be determined - if (debug) getLogger().debug("Falling back to key="+ keyName); + if (debug) getLogger().debug("Falling back to key=" + keyName); return KEY_TESTKEY; - - } - else if (mode.equals(MODE_AUTO_NONE)) { + + } else if (mode.equals(MODE_AUTO_NONE)) { // in auto-node mode, simply copy the input to the output when the key can't be determined. if (debug) getLogger().debug("Unable to determine key, returning: " + KEY_NONE); return KEY_NONE; } - + return null; } @@ -212,55 +230,52 @@ public class ZipSigner } // Loads one of the built-in keys (media, platform, shared, testkey) - public void loadKeys( String name) - throws IOException, GeneralSecurityException - { - + public void loadKeys(String name) + throws IOException, GeneralSecurityException { + keySet = loadedKeys.get(name); if (keySet != null) return; - + keySet = new KeySet(); keySet.setName(name); - loadedKeys.put( name, keySet); - + loadedKeys.put(name, keySet); + if (KEY_NONE.equals(name)) return; issueLoadingCertAndKeysProgressEvent(); // load the private key - URL privateKeyUrl = getClass().getResource("/keys/"+name+".pk8"); + URL privateKeyUrl = getClass().getResource("/keys/" + name + ".pk8"); keySet.setPrivateKey(readPrivateKey(privateKeyUrl, null)); // load the certificate - URL publicKeyUrl = getClass().getResource("/keys/"+name+".x509.pem"); + URL publicKeyUrl = getClass().getResource("/keys/" + name + ".x509.pem"); keySet.setPublicKey(readPublicKey(publicKeyUrl)); // load the signature block template - URL sigBlockTemplateUrl = getClass().getResource("/keys/"+name+".sbt"); + URL sigBlockTemplateUrl = getClass().getResource("/keys/" + name + ".sbt"); if (sigBlockTemplateUrl != null) { keySet.setSigBlockTemplate(readContentAsBytes(sigBlockTemplateUrl)); } } - - public void setKeys( String name, X509Certificate publicKey, PrivateKey privateKey, byte[] signatureBlockTemplate) - { - keySet = new KeySet( name, publicKey, privateKey, signatureBlockTemplate); + + public void setKeys(String name, X509Certificate publicKey, PrivateKey privateKey, byte[] signatureBlockTemplate) { + keySet = new KeySet(name, publicKey, privateKey, signatureBlockTemplate); } - public void setKeys( String name, X509Certificate publicKey, PrivateKey privateKey, String signatureAlgorithm, byte[] signatureBlockTemplate) - { - keySet = new KeySet( name, publicKey, privateKey, signatureAlgorithm, signatureBlockTemplate); + public void setKeys(String name, X509Certificate publicKey, PrivateKey privateKey, String signatureAlgorithm, byte[] signatureBlockTemplate) { + keySet = new KeySet(name, publicKey, privateKey, signatureAlgorithm, signatureBlockTemplate); } public KeySet getKeySet() { return keySet; } - + // Allow the operation to be canceled. public void cancel() { canceled = true; } - + // Allow the instance to sign again if previously canceled. public void resetCanceled() { canceled = false; @@ -271,17 +286,16 @@ public class ZipSigner } @SuppressWarnings("unchecked") - public void loadProvider( String providerClassName) - throws ClassNotFoundException, IllegalAccessException, InstantiationException - { + public void loadProvider(String providerClassName) + throws ClassNotFoundException, IllegalAccessException, InstantiationException { Class providerClass = Class.forName(providerClassName); - Provider provider = (Provider)providerClass.newInstance(); + Provider provider = (Provider) providerClass.newInstance(); Security.insertProviderAt(provider, 1); } public X509Certificate readPublicKey(URL publicKeyUrl) - throws IOException, GeneralSecurityException { + throws IOException, GeneralSecurityException { InputStream input = publicKeyUrl.openStream(); try { CertificateFactory cf = CertificateFactory.getInstance("X.509"); @@ -293,15 +307,15 @@ public class ZipSigner /** * Decrypt an encrypted PKCS 8 format private key. - * + *

* Based on ghstark's post on Aug 6, 2006 at * http://forums.sun.com/thread.jspa?threadID=758133&messageID=4330949 * * @param encryptedPrivateKey The raw data of the private key - * @param keyPassword the key password + * @param keyPassword the key password */ private KeySpec decryptPrivateKey(byte[] encryptedPrivateKey, String keyPassword) - throws GeneralSecurityException { + throws GeneralSecurityException { EncryptedPrivateKeyInfo epkInfo; try { epkInfo = new EncryptedPrivateKeyInfo(encryptedPrivateKey); @@ -326,35 +340,39 @@ public class ZipSigner } } - /** Fetch the content at the specified URL and return it as a byte array. */ - public byte[] readContentAsBytes( URL contentUrl) throws IOException - { - return readContentAsBytes( contentUrl.openStream()); + /** + * Fetch the content at the specified URL and return it as a byte array. + */ + public byte[] readContentAsBytes(URL contentUrl) throws IOException { + return readContentAsBytes(contentUrl.openStream()); } - /** Fetch the content from the given stream and return it as a byte array. */ - public byte[] readContentAsBytes( InputStream input) throws IOException - { + /** + * Fetch the content from the given stream and return it as a byte array. + */ + public byte[] readContentAsBytes(InputStream input) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer = new byte[2048]; - int numRead = input.read( buffer); + int numRead = input.read(buffer); while (numRead != -1) { - baos.write( buffer, 0, numRead); - numRead = input.read( buffer); + baos.write(buffer, 0, numRead); + numRead = input.read(buffer); } byte[] bytes = baos.toByteArray(); return bytes; } - /** Read a PKCS 8 format private key. */ + /** + * Read a PKCS 8 format private key. + */ public PrivateKey readPrivateKey(URL privateKeyUrl, String keyPassword) - throws IOException, GeneralSecurityException { - DataInputStream input = new DataInputStream( privateKeyUrl.openStream()); + throws IOException, GeneralSecurityException { + DataInputStream input = new DataInputStream(privateKeyUrl.openStream()); try { - byte[] bytes = readContentAsBytes( input); + byte[] bytes = readContentAsBytes(input); KeySpec spec = decryptPrivateKey(bytes, keyPassword); if (spec == null) { @@ -371,15 +389,16 @@ public class ZipSigner } } - /** Add the SHA1 of every file to the manifest, creating it if necessary. */ - private Manifest addDigestsToManifest(Map entries) - throws IOException, GeneralSecurityException - { + /** + * Add the SHA1 of every file to the manifest, creating it if necessary. + */ + private Manifest addDigestsToManifest(Map entries) + throws IOException, GeneralSecurityException { Manifest input = null; ZioEntry manifestEntry = entries.get(JarFile.MANIFEST_NAME); if (manifestEntry != null) { input = new Manifest(); - input.read( manifestEntry.getInputStream()); + input.read(manifestEntry.getInputStream()); } Manifest output = new Manifest(); Attributes main = output.getMainAttributes(); @@ -400,30 +419,29 @@ public class ZipSigner // map will be deterministic. TreeMap byName = new TreeMap(); - byName.putAll( entries); + byName.putAll(entries); boolean debug = getLogger().isDebugEnabled(); if (debug) getLogger().debug("Manifest entries:"); - for (ZioEntry entry: byName.values()) { + for (ZioEntry entry : byName.values()) { if (canceled) break; String name = entry.getName(); if (debug) getLogger().debug(name); if (!entry.isDirectory() && !name.equals(JarFile.MANIFEST_NAME) && !name.equals(CERT_SF_NAME) && !name.equals(CERT_RSA_NAME) && (stripPattern == null || - !stripPattern.matcher(name).matches())) - { + !stripPattern.matcher(name).matches())) { - progressHelper.progress( ProgressEvent.PRORITY_NORMAL, resourceAdapter.getString(ResourceAdapter.Item.GENERATING_MANIFEST)); + progressHelper.progress(ProgressEvent.PRORITY_NORMAL, resourceAdapter.getString(ResourceAdapter.Item.GENERATING_MANIFEST)); InputStream data = entry.getInputStream(); while ((num = data.read(buffer)) > 0) { md.update(buffer, 0, num); } - + Attributes attr = null; if (input != null) { - java.util.jar.Attributes inAttr = input.getAttributes(name); - if (inAttr != null) attr = new Attributes( inAttr); + java.util.jar.Attributes inAttr = input.getAttributes(name); + if (inAttr != null) attr = new Attributes(inAttr); } if (attr == null) attr = new Attributes(); attr.putValue("SHA1-Digest", Base64.encode(md.digest())); @@ -435,11 +453,13 @@ public class ZipSigner } - /** Write the signature file to the given output stream. */ + /** + * Write the signature file to the given output stream. + */ private void generateSignatureFile(Manifest manifest, OutputStream out) - throws IOException, GeneralSecurityException { - out.write( ("Signature-Version: 1.0\r\n").getBytes()); - out.write( ("Created-By: 1.0 (Android SignApk)\r\n").getBytes()); + throws IOException, GeneralSecurityException { + out.write(("Signature-Version: 1.0\r\n").getBytes()); + out.write(("Created-By: 1.0 (Android SignApk)\r\n").getBytes()); // BASE64Encoder base64 = new BASE64Encoder(); @@ -452,32 +472,33 @@ public class ZipSigner manifest.write(print); print.flush(); - out.write( ("SHA1-Digest-Manifest: "+ Base64.encode(md.digest()) + "\r\n\r\n").getBytes()); + out.write(("SHA1-Digest-Manifest: " + Base64.encode(md.digest()) + "\r\n\r\n").getBytes()); Map entries = manifest.getEntries(); for (Map.Entry entry : entries.entrySet()) { if (canceled) break; - progressHelper.progress( ProgressEvent.PRORITY_NORMAL, resourceAdapter.getString(ResourceAdapter.Item.GENERATING_SIGNATURE_FILE)); + progressHelper.progress(ProgressEvent.PRORITY_NORMAL, resourceAdapter.getString(ResourceAdapter.Item.GENERATING_SIGNATURE_FILE)); // Digest of the manifest stanza for this entry. - String nameEntry = "Name: " + entry.getKey() + "\r\n"; - print.print( nameEntry); + String nameEntry = "Name: " + entry.getKey() + "\r\n"; + print.print(nameEntry); for (Map.Entry att : entry.getValue().entrySet()) { print.print(att.getKey() + ": " + att.getValue() + "\r\n"); } print.print("\r\n"); print.flush(); - out.write( nameEntry.getBytes()); - out.write( ("SHA1-Digest: " + Base64.encode(md.digest()) + "\r\n\r\n").getBytes()); + out.write(nameEntry.getBytes()); + out.write(("SHA1-Digest: " + Base64.encode(md.digest()) + "\r\n\r\n").getBytes()); } } - /** Write a .RSA file with a digital signature. */ + /** + * Write a .RSA file with a digital signature. + */ @SuppressWarnings("unchecked") - private void writeSignatureBlock( KeySet keySet, byte[] signatureFileBytes, OutputStream out) - throws IOException, GeneralSecurityException - { + private void writeSignatureBlock(KeySet keySet, byte[] signatureFileBytes, OutputStream out) + throws IOException, GeneralSecurityException { if (keySet.getSigBlockTemplate() != null) { // Can't use default Signature on Android. Although it generates a signature that can be verified by jarsigner, @@ -488,35 +509,34 @@ public class ZipSigner signature.update(signatureFileBytes); byte[] signatureBytes = signature.sign(); - out.write( keySet.getSigBlockTemplate()); - out.write( signatureBytes); + out.write(keySet.getSigBlockTemplate()); + out.write(signatureBytes); if (getLogger().isDebugEnabled()) { MessageDigest md = MessageDigest.getInstance("SHA1"); - md.update( signatureFileBytes); + md.update(signatureFileBytes); byte[] sfDigest = md.digest(); - getLogger().debug( "Sig File SHA1: \n" + HexDumpEncoder.encode( sfDigest)); + getLogger().debug("Sig File SHA1: \n" + HexDumpEncoder.encode(sfDigest)); - getLogger().debug( "Signature: \n" + HexDumpEncoder.encode(signatureBytes)); + getLogger().debug("Signature: \n" + HexDumpEncoder.encode(signatureBytes)); Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); cipher.init(Cipher.DECRYPT_MODE, keySet.getPublicKey()); - byte[] tmpData = cipher.doFinal( signatureBytes); - getLogger().debug( "Signature Decrypted: \n" + HexDumpEncoder.encode(tmpData)); + byte[] tmpData = cipher.doFinal(signatureBytes); + getLogger().debug("Signature Decrypted: \n" + HexDumpEncoder.encode(tmpData)); } - } - else { + } else { try { byte[] sigBlock = null; // Use reflection to call the optional generator. Class generatorClass = Class.forName("kellinwood.security.zipsigner.optional.SignatureBlockGenerator"); Method generatorMethod = generatorClass.getMethod("generate", KeySet.class, (new byte[1]).getClass()); - sigBlock = (byte[])generatorMethod.invoke(null, keySet, signatureFileBytes); + sigBlock = (byte[]) generatorMethod.invoke(null, keySet, signatureFileBytes); out.write(sigBlock); } catch (Exception x) { - throw new RuntimeException(x.getMessage(),x); + throw new RuntimeException(x.getMessage(), x); } } } @@ -527,9 +547,8 @@ public class ZipSigner * reduce variation in the output file and make incremental OTAs * more efficient. */ - private void copyFiles(Manifest manifest, Map input, ZipOutput output, long timestamp) - throws IOException - { + private void copyFiles(Manifest manifest, Map input, ZipOutput output, long timestamp) + throws IOException { Map entries = manifest.getEntries(); List names = new ArrayList(entries.keySet()); Collections.sort(names); @@ -546,15 +565,14 @@ public class ZipSigner } /** - * Copy all the files from input to output. + * Copy all the files from input to output. */ - private void copyFiles(Map input, ZipOutput output) - throws IOException - { + private void copyFiles(Map input, ZipOutput output) + throws IOException { int i = 1; for (ZioEntry inEntry : input.values()) { if (canceled) break; - progressHelper.progress( ProgressEvent.PRORITY_NORMAL, resourceAdapter.getString(ResourceAdapter.Item.COPYING_ZIP_ENTRY, i, input.size())); + progressHelper.progress(ProgressEvent.PRORITY_NORMAL, resourceAdapter.getString(ResourceAdapter.Item.COPYING_ZIP_ENTRY, i, input.size())); i += 1; output.write(inEntry); } @@ -563,30 +581,28 @@ public class ZipSigner /** * @deprecated - use the version that takes the passwords as char[] */ - public void signZip( URL keystoreURL, - String keystoreType, - String keystorePw, - String certAlias, - String certPw, - String inputZipFilename, - String outputZipFilename) - throws ClassNotFoundException, IllegalAccessException, InstantiationException, - IOException, GeneralSecurityException - { - signZip( keystoreURL, keystoreType, keystorePw.toCharArray(), certAlias, certPw.toCharArray(), "SHA1withRSA", inputZipFilename, outputZipFilename); + public void signZip(URL keystoreURL, + String keystoreType, + String keystorePw, + String certAlias, + String certPw, + String inputZipFilename, + String outputZipFilename) + throws ClassNotFoundException, IllegalAccessException, InstantiationException, + IOException, GeneralSecurityException { + signZip(keystoreURL, keystoreType, keystorePw.toCharArray(), certAlias, certPw.toCharArray(), "SHA1withRSA", inputZipFilename, outputZipFilename); } - public void signZip( URL keystoreURL, - String keystoreType, - char[] keystorePw, - String certAlias, - char[] certPw, - String signatureAlgorithm, - String inputZipFilename, - String outputZipFilename) - throws ClassNotFoundException, IllegalAccessException, InstantiationException, - IOException, GeneralSecurityException - { + public void signZip(URL keystoreURL, + String keystoreType, + char[] keystorePw, + String certAlias, + char[] certPw, + String signatureAlgorithm, + String inputZipFilename, + String outputZipFilename) + throws ClassNotFoundException, IllegalAccessException, InstantiationException, + IOException, GeneralSecurityException { InputStream keystoreStream = null; @@ -598,115 +614,109 @@ public class ZipSigner keystoreStream = keystoreURL.openStream(); keystore.load(keystoreStream, keystorePw); Certificate cert = keystore.getCertificate(certAlias); - X509Certificate publicKey = (X509Certificate)cert; + X509Certificate publicKey = (X509Certificate) cert; Key key = keystore.getKey(certAlias, certPw); - PrivateKey privateKey = (PrivateKey)key; - - setKeys( "custom", publicKey, privateKey, signatureAlgorithm, null); + PrivateKey privateKey = (PrivateKey) key; - signZip( inputZipFilename, outputZipFilename); - } - finally { + setKeys("custom", publicKey, privateKey, signatureAlgorithm, null); + + signZip(inputZipFilename, outputZipFilename); + } finally { if (keystoreStream != null) keystoreStream.close(); } } - - - /** Sign the input with the default test key and certificate. - * Save result to output file. + /** + * Sign the input with the default test key and certificate. + * Save result to output file. */ - public void signZip( Map zioEntries, String outputZipFilename) - throws IOException, GeneralSecurityException - { - progressHelper.initProgress(); - signZip( zioEntries, new FileOutputStream(outputZipFilename), outputZipFilename); + public void signZip(Map zioEntries, String outputZipFilename) + throws IOException, GeneralSecurityException { + progressHelper.initProgress(); + signZip(zioEntries, new FileOutputStream(outputZipFilename), outputZipFilename); } - - /** Sign the file using the given public key cert, private key, - * and signature block template. The signature block template - * parameter may be null, but if so - * android-sun-jarsign-support.jar must be in the classpath. + + /** + * Sign the file using the given public key cert, private key, + * and signature block template. The signature block template + * parameter may be null, but if so + * android-sun-jarsign-support.jar must be in the classpath. */ - public void signZip( String inputZipFilename, String outputZipFilename) - throws IOException, GeneralSecurityException - { - File inFile = new File( inputZipFilename).getCanonicalFile(); - File outFile = new File( outputZipFilename).getCanonicalFile(); - - if (inFile.equals(outFile)) { - throw new IllegalArgumentException( resourceAdapter.getString(ResourceAdapter.Item.INPUT_SAME_AS_OUTPUT_ERROR)); - } + public void signZip(String inputZipFilename, String outputZipFilename) + throws IOException, GeneralSecurityException { + File inFile = new File(inputZipFilename).getCanonicalFile(); + File outFile = new File(outputZipFilename).getCanonicalFile(); + + if (inFile.equals(outFile)) { + throw new IllegalArgumentException(resourceAdapter.getString(ResourceAdapter.Item.INPUT_SAME_AS_OUTPUT_ERROR)); + } + + progressHelper.initProgress(); + progressHelper.progress(ProgressEvent.PRORITY_IMPORTANT, resourceAdapter.getString(ResourceAdapter.Item.PARSING_CENTRAL_DIRECTORY)); - progressHelper.initProgress(); - progressHelper.progress( ProgressEvent.PRORITY_IMPORTANT, resourceAdapter.getString(ResourceAdapter.Item.PARSING_CENTRAL_DIRECTORY)); - ZipInput input = null; OutputStream outStream = null; try { - input = ZipInput.read( inputZipFilename); - outStream = new FileOutputStream( outputZipFilename); + input = ZipInput.read(inputZipFilename); + outStream = new FileOutputStream(outputZipFilename); signZip(input.getEntries(), outStream, outputZipFilename); - } - finally { - if(input != null) input.close(); - if(outStream != null) outStream.close(); + } finally { + if (input != null) input.close(); + if (outStream != null) outStream.close(); } } - - /** Sign the - * and signature block template. The signature block template - * parameter may be null, but if so - * android-sun-jarsign-support.jar must be in the classpath. + + /** + * Sign the + * and signature block template. The signature block template + * parameter may be null, but if so + * android-sun-jarsign-support.jar must be in the classpath. */ - public void signZip( Map zioEntries, OutputStream outputStream, String outputZipFilename) - throws IOException, GeneralSecurityException - { - boolean debug = getLogger().isDebugEnabled(); - + public void signZip(Map zioEntries, OutputStream outputStream, String outputZipFilename) + throws IOException, GeneralSecurityException { + boolean debug = getLogger().isDebugEnabled(); + progressHelper.initProgress(); if (keySet == null) { - if (!keymode.startsWith(MODE_AUTO)) + if (!keymode.startsWith(MODE_AUTO)) throw new IllegalStateException("No keys configured for signing the file!"); - + // Auto-determine which keys to use - String keyName = this.autoDetectKey( keymode, zioEntries); - if (keyName == null) - throw new AutoKeyException( resourceAdapter.getString(ResourceAdapter.Item.AUTO_KEY_SELECTION_ERROR, new File( outputZipFilename).getName())); - + String keyName = this.autoDetectKey(keymode, zioEntries); + if (keyName == null) + throw new AutoKeyException(resourceAdapter.getString(ResourceAdapter.Item.AUTO_KEY_SELECTION_ERROR, new File(outputZipFilename).getName())); + autoKeyObservable.notifyObservers(keyName); - loadKeys( keyName); - + loadKeys(keyName); + } - ZipOutput zipOutput = null; try { - zipOutput = new ZipOutput( outputStream); + zipOutput = new ZipOutput(outputStream); if (KEY_NONE.equals(keySet.getName())) { progressHelper.setProgressTotalItems(zioEntries.size()); - progressHelper.setProgressCurrentItem(0); + progressHelper.setProgressCurrentItem(0); copyFiles(zioEntries, zipOutput); return; } - + // Calculate total steps to complete for accurate progress percentages. int progressTotalItems = 0; - for (ZioEntry entry: zioEntries.values()) { + for (ZioEntry entry : zioEntries.values()) { String name = entry.getName(); if (!entry.isDirectory() && !name.equals(JarFile.MANIFEST_NAME) && !name.equals(CERT_SF_NAME) && !name.equals(CERT_RSA_NAME) && (stripPattern == null || - !stripPattern.matcher(name).matches())) - { + !stripPattern.matcher(name).matches())) { progressTotalItems += 3; // digest for manifest, digest in sig file, copy data } } @@ -716,12 +726,12 @@ public class ZipSigner // Assume the certificate is valid for at least an hour. long timestamp = keySet.getPublicKey().getNotBefore().getTime() + 3600L * 1000; - + // MANIFEST.MF // progress(ProgressEvent.PRORITY_NORMAL, JarFile.MANIFEST_NAME); Manifest manifest = addDigestsToManifest(zioEntries); if (canceled) return; - ZioEntry ze = new ZioEntry( JarFile.MANIFEST_NAME); + ZioEntry ze = new ZioEntry(JarFile.MANIFEST_NAME); ze.setTime(timestamp); manifest.write(ze.getOutputStream()); zipOutput.write(ze); @@ -730,62 +740,57 @@ public class ZipSigner // CERT.SF ze = new ZioEntry(CERT_SF_NAME); ze.setTime(timestamp); - + ByteArrayOutputStream out = new ByteArrayOutputStream(); generateSignatureFile(manifest, out); if (canceled) return; byte[] sfBytes = out.toByteArray(); if (debug) { - getLogger().debug( "Signature File: \n" + new String( sfBytes) + "\n" + - HexDumpEncoder.encode( sfBytes)); + getLogger().debug("Signature File: \n" + new String(sfBytes) + "\n" + + HexDumpEncoder.encode(sfBytes)); } ze.getOutputStream().write(sfBytes); zipOutput.write(ze); // CERT.RSA - progressHelper.progress( ProgressEvent.PRORITY_NORMAL, resourceAdapter.getString(ResourceAdapter.Item.GENERATING_SIGNATURE_BLOCK)); + progressHelper.progress(ProgressEvent.PRORITY_NORMAL, resourceAdapter.getString(ResourceAdapter.Item.GENERATING_SIGNATURE_BLOCK)); ze = new ZioEntry(CERT_RSA_NAME); ze.setTime(timestamp); writeSignatureBlock(keySet, sfBytes, ze.getOutputStream()); - zipOutput.write( ze); + zipOutput.write(ze); if (canceled) return; // Everything else copyFiles(manifest, zioEntries, zipOutput, timestamp); if (canceled) return; - - } - finally { + + } finally { if (zipOutput != null) zipOutput.close(); if (canceled) { try { - if (outputZipFilename != null) new File( outputZipFilename).delete(); - } - catch (Throwable t) { - getLogger().warning( t.getClass().getName() + ":" + t.getMessage()); + if (outputZipFilename != null) new File(outputZipFilename).delete(); + } catch (Throwable t) { + getLogger().warning(t.getClass().getName() + ":" + t.getMessage()); } } } } - - public void addProgressListener( ProgressListener l) - { + + public void addProgressListener(ProgressListener l) { progressHelper.addProgressListener(l); } - public synchronized void removeProgressListener( ProgressListener l) - { + public synchronized void removeProgressListener(ProgressListener l) { progressHelper.removeProgressListener(l); - } + } - - public static class AutoKeyObservable extends Observable - { + + public static class AutoKeyObservable extends Observable { @Override public void notifyObservers(Object arg) { super.setChanged(); super.notifyObservers(arg); } - + } } \ No newline at end of file diff --git a/app/src/main/java/kellinwood/security/zipsigner/optional/CertCreator.java b/app/src/main/java/kellinwood/security/zipsigner/optional/CertCreator.java index 1ee75051c..3cf27a9ff 100644 --- a/app/src/main/java/kellinwood/security/zipsigner/optional/CertCreator.java +++ b/app/src/main/java/kellinwood/security/zipsigner/optional/CertCreator.java @@ -1,3 +1,4 @@ + package kellinwood.security.zipsigner.optional; import kellinwood.security.zipsigner.KeySet; @@ -7,7 +8,10 @@ import org.bouncycastle.x509.X509V3CertificateGenerator; import java.io.File; import java.io.IOException; import java.math.BigInteger; -import java.security.*; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.KeyStore; +import java.security.SecureRandom; import java.security.cert.X509Certificate; import java.util.Date; @@ -17,84 +21,83 @@ import java.util.Date; */ public class CertCreator { - /** Creates a new keystore and self-signed key. The key will have the same password as the key, and will be - * RSA 2048, with the cert signed using SHA1withRSA. The certificate will have a validity of - * 30 years). + /** + * Creates a new keystore and self-signed key. The key will have the same password as the key, and will be + * RSA 2048, with the cert signed using SHA1withRSA. The certificate will have a validity of + * 30 years). * - * @param storePath - pathname of the new keystore file - * @param password - keystore and key password - * @param keyName - the new key will have this as its alias within the keystore + * @param storePath - pathname of the new keystore file + * @param password - keystore and key password + * @param keyName - the new key will have this as its alias within the keystore * @param distinguishedNameValues - contains Country, State, Locality,...,Common Name, etc. */ - public static void createKeystoreAndKey( String storePath, char[] password, - String keyName, DistinguishedNameValues distinguishedNameValues) - { + public static void createKeystoreAndKey(String storePath, char[] password, + String keyName, DistinguishedNameValues distinguishedNameValues) { createKeystoreAndKey(storePath, password, "RSA", 2048, keyName, password, "SHA1withRSA", 30, - distinguishedNameValues); + distinguishedNameValues); } - public static KeySet createKeystoreAndKey( String storePath, char[] storePass, - String keyAlgorithm, int keySize, String keyName, char[] keyPass, - String certSignatureAlgorithm, int certValidityYears, DistinguishedNameValues distinguishedNameValues) { + public static KeySet createKeystoreAndKey(String storePath, char[] storePass, + String keyAlgorithm, int keySize, String keyName, char[] keyPass, + String certSignatureAlgorithm, int certValidityYears, DistinguishedNameValues distinguishedNameValues) { try { KeySet keySet = createKey(keyAlgorithm, keySize, keyName, certSignatureAlgorithm, certValidityYears, - distinguishedNameValues); + distinguishedNameValues); KeyStore privateKS = KeyStoreFileManager.createKeyStore(storePath, storePass); privateKS.setKeyEntry(keyName, keySet.getPrivateKey(), - keyPass, - new java.security.cert.Certificate[]{keySet.getPublicKey()}); + keyPass, + new java.security.cert.Certificate[]{keySet.getPublicKey()}); File sfile = new File(storePath); if (sfile.exists()) { throw new IOException("File already exists: " + storePath); } - KeyStoreFileManager.writeKeyStore( privateKS, storePath, storePass); + KeyStoreFileManager.writeKeyStore(privateKS, storePath, storePass); return keySet; } catch (RuntimeException x) { throw x; - } catch ( Exception x) { - throw new RuntimeException( x.getMessage(), x); - } - } - - /** Create a new key and store it in an existing keystore. - * - */ - public static KeySet createKey( String storePath, char[] storePass, - String keyAlgorithm, int keySize, String keyName, char[] keyPass, - String certSignatureAlgorithm, int certValidityYears, - DistinguishedNameValues distinguishedNameValues) { - try { - - KeySet keySet = createKey(keyAlgorithm, keySize, keyName, certSignatureAlgorithm, certValidityYears, - distinguishedNameValues); - - KeyStore privateKS = KeyStoreFileManager.loadKeyStore(storePath, storePass); - - privateKS.setKeyEntry(keyName, keySet.getPrivateKey(), - keyPass, - new java.security.cert.Certificate[]{keySet.getPublicKey()}); - - KeyStoreFileManager.writeKeyStore( privateKS, storePath, storePass); - - return keySet; - - } catch (RuntimeException x) { - throw x; - } catch ( Exception x) { + } catch (Exception x) { throw new RuntimeException(x.getMessage(), x); } } - public static KeySet createKey( String keyAlgorithm, int keySize, String keyName, - String certSignatureAlgorithm, int certValidityYears, DistinguishedNameValues distinguishedNameValues) - { + /** + * Create a new key and store it in an existing keystore. + */ + public static KeySet createKey(String storePath, char[] storePass, + String keyAlgorithm, int keySize, String keyName, char[] keyPass, + String certSignatureAlgorithm, int certValidityYears, + DistinguishedNameValues distinguishedNameValues) { + try { + + KeySet keySet = createKey(keyAlgorithm, keySize, keyName, certSignatureAlgorithm, certValidityYears, + distinguishedNameValues); + + KeyStore privateKS = KeyStoreFileManager.loadKeyStore(storePath, storePass); + + privateKS.setKeyEntry(keyName, keySet.getPrivateKey(), + keyPass, + new java.security.cert.Certificate[]{keySet.getPublicKey()}); + + KeyStoreFileManager.writeKeyStore(privateKS, storePath, storePass); + + return keySet; + + } catch (RuntimeException x) { + throw x; + } catch (Exception x) { + throw new RuntimeException(x.getMessage(), x); + } + } + + public static KeySet createKey(String keyAlgorithm, int keySize, String keyName, + String certSignatureAlgorithm, int certValidityYears, DistinguishedNameValues distinguishedNameValues) { try { KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(keyAlgorithm); keyPairGenerator.initialize(keySize); @@ -110,15 +113,15 @@ public class CertCreator { serialNumber = BigInteger.valueOf(new SecureRandom().nextInt()); } v3CertGen.setSerialNumber(serialNumber); - v3CertGen.setIssuerDN( principal); + v3CertGen.setIssuerDN(principal); v3CertGen.setNotBefore(new Date(System.currentTimeMillis() - 1000L * 60L * 60L * 24L * 30L)); - v3CertGen.setNotAfter(new Date(System.currentTimeMillis() + (1000L * 60L * 60L * 24L * 366L * (long)certValidityYears))); + v3CertGen.setNotAfter(new Date(System.currentTimeMillis() + (1000L * 60L * 60L * 24L * 366L * (long) certValidityYears))); v3CertGen.setSubjectDN(principal); v3CertGen.setPublicKey(KPair.getPublic()); v3CertGen.setSignatureAlgorithm(certSignatureAlgorithm); - X509Certificate PKCertificate = v3CertGen.generate(KPair.getPrivate(),"BC"); + X509Certificate PKCertificate = v3CertGen.generate(KPair.getPrivate(), "BC"); KeySet keySet = new KeySet(); keySet.setName(keyName); diff --git a/app/src/main/java/kellinwood/security/zipsigner/optional/CustomKeySigner.java b/app/src/main/java/kellinwood/security/zipsigner/optional/CustomKeySigner.java index e6e0e7033..0631461f4 100644 --- a/app/src/main/java/kellinwood/security/zipsigner/optional/CustomKeySigner.java +++ b/app/src/main/java/kellinwood/security/zipsigner/optional/CustomKeySigner.java @@ -1,3 +1,4 @@ + package kellinwood.security.zipsigner.optional; import kellinwood.security.zipsigner.ZipSigner; @@ -12,26 +13,27 @@ import java.security.cert.X509Certificate; */ public class CustomKeySigner { - /** KeyStore-type agnostic. This method will sign the zip file, automatically handling JKS or BKS keystores. */ - public static void signZip( ZipSigner zipSigner, - String keystorePath, - char[] keystorePw, - String certAlias, - char[] certPw, - String signatureAlgorithm, - String inputZipFilename, - String outputZipFilename) - throws Exception - { + /** + * KeyStore-type agnostic. This method will sign the zip file, automatically handling JKS or BKS keystores. + */ + public static void signZip(ZipSigner zipSigner, + String keystorePath, + char[] keystorePw, + String certAlias, + char[] certPw, + String signatureAlgorithm, + String inputZipFilename, + String outputZipFilename) + throws Exception { zipSigner.issueLoadingCertAndKeysProgressEvent(); - KeyStore keystore = KeyStoreFileManager.loadKeyStore( keystorePath, keystorePw); + KeyStore keystore = KeyStoreFileManager.loadKeyStore(keystorePath, keystorePw); Certificate cert = keystore.getCertificate(certAlias); - X509Certificate publicKey = (X509Certificate)cert; + X509Certificate publicKey = (X509Certificate) cert; Key key = keystore.getKey(certAlias, certPw); - PrivateKey privateKey = (PrivateKey)key; + PrivateKey privateKey = (PrivateKey) key; - zipSigner.setKeys( "custom", publicKey, privateKey, signatureAlgorithm, null); - zipSigner.signZip( inputZipFilename, outputZipFilename); + zipSigner.setKeys("custom", publicKey, privateKey, signatureAlgorithm, null); + zipSigner.signZip(inputZipFilename, outputZipFilename); } } diff --git a/app/src/main/java/kellinwood/security/zipsigner/optional/DistinguishedNameValues.java b/app/src/main/java/kellinwood/security/zipsigner/optional/DistinguishedNameValues.java index 841629388..58f91cb52 100644 --- a/app/src/main/java/kellinwood/security/zipsigner/optional/DistinguishedNameValues.java +++ b/app/src/main/java/kellinwood/security/zipsigner/optional/DistinguishedNameValues.java @@ -1,10 +1,10 @@ + package kellinwood.security.zipsigner.optional; import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.x500.style.BCStyle; import org.bouncycastle.jce.X509Principal; -import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.Vector; @@ -13,61 +13,61 @@ import java.util.Vector; /** * Helper class for dealing with the distinguished name RDNs. */ -public class DistinguishedNameValues extends LinkedHashMap { - +public class DistinguishedNameValues extends LinkedHashMap { + public DistinguishedNameValues() { - put(BCStyle.C,null); - put(BCStyle.ST,null); - put(BCStyle.L,null); - put(BCStyle.STREET,null); - put(BCStyle.O,null); - put(BCStyle.OU,null); - put(BCStyle.CN,null); + put(BCStyle.C, null); + put(BCStyle.ST, null); + put(BCStyle.L, null); + put(BCStyle.STREET, null); + put(BCStyle.O, null); + put(BCStyle.OU, null); + put(BCStyle.CN, null); } public String put(ASN1ObjectIdentifier oid, String value) { if (value != null && value.equals("")) value = null; - if (containsKey(oid)) super.put(oid,value); // preserve original ordering + if (containsKey(oid)) super.put(oid, value); // preserve original ordering else { - super.put(oid,value); + super.put(oid, value); // String cn = remove(BCStyle.CN); // CN will always be last. // put(BCStyle.CN,cn); } return value; } - public void setCountry( String country) { - put(BCStyle.C,country); - } - - public void setState( String state) { - put(BCStyle.ST,state); - } - - public void setLocality( String locality) { - put(BCStyle.L,locality); + public void setCountry(String country) { + put(BCStyle.C, country); } - public void setStreet( String street) { - put( BCStyle.STREET, street); + public void setState(String state) { + put(BCStyle.ST, state); } - public void setOrganization( String organization) { - put(BCStyle.O,organization); + public void setLocality(String locality) { + put(BCStyle.L, locality); } - - public void setOrganizationalUnit( String organizationalUnit) { - put(BCStyle.OU,organizationalUnit); + + public void setStreet(String street) { + put(BCStyle.STREET, street); } - - public void setCommonName( String commonName) { - put(BCStyle.CN,commonName); + + public void setOrganization(String organization) { + put(BCStyle.O, organization); + } + + public void setOrganizationalUnit(String organizationalUnit) { + put(BCStyle.OU, organizationalUnit); + } + + public void setCommonName(String commonName) { + put(BCStyle.CN, commonName); } @Override public int size() { int result = 0; - for( String value : values()) { + for (String value : values()) { if (value != null) result += 1; } return result; @@ -77,13 +77,13 @@ public class DistinguishedNameValues extends LinkedHashMap oids = new Vector(); Vector values = new Vector(); - for (Map.Entry entry : entrySet()) { + for (Map.Entry entry : entrySet()) { if (entry.getValue() != null && !entry.getValue().equals("")) { - oids.add( entry.getKey()); - values.add( entry.getValue()); + oids.add(entry.getKey()); + values.add(entry.getValue()); } } - return new X509Principal(oids,values); + return new X509Principal(oids, values); } } diff --git a/app/src/main/java/kellinwood/security/zipsigner/optional/Fingerprint.java b/app/src/main/java/kellinwood/security/zipsigner/optional/Fingerprint.java index 03beb14d4..c9d4be21b 100644 --- a/app/src/main/java/kellinwood/security/zipsigner/optional/Fingerprint.java +++ b/app/src/main/java/kellinwood/security/zipsigner/optional/Fingerprint.java @@ -1,3 +1,4 @@ + package kellinwood.security.zipsigner.optional; import kellinwood.logging.LoggerInterface; @@ -15,34 +16,34 @@ public class Fingerprint { static LoggerInterface logger = LoggerManager.getLogger(Fingerprint.class.getName()); - static byte[] calcDigest( String algorithm, byte[] encodedCert) { + static byte[] calcDigest(String algorithm, byte[] encodedCert) { byte[] result = null; try { MessageDigest messageDigest = MessageDigest.getInstance(algorithm); messageDigest.update(encodedCert); result = messageDigest.digest(); } catch (Exception x) { - logger.error(x.getMessage(),x); + logger.error(x.getMessage(), x); } return result; } - public static String hexFingerprint( String algorithm, byte[] encodedCert) { + public static String hexFingerprint(String algorithm, byte[] encodedCert) { try { - byte[] digest = calcDigest(algorithm,encodedCert); + byte[] digest = calcDigest(algorithm, encodedCert); if (digest == null) return null; HexTranslator hexTranslator = new HexTranslator(); byte[] hex = new byte[digest.length * 2]; hexTranslator.encode(digest, 0, digest.length, hex, 0); StringBuilder builder = new StringBuilder(); for (int i = 0; i < hex.length; i += 2) { - builder.append((char)hex[i]); - builder.append((char)hex[i+1]); + builder.append((char) hex[i]); + builder.append((char) hex[i + 1]); if (i != (hex.length - 2)) builder.append(':'); } return builder.toString().toUpperCase(); } catch (Exception x) { - logger.error(x.getMessage(),x); + logger.error(x.getMessage(), x); } return null; } @@ -55,14 +56,14 @@ public class Fingerprint { // // } - public static String base64Fingerprint( String algorithm, byte[] encodedCert) { + public static String base64Fingerprint(String algorithm, byte[] encodedCert) { String result = null; try { - byte[] digest = calcDigest(algorithm,encodedCert); + byte[] digest = calcDigest(algorithm, encodedCert); if (digest == null) return result; return Base64.encode(digest); } catch (Exception x) { - logger.error(x.getMessage(),x); + logger.error(x.getMessage(), x); } return result; } diff --git a/app/src/main/java/kellinwood/security/zipsigner/optional/JKS.java b/app/src/main/java/kellinwood/security/zipsigner/optional/JKS.java index 4ba822bb5..024a46aa8 100644 --- a/app/src/main/java/kellinwood/security/zipsigner/optional/JKS.java +++ b/app/src/main/java/kellinwood/security/zipsigner/optional/JKS.java @@ -23,15 +23,17 @@ power to enforce restrictions on reverse-engineering of their software, and it is irresponsible for them to claim they can. */ + package kellinwood.security.zipsigner.optional; +import javax.crypto.EncryptedPrivateKeyInfo; +import javax.crypto.spec.SecretKeySpec; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.DataOutputStream; -import java.io.InputStream; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; - import java.security.DigestInputStream; import java.security.DigestOutputStream; import java.security.Key; @@ -42,29 +44,24 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.UnrecoverableKeyException; - import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; - import java.util.Date; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.Vector; -import javax.crypto.EncryptedPrivateKeyInfo; -import javax.crypto.spec.SecretKeySpec; - /** * This is an implementation of Sun's proprietary key store * algorithm, called "JKS" for "Java Key Store". This implementation was * created entirely through reverse-engineering. - * + *

*

The format of JKS files is, from the start of the file: - * + *

*

    *
  1. Magic bytes. This is a four-byte integer, in big-endian byte * order, equal to 0xFEEDFEED.
  2. @@ -74,46 +71,46 @@ import javax.crypto.spec.SecretKeySpec; *
  3. The number of entrires in this keystore, as a four-byte * integer. Call this value n
  4. *
  5. Then, n times: - *
      - *
    1. The entry type, a four-byte int. The value 1 denotes a private - * key entry, and 2 denotes a trusted certificate.
    2. - *
    3. The entry's alias, formatted as strings such as those written - * by DataOutput.writeUTF(String).
    4. - *
    5. An eight-byte integer, representing the entry's creation date, - * in milliseconds since the epoch. - * - *

      Then, if the entry is a private key entry: - *

        - *
      1. The size of the encoded key as a four-byte int, then that - * number of bytes. The encoded key is the DER encoded bytes of the - * EncryptedPrivateKeyInfo structure (the - * encryption algorithm is discussed later).
      2. - *
      3. A four-byte integer, followed by that many encoded - * certificates, encoded as described in the trusted certificates - * section.
      4. - *
      - * - *

      Otherwise, the entry is a trusted certificate, which is encoded - * as the name of the encoding algorithm (e.g. X.509), encoded the same - * way as alias names. Then, a four-byte integer representing the size - * of the encoded certificate, then that many bytes representing the - * encoded certificate (e.g. the DER bytes in the case of X.509). - *

    6. - *
    + *
      + *
    1. The entry type, a four-byte int. The value 1 denotes a private + * key entry, and 2 denotes a trusted certificate.
    2. + *
    3. The entry's alias, formatted as strings such as those written + * by DataOutput.writeUTF(String).
    4. + *
    5. An eight-byte integer, representing the entry's creation date, + * in milliseconds since the epoch. + *

      + *

      Then, if the entry is a private key entry: + *

        + *
      1. The size of the encoded key as a four-byte int, then that + * number of bytes. The encoded key is the DER encoded bytes of the + * EncryptedPrivateKeyInfo structure (the + * encryption algorithm is discussed later).
      2. + *
      3. A four-byte integer, followed by that many encoded + * certificates, encoded as described in the trusted certificates + * section.
      4. + *
      + *

      + *

      Otherwise, the entry is a trusted certificate, which is encoded + * as the name of the encoding algorithm (e.g. X.509), encoded the same + * way as alias names. Then, a four-byte integer representing the size + * of the encoded certificate, then that many bytes representing the + * encoded certificate (e.g. the DER bytes in the case of X.509). + *

    6. + *
    *
  6. *
  7. Then, the signature.
  8. *
* * * - * + *

*

(See this file for some idea of how I * was able to figure out these algorithms)

- * + *

*

Decrypting the key works as follows: - * + *

*

    *
  1. The key length is the length of the ciphertext minus 40. The * encrypted key, ekey, is the middle bytes of the @@ -129,36 +126,37 @@ import javax.crypto.spec.SecretKeySpec; * the last 20 bytes of the ciphertext, output FAIL. Otherwise, * output key.
  2. *
- * + *

*

The signature is defined as SHA-1(UTF-16BE(password) + * US_ASCII("Mighty Aphrodite") + encoded_keystore) (yup, Sun * engineers are just that clever). - * + *

*

(Above, SHA-1 denotes the secure hash algorithm, UTF-16BE the * big-endian byte representation of a UTF-16 string, and US_ASCII the * ASCII byte representation of the string.) - * + *

*

The source code of this class should be available in the file JKS.java. * * @author Casey Marshall (rsdio@metastatic.org) - * - * Changes by Ken Ellinwood: - * ** Fixed a NullPointerException in engineLoad(). This method must return gracefully if the keystore input stream is null. - * ** engineGetCertificateEntry() was updated to return the first cert in the chain for private key entries. - * ** Lowercase the alias names, otherwise keytool chokes on the file created by this code. - * ** Fixed the integrity check in engineLoad(), previously the exception was never thrown regardless of password value. + *

+ * Changes by Ken Ellinwood: + * ** Fixed a NullPointerException in engineLoad(). This method must return gracefully if the keystore input stream is null. + * ** engineGetCertificateEntry() was updated to return the first cert in the chain for private key entries. + * ** Lowercase the alias names, otherwise keytool chokes on the file created by this code. + * ** Fixed the integrity check in engineLoad(), previously the exception was never thrown regardless of password value. */ -public class JKS extends KeyStoreSpi -{ +public class JKS extends KeyStoreSpi { // Constants and fields. // ------------------------------------------------------------------------ - /** Ah, Sun. So goddamned clever with those magic bytes. */ + /** + * Ah, Sun. So goddamned clever with those magic bytes. + */ private static final int MAGIC = 0xFEEDFEED; - private static final int PRIVATE_KEY = 1; + private static final int PRIVATE_KEY = 1; private static final int TRUSTED_CERT = 2; private final Vector aliases; @@ -170,8 +168,7 @@ public class JKS extends KeyStoreSpi // Constructor. // ------------------------------------------------------------------------ - public JKS() - { + public JKS() { super(); aliases = new Vector(); trustedCerts = new HashMap(); @@ -185,41 +182,33 @@ public class JKS extends KeyStoreSpi // ------------------------------------------------------------------------ public Key engineGetKey(String alias, char[] password) - throws NoSuchAlgorithmException, UnrecoverableKeyException - { + throws NoSuchAlgorithmException, UnrecoverableKeyException { alias = alias.toLowerCase(); if (!privateKeys.containsKey(alias)) return null; byte[] key = decryptKey((byte[]) privateKeys.get(alias), - charsToBytes(password)); + charsToBytes(password)); Certificate[] chain = engineGetCertificateChain(alias); - if (chain.length > 0) - { - try - { + if (chain.length > 0) { + try { // Private and public keys MUST have the same algorithm. KeyFactory fact = KeyFactory.getInstance( - chain[0].getPublicKey().getAlgorithm()); + chain[0].getPublicKey().getAlgorithm()); return fact.generatePrivate(new PKCS8EncodedKeySpec(key)); - } - catch (InvalidKeySpecException x) - { + } catch (InvalidKeySpecException x) { throw new UnrecoverableKeyException(x.getMessage()); } - } - else + } else return new SecretKeySpec(key, alias); } - public Certificate[] engineGetCertificateChain(String alias) - { + public Certificate[] engineGetCertificateChain(String alias) { alias = alias.toLowerCase(); return (Certificate[]) certChains.get(alias); } - public Certificate engineGetCertificate(String alias) - { + public Certificate engineGetCertificate(String alias) { alias = alias.toLowerCase(); if (engineIsKeyEntry(alias)) { Certificate[] certChain = (Certificate[]) certChains.get(alias); @@ -228,8 +217,7 @@ public class JKS extends KeyStoreSpi return (Certificate) trustedCerts.get(alias); } - public Date engineGetCreationDate(String alias) - { + public Date engineGetCreationDate(String alias) { alias = alias.toLowerCase(); return (Date) dates.get(alias); } @@ -237,8 +225,7 @@ public class JKS extends KeyStoreSpi // XXX implement writing methods. public void engineSetKeyEntry(String alias, Key key, char[] passwd, Certificate[] certChain) - throws KeyStoreException - { + throws KeyStoreException { alias = alias.toLowerCase(); if (trustedCerts.containsKey(alias)) throw new KeyStoreException("\"" + alias + " is a trusted certificate entry"); @@ -247,25 +234,20 @@ public class JKS extends KeyStoreSpi certChains.put(alias, certChain); else certChains.put(alias, new Certificate[0]); - if (!aliases.contains(alias)) - { + if (!aliases.contains(alias)) { dates.put(alias, new Date()); aliases.add(alias); } } public void engineSetKeyEntry(String alias, byte[] encodedKey, Certificate[] certChain) - throws KeyStoreException - { + throws KeyStoreException { alias = alias.toLowerCase(); if (trustedCerts.containsKey(alias)) throw new KeyStoreException("\"" + alias + "\" is a trusted certificate entry"); - try - { + try { new EncryptedPrivateKeyInfo(encodedKey); - } - catch (IOException ioe) - { + } catch (IOException ioe) { throw new KeyStoreException("encoded key is not an EncryptedPrivateKeyInfo"); } privateKeys.put(alias, encodedKey); @@ -273,67 +255,56 @@ public class JKS extends KeyStoreSpi certChains.put(alias, certChain); else certChains.put(alias, new Certificate[0]); - if (!aliases.contains(alias)) - { + if (!aliases.contains(alias)) { dates.put(alias, new Date()); aliases.add(alias); } } public void engineSetCertificateEntry(String alias, Certificate cert) - throws KeyStoreException - { + throws KeyStoreException { alias = alias.toLowerCase(); if (privateKeys.containsKey(alias)) throw new KeyStoreException("\"" + alias + "\" is a private key entry"); if (cert == null) throw new NullPointerException(); trustedCerts.put(alias, cert); - if (!aliases.contains(alias)) - { + if (!aliases.contains(alias)) { dates.put(alias, new Date()); aliases.add(alias); } } - public void engineDeleteEntry(String alias) throws KeyStoreException - { + public void engineDeleteEntry(String alias) throws KeyStoreException { alias = alias.toLowerCase(); aliases.remove(alias); } - public Enumeration engineAliases() - { + public Enumeration engineAliases() { return aliases.elements(); } - public boolean engineContainsAlias(String alias) - { + public boolean engineContainsAlias(String alias) { alias = alias.toLowerCase(); return aliases.contains(alias); } - public int engineSize() - { + public int engineSize() { return aliases.size(); } - public boolean engineIsKeyEntry(String alias) - { + public boolean engineIsKeyEntry(String alias) { alias = alias.toLowerCase(); return privateKeys.containsKey(alias); } - public boolean engineIsCertificateEntry(String alias) - { + public boolean engineIsCertificateEntry(String alias) { alias = alias.toLowerCase(); return trustedCerts.containsKey(alias); } - public String engineGetCertificateAlias(Certificate cert) - { - for (Iterator keys = trustedCerts.keySet().iterator(); keys.hasNext(); ) - { + public String engineGetCertificateAlias(Certificate cert) { + for (Iterator keys = trustedCerts.keySet().iterator(); keys.hasNext(); ) { String alias = (String) keys.next(); if (cert.equals(trustedCerts.get(alias))) return alias; @@ -342,8 +313,7 @@ public class JKS extends KeyStoreSpi } public void engineStore(OutputStream out, char[] passwd) - throws IOException, NoSuchAlgorithmException, CertificateException - { + throws IOException, NoSuchAlgorithmException, CertificateException { MessageDigest md = MessageDigest.getInstance("SHA1"); md.update(charsToBytes(passwd)); md.update("Mighty Aphrodite".getBytes("UTF-8")); @@ -351,18 +321,14 @@ public class JKS extends KeyStoreSpi dout.writeInt(MAGIC); dout.writeInt(2); dout.writeInt(aliases.size()); - for (Enumeration e = aliases.elements(); e.hasMoreElements(); ) - { + for (Enumeration e = aliases.elements(); e.hasMoreElements(); ) { String alias = (String) e.nextElement(); - if (trustedCerts.containsKey(alias)) - { + if (trustedCerts.containsKey(alias)) { dout.writeInt(TRUSTED_CERT); dout.writeUTF(alias); dout.writeLong(((Date) dates.get(alias)).getTime()); writeCert(dout, (Certificate) trustedCerts.get(alias)); - } - else - { + } else { dout.writeInt(PRIVATE_KEY); dout.writeUTF(alias); dout.writeLong(((Date) dates.get(alias)).getTime()); @@ -380,8 +346,7 @@ public class JKS extends KeyStoreSpi } public void engineLoad(InputStream in, char[] passwd) - throws IOException, NoSuchAlgorithmException, CertificateException - { + throws IOException, NoSuchAlgorithmException, CertificateException { MessageDigest md = MessageDigest.getInstance("SHA"); if (passwd != null) md.update(charsToBytes(passwd)); md.update("Mighty Aphrodite".getBytes("UTF-8")); // HAR HAR @@ -399,14 +364,12 @@ public class JKS extends KeyStoreSpi aliases.ensureCapacity(n); if (n < 0) throw new LoadKeystoreException("Malformed key store"); - for (int i = 0; i < n; i++) - { + for (int i = 0; i < n; i++) { int type = din.readInt(); String alias = din.readUTF(); aliases.add(alias); dates.put(alias, new Date(din.readLong())); - switch (type) - { + switch (type) { case PRIVATE_KEY: int len = din.readInt(); byte[] encoded = new byte[len]; @@ -442,8 +405,7 @@ public class JKS extends KeyStoreSpi // ------------------------------------------------------------------------ private static Certificate readCert(DataInputStream in) - throws IOException, CertificateException, NoSuchAlgorithmException - { + throws IOException, CertificateException, NoSuchAlgorithmException { String type = in.readUTF(); int len = in.readInt(); byte[] encoded = new byte[len]; @@ -453,8 +415,7 @@ public class JKS extends KeyStoreSpi } private static void writeCert(DataOutputStream dout, Certificate cert) - throws IOException, CertificateException - { + throws IOException, CertificateException { dout.writeUTF(cert.getType()); byte[] b = cert.getEncoded(); dout.writeInt(b.length); @@ -462,29 +423,25 @@ public class JKS extends KeyStoreSpi } private static byte[] decryptKey(byte[] encryptedPKI, byte[] passwd) - throws UnrecoverableKeyException - { - try - { + throws UnrecoverableKeyException { + try { EncryptedPrivateKeyInfo epki = - new EncryptedPrivateKeyInfo(encryptedPKI); + new EncryptedPrivateKeyInfo(encryptedPKI); byte[] encr = epki.getEncryptedData(); byte[] keystream = new byte[20]; System.arraycopy(encr, 0, keystream, 0, 20); byte[] check = new byte[20]; - System.arraycopy(encr, encr.length-20, check, 0, 20); + System.arraycopy(encr, encr.length - 20, check, 0, 20); byte[] key = new byte[encr.length - 40]; MessageDigest sha = MessageDigest.getInstance("SHA1"); int count = 0; - while (count < key.length) - { + while (count < key.length) { sha.reset(); sha.update(passwd); sha.update(keystream); sha.digest(keystream, 0, keystream.length); - for (int i = 0; i < keystream.length && count < key.length; i++) - { - key[count] = (byte) (keystream[i] ^ encr[count+20]); + for (int i = 0; i < keystream.length && count < key.length; i++) { + key[count] = (byte) (keystream[i] ^ encr[count + 20]); count++; } } @@ -494,18 +451,14 @@ public class JKS extends KeyStoreSpi if (!MessageDigest.isEqual(check, sha.digest())) throw new UnrecoverableKeyException("checksum mismatch"); return key; - } - catch (Exception x) - { + } catch (Exception x) { throw new UnrecoverableKeyException(x.getMessage()); } } private static byte[] encryptKey(Key key, byte[] passwd) - throws KeyStoreException - { - try - { + throws KeyStoreException { + try { MessageDigest sha = MessageDigest.getInstance("SHA1"); SecureRandom rand = SecureRandom.getInstance("SHA1PRNG"); byte[] k = key.getEncoded(); @@ -513,15 +466,13 @@ public class JKS extends KeyStoreSpi byte[] keystream = rand.getSeed(20); System.arraycopy(keystream, 0, encrypted, 0, 20); int count = 0; - while (count < k.length) - { + while (count < k.length) { sha.reset(); sha.update(passwd); sha.update(keystream); sha.digest(keystream, 0, keystream.length); - for (int i = 0; i < keystream.length && count < k.length; i++) - { - encrypted[count+20] = (byte) (keystream[i] ^ k[count]); + for (int i = 0; i < keystream.length && count < k.length; i++) { + encrypted[count + 20] = (byte) (keystream[i] ^ k[count]); count++; } } @@ -532,21 +483,17 @@ public class JKS extends KeyStoreSpi // 1.3.6.1.4.1.42.2.17.1.1 is Sun's private OID for this // encryption algorithm. return new EncryptedPrivateKeyInfo("1.3.6.1.4.1.42.2.17.1.1", - encrypted).getEncoded(); - } - catch (Exception x) - { + encrypted).getEncoded(); + } catch (Exception x) { throw new KeyStoreException(x.getMessage()); } } - private static byte[] charsToBytes(char[] passwd) - { + private static byte[] charsToBytes(char[] passwd) { byte[] buf = new byte[passwd.length * 2]; - for (int i = 0, j = 0; i < passwd.length; i++) - { + for (int i = 0, j = 0; i < passwd.length; i++) { buf[j++] = (byte) (passwd[i] >>> 8); - buf[j++] = (byte) passwd[i]; + buf[j++] = (byte) passwd[i]; } return buf; } diff --git a/app/src/main/java/kellinwood/security/zipsigner/optional/JksKeyStore.java b/app/src/main/java/kellinwood/security/zipsigner/optional/JksKeyStore.java index 8ecc66d38..ddda76ef6 100644 --- a/app/src/main/java/kellinwood/security/zipsigner/optional/JksKeyStore.java +++ b/app/src/main/java/kellinwood/security/zipsigner/optional/JksKeyStore.java @@ -1,3 +1,4 @@ + package kellinwood.security.zipsigner.optional; diff --git a/app/src/main/java/kellinwood/security/zipsigner/optional/KeyNameConflictException.java b/app/src/main/java/kellinwood/security/zipsigner/optional/KeyNameConflictException.java index 2e434da62..153d81c95 100644 --- a/app/src/main/java/kellinwood/security/zipsigner/optional/KeyNameConflictException.java +++ b/app/src/main/java/kellinwood/security/zipsigner/optional/KeyNameConflictException.java @@ -1,3 +1,4 @@ + package kellinwood.security.zipsigner.optional; public class KeyNameConflictException extends Exception { diff --git a/app/src/main/java/kellinwood/security/zipsigner/optional/KeyStoreFileManager.java b/app/src/main/java/kellinwood/security/zipsigner/optional/KeyStoreFileManager.java index 7ed9a512f..1123704d6 100644 --- a/app/src/main/java/kellinwood/security/zipsigner/optional/KeyStoreFileManager.java +++ b/app/src/main/java/kellinwood/security/zipsigner/optional/KeyStoreFileManager.java @@ -1,3 +1,4 @@ + package kellinwood.security.zipsigner.optional; @@ -5,27 +6,36 @@ import kellinwood.logging.LoggerInterface; import kellinwood.logging.LoggerManager; import org.bouncycastle.jce.provider.BouncyCastleProvider; -import java.io.*; -import java.security.*; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.security.Key; +import java.security.KeyStore; +import java.security.Provider; +import java.security.Security; import java.security.cert.Certificate; - /** */ public class KeyStoreFileManager { static Provider provider = new BouncyCastleProvider(); - public static Provider getProvider() { return provider; } - - public static void setProvider(Provider provider) { - if (KeyStoreFileManager.provider != null) Security.removeProvider( KeyStoreFileManager.provider.getName()); - KeyStoreFileManager.provider = provider; - Security.addProvider( provider); + public static Provider getProvider() { + return provider; } - static LoggerInterface logger = LoggerManager.getLogger( KeyStoreFileManager.class.getName()); + public static void setProvider(Provider provider) { + if (KeyStoreFileManager.provider != null) Security.removeProvider(KeyStoreFileManager.provider.getName()); + KeyStoreFileManager.provider = provider; + Security.addProvider(provider); + } + + static LoggerInterface logger = LoggerManager.getLogger(KeyStoreFileManager.class.getName()); static { // Add the bouncycastle version of the BC provider so that the implementation classes returned @@ -34,12 +44,12 @@ public class KeyStoreFileManager { } - public static KeyStore loadKeyStore( String keystorePath, String encodedPassword) - throws Exception{ + public static KeyStore loadKeyStore(String keystorePath, String encodedPassword) + throws Exception { char password[] = null; try { if (encodedPassword != null) { - password = PasswordObfuscator.getInstance().decodeKeystorePassword( keystorePath, encodedPassword); + password = PasswordObfuscator.getInstance().decodeKeystorePassword(keystorePath, encodedPassword); } return loadKeyStore(keystorePath, password); } finally { @@ -47,27 +57,24 @@ public class KeyStoreFileManager { } } - public static KeyStore createKeyStore( String keystorePath, char[] password) - throws Exception - { + public static KeyStore createKeyStore(String keystorePath, char[] password) + throws Exception { KeyStore ks = null; if (keystorePath.toLowerCase().endsWith(".bks")) { ks = KeyStore.getInstance("bks", new BouncyCastleProvider()); - } - else ks = new JksKeyStore(); + } else ks = new JksKeyStore(); ks.load(null, password); return ks; } - public static KeyStore loadKeyStore( String keystorePath, char[] password) - throws Exception - { + public static KeyStore loadKeyStore(String keystorePath, char[] password) + throws Exception { KeyStore ks = null; try { ks = new JksKeyStore(); - FileInputStream fis = new FileInputStream( keystorePath); - ks.load( fis, password); + FileInputStream fis = new FileInputStream(keystorePath); + ks.load(fis, password); fis.close(); return ks; } catch (LoadKeystoreException x) { @@ -78,8 +85,8 @@ public class KeyStoreFileManager { // logger.warning( x.getMessage(), x); try { ks = KeyStore.getInstance("bks", getProvider()); - FileInputStream fis = new FileInputStream( keystorePath); - ks.load( fis, password); + FileInputStream fis = new FileInputStream(keystorePath); + ks.load(fis, password); fis.close(); return ks; } catch (Exception e) { @@ -88,29 +95,27 @@ public class KeyStoreFileManager { } } - public static void writeKeyStore( KeyStore ks, String keystorePath, String encodedPassword) - throws Exception - { + public static void writeKeyStore(KeyStore ks, String keystorePath, String encodedPassword) + throws Exception { char password[] = null; try { - password = PasswordObfuscator.getInstance().decodeKeystorePassword( keystorePath, encodedPassword); - writeKeyStore( ks, keystorePath, password); + password = PasswordObfuscator.getInstance().decodeKeystorePassword(keystorePath, encodedPassword); + writeKeyStore(ks, keystorePath, password); } finally { if (password != null) PasswordObfuscator.flush(password); } } - public static void writeKeyStore( KeyStore ks, String keystorePath, char[] password) - throws Exception - { + public static void writeKeyStore(KeyStore ks, String keystorePath, char[] password) + throws Exception { - File keystoreFile = new File( keystorePath); + File keystoreFile = new File(keystorePath); try { if (keystoreFile.exists()) { // I've had some trouble saving new verisons of the keystore file in which the file becomes empty/corrupt. // Saving the new version to a new file and creating a backup of the old version. - File tmpFile = File.createTempFile( keystoreFile.getName(), null, keystoreFile.getParentFile()); - FileOutputStream fos = new FileOutputStream( tmpFile); + File tmpFile = File.createTempFile(keystoreFile.getName(), null, keystoreFile.getParentFile()); + FileOutputStream fos = new FileOutputStream(tmpFile); ks.store(fos, password); fos.flush(); fos.close(); @@ -125,18 +130,19 @@ public class KeyStoreFileManager { */ renameTo(tmpFile, keystoreFile); } else { - FileOutputStream fos = new FileOutputStream( keystorePath); + FileOutputStream fos = new FileOutputStream(keystorePath); ks.store(fos, password); fos.close(); } } catch (Exception x) { try { File logfile = File.createTempFile("zipsigner-error", ".log", keystoreFile.getParentFile()); - PrintWriter pw = new PrintWriter(new FileWriter( logfile)); - x.printStackTrace( pw); + PrintWriter pw = new PrintWriter(new FileWriter(logfile)); + x.printStackTrace(pw); pw.flush(); pw.close(); - } catch (Exception y) {} + } catch (Exception y) { + } throw x; } } @@ -159,15 +165,21 @@ public class KeyStoreFileManager { count += n; } } finally { - try { output.close(); } catch (IOException x) {} // Ignore + try { + output.close(); + } catch (IOException x) { + } // Ignore } } finally { - try { input.close(); } catch (IOException x) {} + try { + input.close(); + } catch (IOException x) { + } } if (srcFile.length() != destFile.length()) { throw new IOException("Failed to copy full contents from '" + - srcFile + "' to '" + destFile + "'"); + srcFile + "' to '" + destFile + "'"); } if (preserveFileDate) { destFile.setLastModified(srcFile.lastModified()); @@ -176,23 +188,20 @@ public class KeyStoreFileManager { public static void renameTo(File fromFile, File toFile) - throws IOException - { + throws IOException { copyFile(fromFile, toFile, true); if (!fromFile.delete()) throw new IOException("Failed to delete " + fromFile); } public static void deleteKey(String storePath, String storePass, String keyName) - throws Exception - { - KeyStore ks = loadKeyStore( storePath, storePass); - ks.deleteEntry( keyName); + throws Exception { + KeyStore ks = loadKeyStore(storePath, storePass); + ks.deleteEntry(keyName); writeKeyStore(ks, storePath, storePass); } - public static String renameKey( String keystorePath, String storePass, String oldKeyName, String newKeyName, String keyPass) - throws Exception - { + public static String renameKey(String keystorePath, String storePass, String oldKeyName, String newKeyName, String keyPass) + throws Exception { char[] keyPw = null; try { @@ -201,59 +210,53 @@ public class KeyStoreFileManager { if (ks.containsAlias(newKeyName)) throw new KeyNameConflictException(); - keyPw = PasswordObfuscator.getInstance().decodeAliasPassword( keystorePath, oldKeyName, keyPass); + keyPw = PasswordObfuscator.getInstance().decodeAliasPassword(keystorePath, oldKeyName, keyPass); Key key = ks.getKey(oldKeyName, keyPw); - Certificate cert = ks.getCertificate( oldKeyName); + Certificate cert = ks.getCertificate(oldKeyName); - ks.setKeyEntry(newKeyName, key, keyPw, new Certificate[] { cert}); - ks.deleteEntry( oldKeyName); + ks.setKeyEntry(newKeyName, key, keyPw, new Certificate[]{cert}); + ks.deleteEntry(oldKeyName); writeKeyStore(ks, keystorePath, storePass); return newKeyName; - } - finally { + } finally { PasswordObfuscator.flush(keyPw); } } - public static KeyStore.Entry getKeyEntry( String keystorePath, String storePass, String keyName, String keyPass) - throws Exception - { + public static KeyStore.Entry getKeyEntry(String keystorePath, String storePass, String keyName, String keyPass) + throws Exception { char[] keyPw = null; KeyStore.PasswordProtection passwordProtection = null; try { KeyStore ks = loadKeyStore(keystorePath, storePass); - keyPw = PasswordObfuscator.getInstance().decodeAliasPassword( keystorePath, keyName, keyPass); + keyPw = PasswordObfuscator.getInstance().decodeAliasPassword(keystorePath, keyName, keyPass); passwordProtection = new KeyStore.PasswordProtection(keyPw); - return ks.getEntry( keyName, passwordProtection); - } - finally { + return ks.getEntry(keyName, passwordProtection); + } finally { if (keyPw != null) PasswordObfuscator.flush(keyPw); if (passwordProtection != null) passwordProtection.destroy(); } } - public static boolean containsKey( String keystorePath, String storePass, String keyName) - throws Exception - { + public static boolean containsKey(String keystorePath, String storePass, String keyName) + throws Exception { KeyStore ks = loadKeyStore(keystorePath, storePass); - return ks.containsAlias( keyName); + return ks.containsAlias(keyName); } /** - * * @param keystorePath * @param encodedPassword * @throws Exception if the password is invalid */ - public static void validateKeystorePassword( String keystorePath, String encodedPassword) - throws Exception - { + public static void validateKeystorePassword(String keystorePath, String encodedPassword) + throws Exception { char[] password = null; try { - KeyStore ks = KeyStoreFileManager.loadKeyStore( keystorePath, encodedPassword); + KeyStore ks = KeyStoreFileManager.loadKeyStore(keystorePath, encodedPassword); } finally { if (password != null) PasswordObfuscator.flush(password); } @@ -261,19 +264,17 @@ public class KeyStoreFileManager { } /** - * * @param keystorePath * @param keyName * @param encodedPassword * @throws java.security.UnrecoverableKeyException if the password is invalid */ - public static void validateKeyPassword( String keystorePath, String keyName, String encodedPassword) - throws Exception - { + public static void validateKeyPassword(String keystorePath, String keyName, String encodedPassword) + throws Exception { char[] password = null; try { - KeyStore ks = KeyStoreFileManager.loadKeyStore( keystorePath, (char[])null); - password = PasswordObfuscator.getInstance().decodeAliasPassword(keystorePath,keyName, encodedPassword); + KeyStore ks = KeyStoreFileManager.loadKeyStore(keystorePath, (char[]) null); + password = PasswordObfuscator.getInstance().decodeAliasPassword(keystorePath, keyName, encodedPassword); ks.getKey(keyName, password); } finally { if (password != null) PasswordObfuscator.flush(password); diff --git a/app/src/main/java/kellinwood/security/zipsigner/optional/LoadKeystoreException.java b/app/src/main/java/kellinwood/security/zipsigner/optional/LoadKeystoreException.java index d146abb16..e1aaab6b0 100644 --- a/app/src/main/java/kellinwood/security/zipsigner/optional/LoadKeystoreException.java +++ b/app/src/main/java/kellinwood/security/zipsigner/optional/LoadKeystoreException.java @@ -1,3 +1,4 @@ + package kellinwood.security.zipsigner.optional; import java.io.IOException; diff --git a/app/src/main/java/kellinwood/security/zipsigner/optional/PasswordObfuscator.java b/app/src/main/java/kellinwood/security/zipsigner/optional/PasswordObfuscator.java index e76334e0d..352c0b30d 100644 --- a/app/src/main/java/kellinwood/security/zipsigner/optional/PasswordObfuscator.java +++ b/app/src/main/java/kellinwood/security/zipsigner/optional/PasswordObfuscator.java @@ -1,3 +1,4 @@ + package kellinwood.security.zipsigner.optional; import kellinwood.logging.LoggerInterface; @@ -6,19 +7,24 @@ import kellinwood.security.zipsigner.Base64; import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; -import java.io.*; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.Writer; public class PasswordObfuscator { private static PasswordObfuscator instance = null; - - static final String x = "harold-and-maude"; + + static final String x = "harold-and-maude"; LoggerInterface logger; SecretKeySpec skeySpec; - + private PasswordObfuscator() { - logger = LoggerManager.getLogger( PasswordObfuscator.class.getName()); + logger = LoggerManager.getLogger(PasswordObfuscator.class.getName()); skeySpec = new SecretKeySpec(x.getBytes(), "AES"); } @@ -27,39 +33,39 @@ public class PasswordObfuscator { return instance; } - public String encodeKeystorePassword( String keystorePath, String password) { - return encode( keystorePath, password); + public String encodeKeystorePassword(String keystorePath, String password) { + return encode(keystorePath, password); } - public String encodeKeystorePassword( String keystorePath, char[] password) { - return encode( keystorePath, password); + public String encodeKeystorePassword(String keystorePath, char[] password) { + return encode(keystorePath, password); } - public String encodeAliasPassword( String keystorePath, String aliasName, String password) { - return encode( keystorePath+aliasName, password); + public String encodeAliasPassword(String keystorePath, String aliasName, String password) { + return encode(keystorePath + aliasName, password); } - public String encodeAliasPassword( String keystorePath, String aliasName, char[] password) { - return encode( keystorePath+aliasName, password); + public String encodeAliasPassword(String keystorePath, String aliasName, char[] password) { + return encode(keystorePath + aliasName, password); } - public char[] decodeKeystorePassword( String keystorePath, String password) { - return decode(keystorePath,password); + public char[] decodeKeystorePassword(String keystorePath, String password) { + return decode(keystorePath, password); } - public char[] decodeAliasPassword( String keystorePath, String aliasName, String password) { - return decode(keystorePath+aliasName,password); + public char[] decodeAliasPassword(String keystorePath, String aliasName, String password) { + return decode(keystorePath + aliasName, password); } - public String encode( String junk, String password) { + public String encode(String junk, String password) { if (password == null) return null; char[] c = password.toCharArray(); - String result = encode( junk, c); + String result = encode(junk, c); flush(c); return result; } - public String encode( String junk, char[] password) { + public String encode(String junk, char[] password) { if (password == null) return null; try { // Instantiate the cipher @@ -70,33 +76,33 @@ public class PasswordObfuscator { w.write(junk); w.write(password); w.flush(); - byte[] encoded = cipher.doFinal( baos.toByteArray()); - return Base64.encode( encoded); + byte[] encoded = cipher.doFinal(baos.toByteArray()); + return Base64.encode(encoded); } catch (Exception x) { logger.error("Failed to obfuscate password", x); } return null; } - - public char[] decode( String junk, String password) { + + public char[] decode(String junk, String password) { if (password == null) return null; try { // Instantiate the cipher Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); SecretKeySpec skeySpec = new SecretKeySpec(x.getBytes(), "AES"); cipher.init(Cipher.DECRYPT_MODE, skeySpec); - byte[] bytes = cipher.doFinal( Base64.decode(password.getBytes())); - BufferedReader r = new BufferedReader( new InputStreamReader( new ByteArrayInputStream( bytes))); + byte[] bytes = cipher.doFinal(Base64.decode(password.getBytes())); + BufferedReader r = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(bytes))); char[] cb = new char[128]; int length = 0; int numRead; - while ((numRead = r.read(cb, length, 128-length)) != -1) { + while ((numRead = r.read(cb, length, 128 - length)) != -1) { length += numRead; } - + if (length <= junk.length()) return null; - - char[] result = new char[ length - junk.length()]; + + char[] result = new char[length - junk.length()]; int j = 0; for (int i = junk.length(); i < length; i++) { result[j] = cb[i]; @@ -104,21 +110,21 @@ public class PasswordObfuscator { } flush(cb); return result; - + } catch (Exception x) { logger.error("Failed to decode password", x); } return null; } - - public static void flush( char[] charArray) { + + public static void flush(char[] charArray) { if (charArray == null) return; for (int i = 0; i < charArray.length; i++) { charArray[i] = '\0'; } } - public static void flush( byte[] charArray) { + public static void flush(byte[] charArray) { if (charArray == null) return; for (int i = 0; i < charArray.length; i++) { charArray[i] = 0; diff --git a/app/src/main/java/kellinwood/security/zipsigner/optional/SignatureBlockGenerator.java b/app/src/main/java/kellinwood/security/zipsigner/optional/SignatureBlockGenerator.java index ee01cc765..197cefa44 100644 --- a/app/src/main/java/kellinwood/security/zipsigner/optional/SignatureBlockGenerator.java +++ b/app/src/main/java/kellinwood/security/zipsigner/optional/SignatureBlockGenerator.java @@ -1,8 +1,13 @@ + package kellinwood.security.zipsigner.optional; import kellinwood.security.zipsigner.KeySet; import org.bouncycastle.cert.jcajce.JcaCertStore; -import org.bouncycastle.cms.*; +import org.bouncycastle.cms.CMSProcessableByteArray; +import org.bouncycastle.cms.CMSSignedData; +import org.bouncycastle.cms.CMSSignedDataGenerator; +import org.bouncycastle.cms.CMSTypedData; +import org.bouncycastle.cms.SignerInfoGenerator; import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder; import org.bouncycastle.operator.ContentSigner; import org.bouncycastle.operator.DigestCalculatorProvider; @@ -39,11 +44,11 @@ public class SignatureBlockGenerator { JcaDigestCalculatorProviderBuilder jcaDigestCalculatorProviderBuilder = new JcaDigestCalculatorProviderBuilder().setProvider("SC"); DigestCalculatorProvider digestCalculatorProvider = jcaDigestCalculatorProviderBuilder.build(); - JcaSignerInfoGeneratorBuilder jcaSignerInfoGeneratorBuilder = new JcaSignerInfoGeneratorBuilder( digestCalculatorProvider); + JcaSignerInfoGeneratorBuilder jcaSignerInfoGeneratorBuilder = new JcaSignerInfoGeneratorBuilder(digestCalculatorProvider); jcaSignerInfoGeneratorBuilder.setDirectSignature(true); SignerInfoGenerator signerInfoGenerator = jcaSignerInfoGeneratorBuilder.build(sha1Signer, keySet.getPublicKey()); - gen.addSignerInfoGenerator( signerInfoGenerator); + gen.addSignerInfoGenerator(signerInfoGenerator); gen.addCertificates(certs); diff --git a/app/src/main/java/kellinwood/zipio/CentralEnd.java b/app/src/main/java/kellinwood/zipio/CentralEnd.java index 3d3a8b2b4..f52b44fdb 100644 --- a/app/src/main/java/kellinwood/zipio/CentralEnd.java +++ b/app/src/main/java/kellinwood/zipio/CentralEnd.java @@ -13,15 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package kellinwood.zipio; -import java.io.IOException; +package kellinwood.zipio; import kellinwood.logging.LoggerInterface; import kellinwood.logging.LoggerManager; -public class CentralEnd -{ +import java.io.IOException; + +public class CentralEnd { public int signature = 0x06054b50; // end of central dir signature 4 bytes public short numberThisDisk = 0; // number of this disk 2 bytes public short centralStartDisk = 0; // number of the disk with the start of the central directory 2 bytes @@ -34,73 +34,70 @@ public class CentralEnd private static LoggerInterface log; - public static CentralEnd read(ZipInput input) throws IOException - { + public static CentralEnd read(ZipInput input) throws IOException { int signature = input.readInt(); if (signature != 0x06054b50) { // back up to the signature - input.seek( input.getFilePointer() - 4); + input.seek(input.getFilePointer() - 4); return null; } CentralEnd entry = new CentralEnd(); - entry.doRead( input); + entry.doRead(input); return entry; } public static LoggerInterface getLogger() { - if (log == null) log = LoggerManager.getLogger( CentralEnd.class.getName()); + if (log == null) log = LoggerManager.getLogger(CentralEnd.class.getName()); return log; } - private void doRead( ZipInput input) throws IOException - { + private void doRead(ZipInput input) throws IOException { boolean debug = getLogger().isDebugEnabled(); numberThisDisk = input.readShort(); - if (debug) log.debug( String.format("This disk number: 0x%04x", numberThisDisk)); + if (debug) log.debug(String.format("This disk number: 0x%04x", numberThisDisk)); centralStartDisk = input.readShort(); - if (debug) log.debug( String.format("Central dir start disk number: 0x%04x", centralStartDisk)); + if (debug) log.debug(String.format("Central dir start disk number: 0x%04x", centralStartDisk)); numCentralEntries = input.readShort(); - if (debug) log.debug( String.format("Central entries on this disk: 0x%04x", numCentralEntries)); + if (debug) log.debug(String.format("Central entries on this disk: 0x%04x", numCentralEntries)); totalCentralEntries = input.readShort(); - if (debug) log.debug( String.format("Total number of central entries: 0x%04x", totalCentralEntries)); + if (debug) log.debug(String.format("Total number of central entries: 0x%04x", totalCentralEntries)); centralDirectorySize = input.readInt(); - if (debug) log.debug( String.format("Central directory size: 0x%08x", centralDirectorySize)); + if (debug) log.debug(String.format("Central directory size: 0x%08x", centralDirectorySize)); centralStartOffset = input.readInt(); - if (debug) log.debug( String.format("Central directory offset: 0x%08x", centralStartOffset)); + if (debug) log.debug(String.format("Central directory offset: 0x%08x", centralStartOffset)); short zipFileCommentLen = input.readShort(); fileComment = input.readString(zipFileCommentLen); - if (debug) log.debug( ".ZIP file comment: " + fileComment); + if (debug) log.debug(".ZIP file comment: " + fileComment); } - public void write( ZipOutput output) throws IOException - { + public void write(ZipOutput output) throws IOException { boolean debug = getLogger().isDebugEnabled(); - output.writeInt( signature); - output.writeShort( numberThisDisk); - output.writeShort( centralStartDisk); - output.writeShort( numCentralEntries); - output.writeShort( totalCentralEntries); - output.writeInt( centralDirectorySize ); - output.writeInt( centralStartOffset ); - output.writeShort( (short)fileComment.length()); - output.writeString( fileComment); + output.writeInt(signature); + output.writeShort(numberThisDisk); + output.writeShort(centralStartDisk); + output.writeShort(numCentralEntries); + output.writeShort(totalCentralEntries); + output.writeInt(centralDirectorySize); + output.writeInt(centralStartOffset); + output.writeShort((short) fileComment.length()); + output.writeString(fileComment); } diff --git a/app/src/main/java/kellinwood/zipio/ZioEntry.java b/app/src/main/java/kellinwood/zipio/ZioEntry.java index a2c80f970..f61cec7c0 100644 --- a/app/src/main/java/kellinwood/zipio/ZioEntry.java +++ b/app/src/main/java/kellinwood/zipio/ZioEntry.java @@ -13,13 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package kellinwood.zipio; +import kellinwood.logging.LoggerInterface; +import kellinwood.logging.LoggerManager; + import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.io.File; import java.io.OutputStream; import java.io.SequenceInputStream; import java.util.Date; @@ -27,9 +31,6 @@ import java.util.zip.CRC32; import java.util.zip.Inflater; import java.util.zip.InflaterInputStream; -import kellinwood.logging.LoggerInterface; -import kellinwood.logging.LoggerManager; - public class ZioEntry implements Cloneable { private ZipInput zipInput; @@ -51,48 +52,47 @@ public class ZioEntry implements Cloneable { private short diskNumberStart; private short internalAttributes; private int externalAttributes; - + private int localHeaderOffset; private long dataPosition = -1; private byte[] data = null; private ZioEntryOutputStream entryOut = null; - + private static byte[] alignBytes = new byte[4]; - + private static LoggerInterface log; - public ZioEntry( ZipInput input) { + public ZioEntry(ZipInput input) { zipInput = input; } public static LoggerInterface getLogger() { - if (log == null) log = LoggerManager.getLogger( ZioEntry.class.getName()); + if (log == null) log = LoggerManager.getLogger(ZioEntry.class.getName()); return log; } - public ZioEntry( String name) { + public ZioEntry(String name) { filename = name; fileComment = ""; compression = 8; extraData = new byte[0]; - setTime( System.currentTimeMillis()); + setTime(System.currentTimeMillis()); } - - public ZioEntry( String name, String sourceDataFile) - throws IOException - { - zipInput = new ZipInput( sourceDataFile); + + public ZioEntry(String name, String sourceDataFile) + throws IOException { + zipInput = new ZipInput(sourceDataFile); filename = name; fileComment = ""; this.compression = 0; - this.size = (int)zipInput.getFileLength(); + this.size = (int) zipInput.getFileLength(); this.compressedSize = this.size; - if (getLogger().isDebugEnabled()) - getLogger().debug(String.format("Computing CRC for %s, size=%d",sourceDataFile,size)); - + if (getLogger().isDebugEnabled()) + getLogger().debug(String.format("Computing CRC for %s, size=%d", sourceDataFile, size)); + // compute CRC CRC32 crc = new CRC32(); @@ -100,27 +100,25 @@ public class ZioEntry implements Cloneable { int numRead = 0; while (numRead != size) { - int count = zipInput.read( buffer, 0, Math.min( buffer.length, (this.size - numRead))); + int count = zipInput.read(buffer, 0, Math.min(buffer.length, (this.size - numRead))); if (count > 0) { - crc.update( buffer, 0, count); + crc.update(buffer, 0, count); numRead += count; } } - this.crc32 = (int)crc.getValue(); + this.crc32 = (int) crc.getValue(); zipInput.seek(0); this.dataPosition = 0; extraData = new byte[0]; - setTime( new File(sourceDataFile).lastModified()); + setTime(new File(sourceDataFile).lastModified()); } - - - public ZioEntry( String name, String sourceDataFile, short compression, int crc32, int compressedSize, int size) - throws IOException - { - zipInput = new ZipInput( sourceDataFile); + + public ZioEntry(String name, String sourceDataFile, short compression, int crc32, int compressedSize, int size) + throws IOException { + zipInput = new ZipInput(sourceDataFile); filename = name; fileComment = ""; this.compression = compression; @@ -129,39 +127,35 @@ public class ZioEntry implements Cloneable { this.size = size; this.dataPosition = 0; extraData = new byte[0]; - setTime( new File(sourceDataFile).lastModified()); + setTime(new File(sourceDataFile).lastModified()); } - + // Return a copy with a new name - public ZioEntry getClonedEntry( String newName) - { - + public ZioEntry getClonedEntry(String newName) { + ZioEntry clone; try { - clone = (ZioEntry)this.clone(); - } - catch (CloneNotSupportedException e) - { + clone = (ZioEntry) this.clone(); + } catch (CloneNotSupportedException e) { throw new IllegalStateException("clone() failed!"); } clone.setName(newName); return clone; } - - public void readLocalHeader() throws IOException - { + + public void readLocalHeader() throws IOException { ZipInput input = zipInput; int tmp; boolean debug = getLogger().isDebugEnabled(); - input.seek( localHeaderOffset); + input.seek(localHeaderOffset); - if (debug) getLogger().debug( String.format("FILE POSITION: 0x%08x", input.getFilePointer())); + if (debug) getLogger().debug(String.format("FILE POSITION: 0x%08x", input.getFilePointer())); // 0 4 Local file header signature = 0x04034b50 int signature = input.readInt(); if (signature != 0x04034b50) { - throw new IllegalStateException( String.format("Local header not found at pos=0x%08x, file=%s", input.getFilePointer(), filename)); + throw new IllegalStateException(String.format("Local header not found at pos=0x%08x, file=%s", input.getFilePointer(), filename)); } // This method is usually called just before the data read, so @@ -172,38 +166,46 @@ public class ZioEntry implements Cloneable { int tmpInt; short tmpShort; - + // 4 2 Version needed to extract (minimum) - /* versionRequired */ tmpShort = input.readShort(); + /* versionRequired */ + tmpShort = input.readShort(); if (debug) log.debug(String.format("Version required: 0x%04x", tmpShort /*versionRequired*/)); // 6 2 General purpose bit flag - /* generalPurposeBits */ tmpShort = input.readShort(); + /* generalPurposeBits */ + tmpShort = input.readShort(); if (debug) log.debug(String.format("General purpose bits: 0x%04x", tmpShort /* generalPurposeBits */)); - + // 8 2 Compression method - /* compression */ tmpShort = input.readShort(); + /* compression */ + tmpShort = input.readShort(); if (debug) log.debug(String.format("Compression: 0x%04x", tmpShort /* compression */)); // 10 2 File last modification time - /* modificationTime */ tmpShort = input.readShort(); + /* modificationTime */ + tmpShort = input.readShort(); if (debug) log.debug(String.format("Modification time: 0x%04x", tmpShort /* modificationTime */)); // 12 2 File last modification date - /* modificationDate */ tmpShort = input.readShort(); + /* modificationDate */ + tmpShort = input.readShort(); if (debug) log.debug(String.format("Modification date: 0x%04x", tmpShort /* modificationDate */)); // 14 4 CRC-32 - /* crc32 */ tmpInt = input.readInt(); + /* crc32 */ + tmpInt = input.readInt(); if (debug) log.debug(String.format("CRC-32: 0x%04x", tmpInt /*crc32*/)); // 18 4 Compressed size - /* compressedSize*/ tmpInt = input.readInt(); + /* compressedSize*/ + tmpInt = input.readInt(); if (debug) log.debug(String.format("Compressed size: 0x%04x", tmpInt /*compressedSize*/)); // 22 4 Uncompressed size - /* size */ tmpInt = input.readInt(); - if (debug) log.debug(String.format("Size: 0x%04x", tmpInt /*size*/ )); + /* size */ + tmpInt = input.readInt(); + if (debug) log.debug(String.format("Size: 0x%04x", tmpInt /*size*/)); // 26 2 File name length (n) short fileNameLen = input.readShort(); @@ -218,46 +220,45 @@ public class ZioEntry implements Cloneable { if (debug) log.debug("Filename: " + filename); // Extra data - byte[] extra = input.readBytes( extraLen); + byte[] extra = input.readBytes(extraLen); // Record the file position of this entry's data. dataPosition = input.getFilePointer(); - if (debug) log.debug(String.format("Data position: 0x%08x",dataPosition)); + if (debug) log.debug(String.format("Data position: 0x%08x", dataPosition)); } - public void writeLocalEntry( ZipOutput output) throws IOException - { + public void writeLocalEntry(ZipOutput output) throws IOException { if (data == null && dataPosition < 0 && zipInput != null) { readLocalHeader(); } - - localHeaderOffset = (int)output.getFilePointer(); + + localHeaderOffset = (int) output.getFilePointer(); boolean debug = getLogger().isDebugEnabled(); - + if (debug) { - getLogger().debug( String.format("Writing local header at 0x%08x - %s", localHeaderOffset, filename)); + getLogger().debug(String.format("Writing local header at 0x%08x - %s", localHeaderOffset, filename)); } - + if (entryOut != null) { entryOut.close(); size = entryOut.getSize(); - data = ((ByteArrayOutputStream)entryOut.getWrappedStream()).toByteArray(); + data = ((ByteArrayOutputStream) entryOut.getWrappedStream()).toByteArray(); compressedSize = data.length; crc32 = entryOut.getCRC(); } - - output.writeInt( 0x04034b50); - output.writeShort( versionRequired); - output.writeShort( generalPurposeBits); - output.writeShort( compression); - output.writeShort( modificationTime); - output.writeShort( modificationDate); - output.writeInt( crc32); - output.writeInt( compressedSize); - output.writeInt( size); - output.writeShort( (short)filename.length()); + + output.writeInt(0x04034b50); + output.writeShort(versionRequired); + output.writeShort(generalPurposeBits); + output.writeShort(compression); + output.writeShort(modificationTime); + output.writeShort(modificationDate); + output.writeInt(crc32); + output.writeInt(compressedSize); + output.writeInt(size); + output.writeShort((short) filename.length()); numAlignBytes = 0; @@ -265,77 +266,74 @@ public class ZioEntry implements Cloneable { if (compression == 0) { long dataPos = output.getFilePointer() + // current position - 2 + // plus size of extra data length - filename.length() + // plus filename - extraData.length; // plus extra data + 2 + // plus size of extra data length + filename.length() + // plus filename + extraData.length; // plus extra data - short dataPosMod4 = (short)(dataPos % 4); + short dataPosMod4 = (short) (dataPos % 4); if (dataPosMod4 > 0) { - numAlignBytes = (short)(4 - dataPosMod4); + numAlignBytes = (short) (4 - dataPosMod4); } } - + // 28 2 Extra field length (m) - output.writeShort( (short)(extraData.length + numAlignBytes)); + output.writeShort((short) (extraData.length + numAlignBytes)); // 30 n File name - output.writeString( filename); + output.writeString(filename); // Extra data - output.writeBytes( extraData); + output.writeBytes(extraData); // Zipalign bytes if (numAlignBytes > 0) { - output.writeBytes( alignBytes, 0, numAlignBytes); + output.writeBytes(alignBytes, 0, numAlignBytes); } if (debug) getLogger().debug(String.format("Data position 0x%08x", output.getFilePointer())); if (data != null) { - output.writeBytes( data); + output.writeBytes(data); if (debug) getLogger().debug(String.format("Wrote %d bytes", data.length)); - } - else { + } else { if (debug) getLogger().debug(String.format("Seeking to position 0x%08x", dataPosition)); - zipInput.seek( dataPosition); - - int bufferSize = Math.min( compressedSize, 8096); + zipInput.seek(dataPosition); + + int bufferSize = Math.min(compressedSize, 8096); byte[] buffer = new byte[bufferSize]; long totalCount = 0; - + while (totalCount != compressedSize) { - int numRead = zipInput.in.read( buffer, 0, (int)Math.min( compressedSize - totalCount, bufferSize)); + int numRead = zipInput.in.read(buffer, 0, (int) Math.min(compressedSize - totalCount, bufferSize)); if (numRead > 0) { output.writeBytes(buffer, 0, numRead); if (debug) getLogger().debug(String.format("Wrote %d bytes", numRead)); totalCount += numRead; - } - else throw new IllegalStateException(String.format("EOF reached while copying %s with %d bytes left to go", filename, compressedSize - totalCount)); + } else + throw new IllegalStateException(String.format("EOF reached while copying %s with %d bytes left to go", filename, compressedSize - totalCount)); } } - } - - public static ZioEntry read(ZipInput input) throws IOException - { + } + + public static ZioEntry read(ZipInput input) throws IOException { // 0 4 Central directory header signature = 0x02014b50 int signature = input.readInt(); if (signature != 0x02014b50) { // back up to the signature - input.seek( input.getFilePointer() - 4); + input.seek(input.getFilePointer() - 4); return null; } - ZioEntry entry = new ZioEntry( input); + ZioEntry entry = new ZioEntry(input); - entry.doRead( input); + entry.doRead(input); return entry; } - private void doRead( ZipInput input) throws IOException - { + private void doRead(ZipInput input) throws IOException { boolean debug = getLogger().isDebugEnabled(); @@ -352,7 +350,7 @@ public class ZioEntry implements Cloneable { if (debug) log.debug(String.format("General purpose bits: 0x%04x", generalPurposeBits)); // Bits 1, 2, 3, and 11 are allowed to be set (first bit is bit zero). Any others are a problem. if ((generalPurposeBits & 0xF7F1) != 0x0000) { - throw new IllegalStateException("Can't handle general purpose bits == "+String.format("0x%04x",generalPurposeBits)); + throw new IllegalStateException("Can't handle general purpose bits == " + String.format("0x%04x", generalPurposeBits)); } // 8 2 Compression method @@ -406,13 +404,13 @@ public class ZioEntry implements Cloneable { filename = input.readString(fileNameLen); if (debug) log.debug("Filename: " + filename); - extraData = input.readBytes( extraLen); + extraData = input.readBytes(extraLen); - fileComment = input.readString( fileCommentLen); + fileComment = input.readString(fileCommentLen); if (debug) log.debug("File comment: " + fileComment); - generalPurposeBits = (short)(generalPurposeBits & 0x0800); // Don't write a data descriptor, preserve UTF-8 encoded filename bit - + generalPurposeBits = (short) (generalPurposeBits & 0x0800); // Don't write a data descriptor, preserve UTF-8 encoded filename bit + // Don't write zero-length entries with compression. if (size == 0) { compressedSize = 0; @@ -422,19 +420,21 @@ public class ZioEntry implements Cloneable { } - /** Returns the entry's data. */ - public byte[] getData() throws IOException - { + /** + * Returns the entry's data. + */ + public byte[] getData() throws IOException { if (data != null) return data; - + byte[] tmpdata = new byte[size]; - + InputStream din = getInputStream(); int count = 0; - + while (count != size) { - int numRead = din.read( tmpdata, count, size-count); - if (numRead < 0) throw new IllegalStateException(String.format("Read failed, expecting %d bytes, got %d instead", size, count)); + int numRead = din.read(tmpdata, count, size - count); + if (numRead < 0) + throw new IllegalStateException(String.format("Read failed, expecting %d bytes, got %d instead", size, count)); count += numRead; } return tmpdata; @@ -447,70 +447,68 @@ public class ZioEntry implements Cloneable { // Returns an input stream for reading the entry's data. public InputStream getInputStream(OutputStream monitorStream) throws IOException { - + if (entryOut != null) { entryOut.close(); size = entryOut.getSize(); - data = ((ByteArrayOutputStream)entryOut.getWrappedStream()).toByteArray(); + data = ((ByteArrayOutputStream) entryOut.getWrappedStream()).toByteArray(); compressedSize = data.length; crc32 = entryOut.getCRC(); entryOut = null; - InputStream rawis = new ByteArrayInputStream( data); + InputStream rawis = new ByteArrayInputStream(data); if (compression == 0) return rawis; else { // Hacky, inflate using a sequence of input streams that returns 1 byte more than the actual length of the data. // This extra dummy byte is required by InflaterInputStream when the data doesn't have the header and crc fields (as it is in zip files). - return new InflaterInputStream( new SequenceInputStream(rawis, new ByteArrayInputStream(new byte[1])), new Inflater( true)); + return new InflaterInputStream(new SequenceInputStream(rawis, new ByteArrayInputStream(new byte[1])), new Inflater(true)); } } - + ZioEntryInputStream dataStream; dataStream = new ZioEntryInputStream(this); - if (monitorStream != null) dataStream.setMonitorStream( monitorStream); - if (compression != 0) { + if (monitorStream != null) dataStream.setMonitorStream(monitorStream); + if (compression != 0) { // Note: When using nowrap=true with Inflater it is also necessary to provide // an extra "dummy" byte as input. This is required by the ZLIB native library // in order to support certain optimizations. dataStream.setReturnDummyByte(true); - return new InflaterInputStream( dataStream, new Inflater( true)); - } - else return dataStream; + return new InflaterInputStream(dataStream, new Inflater(true)); + } else return dataStream; } // Returns an output stream for writing an entry's data. - public OutputStream getOutputStream() - { - entryOut = new ZioEntryOutputStream( compression, new ByteArrayOutputStream()); + public OutputStream getOutputStream() { + entryOut = new ZioEntryOutputStream(compression, new ByteArrayOutputStream()); return entryOut; } - public void write( ZipOutput output) throws IOException { + public void write(ZipOutput output) throws IOException { boolean debug = getLogger().isDebugEnabled(); - output.writeInt( 0x02014b50); - output.writeShort( versionMadeBy); - output.writeShort( versionRequired); - output.writeShort( generalPurposeBits); - output.writeShort( compression); - output.writeShort( modificationTime); - output.writeShort( modificationDate); - output.writeInt( crc32); - output.writeInt( compressedSize); - output.writeInt( size); - output.writeShort( (short)filename.length()); - output.writeShort( (short)(extraData.length + numAlignBytes)); - output.writeShort( (short)fileComment.length()); - output.writeShort( diskNumberStart); - output.writeShort( internalAttributes); - output.writeInt( externalAttributes); - output.writeInt( localHeaderOffset); - - output.writeString( filename); - output.writeBytes( extraData); - if (numAlignBytes > 0) output.writeBytes( alignBytes, 0, numAlignBytes); - output.writeString( fileComment); + output.writeInt(0x02014b50); + output.writeShort(versionMadeBy); + output.writeShort(versionRequired); + output.writeShort(generalPurposeBits); + output.writeShort(compression); + output.writeShort(modificationTime); + output.writeShort(modificationDate); + output.writeInt(crc32); + output.writeInt(compressedSize); + output.writeInt(size); + output.writeShort((short) filename.length()); + output.writeShort((short) (extraData.length + numAlignBytes)); + output.writeShort((short) fileComment.length()); + output.writeShort(diskNumberStart); + output.writeShort(internalAttributes); + output.writeInt(externalAttributes); + output.writeInt(localHeaderOffset); + + output.writeString(filename); + output.writeBytes(extraData); + if (numAlignBytes > 0) output.writeBytes(alignBytes, 0, numAlignBytes); + output.writeString(fileComment); } @@ -518,13 +516,13 @@ public class ZioEntry implements Cloneable { * Returns timetamp in Java format */ public long getTime() { - int year = (int)(((modificationDate >> 9) & 0x007f) + 80); - int month = (int)(((modificationDate >> 5) & 0x000f) - 1); - int day = (int)(modificationDate & 0x001f); - int hour = (int)((modificationTime >> 11) & 0x001f); - int minute = (int)((modificationTime >> 5) & 0x003f); - int seconds = (int)((modificationTime << 1) & 0x003e); - Date d = new Date( year, month, day, hour, minute, seconds); + int year = (int) (((modificationDate >> 9) & 0x007f) + 80); + int month = (int) (((modificationDate >> 5) & 0x000f) - 1); + int day = (int) (modificationDate & 0x001f); + int hour = (int) ((modificationTime >> 11) & 0x001f); + int minute = (int) ((modificationTime >> 5) & 0x003f); + int seconds = (int) ((modificationTime << 1) & 0x003e); + Date d = new Date(year, month, day, hour, minute, seconds); return d.getTime(); } @@ -537,32 +535,33 @@ public class ZioEntry implements Cloneable { int year = d.getYear() + 1900; if (year < 1980) { dtime = (1 << 21) | (1 << 16); - } - else { + } else { dtime = (year - 1980) << 25 | (d.getMonth() + 1) << 21 | - d.getDate() << 16 | d.getHours() << 11 | d.getMinutes() << 5 | - d.getSeconds() >> 1; + d.getDate() << 16 | d.getHours() << 11 | d.getMinutes() << 5 | + d.getSeconds() >> 1; } - modificationDate = (short)(dtime >> 16); - modificationTime = (short)(dtime & 0xFFFF); + modificationDate = (short) (dtime >> 16); + modificationTime = (short) (dtime & 0xFFFF); } public boolean isDirectory() { return filename.endsWith("/"); } - + public String getName() { return filename; } - - public void setName( String filename) { + + public void setName(String filename) { this.filename = filename; } - - /** Use 0 (STORED), or 8 (DEFLATE). */ - public void setCompression( int compression) { - this.compression = (short)compression; + + /** + * Use 0 (STORED), or 8 (DEFLATE). + */ + public void setCompression(int compression) { + this.compression = (short) compression; } public short getVersionMadeBy() { diff --git a/app/src/main/java/kellinwood/zipio/ZioEntryInputStream.java b/app/src/main/java/kellinwood/zipio/ZioEntryInputStream.java index 8d4b7cecd..7ed226c62 100644 --- a/app/src/main/java/kellinwood/zipio/ZioEntryInputStream.java +++ b/app/src/main/java/kellinwood/zipio/ZioEntryInputStream.java @@ -13,18 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package kellinwood.zipio; +import kellinwood.logging.LoggerInterface; +import kellinwood.logging.LoggerManager; + import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.RandomAccessFile; -import kellinwood.logging.LoggerInterface; -import kellinwood.logging.LoggerManager; - -/** Input stream used to read just the data from a zip file entry. */ +/** + * Input stream used to read just the data from a zip file entry. + */ public class ZioEntryInputStream extends InputStream { RandomAccessFile raf; @@ -34,10 +37,10 @@ public class ZioEntryInputStream extends InputStream { boolean debug; boolean returnDummyByte = false; OutputStream monitor = null; - - public ZioEntryInputStream( ZioEntry entry) throws IOException { - log = LoggerManager.getLogger( this.getClass().getName()); + public ZioEntryInputStream(ZioEntry entry) throws IOException { + + log = LoggerManager.getLogger(this.getClass().getName()); debug = log.isDebugEnabled(); offset = 0; size = entry.getCompressedSize(); @@ -45,25 +48,24 @@ public class ZioEntryInputStream extends InputStream { long dpos = entry.getDataPosition(); if (dpos >= 0) { if (debug) log.debug(String.format("Seeking to %d", entry.getDataPosition())); - raf.seek( entry.getDataPosition()); - } - else { + raf.seek(entry.getDataPosition()); + } else { // seeks to, then reads, the local header, causing the // file pointer to be positioned at the start of the data. entry.readLocalHeader(); } - + } - public void setReturnDummyByte( boolean returnExtraByte) { + public void setReturnDummyByte(boolean returnExtraByte) { returnDummyByte = returnExtraByte; } - + // For debugging, if the monitor is set we write all data read to the monitor. public void setMonitorStream(OutputStream monitorStream) { monitor = monitorStream; } - + @Override public void close() throws IOException { } @@ -87,22 +89,20 @@ public class ZioEntryInputStream extends InputStream { if (returnDummyByte) { returnDummyByte = false; return 0; - } - else return -1; + } else return -1; } int b = raf.read(); if (b >= 0) { if (monitor != null) monitor.write(b); if (debug) log.debug("Read 1 byte"); offset += 1; - } - else if (debug) log.debug("Read 0 bytes"); + } else if (debug) log.debug("Read 0 bytes"); return b; } @Override public int read(byte[] b, int off, int len) throws IOException { - return readBytes( b, off, len); + return readBytes(b, off, len); } private int readBytes(byte[] b, int off, int len) throws IOException { @@ -111,10 +111,9 @@ public class ZioEntryInputStream extends InputStream { returnDummyByte = false; b[off] = 0; return 1; - } - else return -1; - } - int numToRead = Math.min( len, available()); + } else return -1; + } + int numToRead = Math.min(len, available()); int numRead = raf.read(b, off, numToRead); if (numRead > 0) { if (monitor != null) monitor.write(b, off, numRead); @@ -126,14 +125,14 @@ public class ZioEntryInputStream extends InputStream { @Override public int read(byte[] b) throws IOException { - return readBytes( b, 0, b.length); + return readBytes(b, 0, b.length); } @Override public long skip(long n) throws IOException { - long numToSkip = Math.min( n, available()); - raf.seek( raf.getFilePointer() + numToSkip); - if (debug) log.debug(String.format("Skipped %d bytes", numToSkip)); + long numToSkip = Math.min(n, available()); + raf.seek(raf.getFilePointer() + numToSkip); + if (debug) log.debug(String.format("Skipped %d bytes", numToSkip)); return numToSkip; } } diff --git a/app/src/main/java/kellinwood/zipio/ZioEntryOutputStream.java b/app/src/main/java/kellinwood/zipio/ZioEntryOutputStream.java index 0674c9d84..e13332412 100644 --- a/app/src/main/java/kellinwood/zipio/ZioEntryOutputStream.java +++ b/app/src/main/java/kellinwood/zipio/ZioEntryOutputStream.java @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package kellinwood.zipio; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.zip.CRC32; @@ -28,25 +28,24 @@ public class ZioEntryOutputStream extends OutputStream { int crcValue = 0; OutputStream wrapped; OutputStream downstream; - - public ZioEntryOutputStream( int compression, OutputStream wrapped) - { + + public ZioEntryOutputStream(int compression, OutputStream wrapped) { this.wrapped = wrapped; if (compression != 0) - downstream = new DeflaterOutputStream( wrapped, new Deflater( Deflater.BEST_COMPRESSION, true)); + downstream = new DeflaterOutputStream(wrapped, new Deflater(Deflater.BEST_COMPRESSION, true)); else downstream = wrapped; } - + public void close() throws IOException { downstream.flush(); downstream.close(); - crcValue = (int)crc.getValue(); + crcValue = (int) crc.getValue(); } public int getCRC() { return crcValue; } - + public void flush() throws IOException { downstream.flush(); } @@ -58,14 +57,14 @@ public class ZioEntryOutputStream extends OutputStream { } public void write(byte[] b, int off, int len) throws IOException { - downstream.write( b, off, len); - crc.update( b, off, len); + downstream.write(b, off, len); + crc.update(b, off, len); size += len; } public void write(int b) throws IOException { - downstream.write( b); - crc.update( b); + downstream.write(b); + crc.update(b); size += 1; } @@ -73,10 +72,9 @@ public class ZioEntryOutputStream extends OutputStream { return size; } - public OutputStream getWrappedStream() - { + public OutputStream getWrappedStream() { return wrapped; } - + } diff --git a/app/src/main/java/kellinwood/zipio/ZipInput.java b/app/src/main/java/kellinwood/zipio/ZipInput.java index 44e62e9dc..54f2ea101 100644 --- a/app/src/main/java/kellinwood/zipio/ZipInput.java +++ b/app/src/main/java/kellinwood/zipio/ZipInput.java @@ -13,16 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package kellinwood.zipio; +import kellinwood.logging.LoggerInterface; +import kellinwood.logging.LoggerManager; + import java.io.Closeable; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; -import java.text.DateFormat; -import java.text.SimpleDateFormat; import java.util.Collection; -import java.util.Date; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; @@ -31,15 +32,10 @@ import java.util.jar.Manifest; import java.util.regex.Matcher; import java.util.regex.Pattern; - -import kellinwood.logging.LoggerInterface; -import kellinwood.logging.LoggerManager; - /** * */ -public class ZipInput implements Closeable -{ +public class ZipInput implements Closeable { static LoggerInterface log; public String inputFilename; @@ -47,14 +43,13 @@ public class ZipInput implements Closeable long fileLength; int scanIterations = 0; - Map zioEntries = new LinkedHashMap(); + Map zioEntries = new LinkedHashMap(); CentralEnd centralEnd; Manifest manifest; - public ZipInput( String filename) throws IOException - { + public ZipInput(String filename) throws IOException { this.inputFilename = filename; - in = new RandomAccessFile( new File( inputFilename), "r"); + in = new RandomAccessFile(new File(inputFilename), "r"); fileLength = in.length(); } @@ -70,128 +65,131 @@ public class ZipInput implements Closeable public long getFileLength() { return fileLength; } - - public static ZipInput read( String filename) throws IOException { - ZipInput zipInput = new ZipInput( filename); + + public static ZipInput read(String filename) throws IOException { + ZipInput zipInput = new ZipInput(filename); zipInput.doRead(); return zipInput; } - - - public ZioEntry getEntry( String filename) { + + + public ZioEntry getEntry(String filename) { return zioEntries.get(filename); } - - public Map getEntries() { + + public Map getEntries() { return zioEntries; } - - /** Returns the names of immediate children in the directory with the given name. - * The path value must end with a "/" character. Use a value of "/" - * to get the root entries. + + /** + * Returns the names of immediate children in the directory with the given name. + * The path value must end with a "/" character. Use a value of "/" + * to get the root entries. */ - public Collection list(String path) - { + public Collection list(String path) { if (!path.endsWith("/")) throw new IllegalArgumentException("Invalid path -- does not end with '/'"); - + if (path.startsWith("/")) path = path.substring(1); - - Pattern p = Pattern.compile( String.format("^%s([^/]+/?).*", path)); - + + Pattern p = Pattern.compile(String.format("^%s([^/]+/?).*", path)); + Set names = new TreeSet(); - + for (String name : zioEntries.keySet()) { Matcher m = p.matcher(name); if (m.matches()) names.add(m.group(1)); } return names; } - + public Manifest getManifest() throws IOException { if (manifest == null) { ZioEntry e = zioEntries.get("META-INF/MANIFEST.MF"); if (e != null) { - manifest = new Manifest( e.getInputStream()); + manifest = new Manifest(e.getInputStream()); } } - return manifest; + return manifest; } - /** Scan the end of the file for the end of central directory record (EOCDR). - Returns the file offset of the EOCD signature. The size parameter is an - initial buffer size (e.g., 256). + /** + * Scan the end of the file for the end of central directory record (EOCDR). + * Returns the file offset of the EOCD signature. The size parameter is an + * initial buffer size (e.g., 256). */ - public long scanForEOCDR( int size) throws IOException { - if (size > fileLength || size > 65536) throw new IllegalStateException( "End of central directory not found in " + inputFilename); + public long scanForEOCDR(int size) throws IOException { + if (size > fileLength || size > 65536) + throw new IllegalStateException("End of central directory not found in " + inputFilename); - int scanSize = (int)Math.min( fileLength, size); + int scanSize = (int) Math.min(fileLength, size); byte[] scanBuf = new byte[scanSize]; - in.seek( fileLength - scanSize); + in.seek(fileLength - scanSize); - in.readFully( scanBuf); + in.readFully(scanBuf); for (int i = scanSize - 22; i >= 0; i--) { scanIterations += 1; - if (scanBuf[i] == 0x50 && scanBuf[i+1] == 0x4b && scanBuf[i+2] == 0x05 && scanBuf[i+3] == 0x06) { + if (scanBuf[i] == 0x50 && scanBuf[i + 1] == 0x4b && scanBuf[i + 2] == 0x05 && scanBuf[i + 3] == 0x06) { return fileLength - scanSize + i; } } - return scanForEOCDR( size * 2); + return scanForEOCDR(size * 2); } - - - private void doRead() - { + + + private void doRead() { try { - long posEOCDR = scanForEOCDR( 256); - in.seek( posEOCDR); - centralEnd = CentralEnd.read( this); + long posEOCDR = scanForEOCDR(256); + in.seek(posEOCDR); + centralEnd = CentralEnd.read(this); boolean debug = getLogger().isDebugEnabled(); if (debug) { getLogger().debug(String.format("EOCD found in %d iterations", scanIterations)); getLogger().debug(String.format("Directory entries=%d, size=%d, offset=%d/0x%08x", centralEnd.totalCentralEntries, - centralEnd.centralDirectorySize, centralEnd.centralStartOffset, centralEnd.centralStartOffset)); + centralEnd.centralDirectorySize, centralEnd.centralStartOffset, centralEnd.centralStartOffset)); - ZipListingHelper.listHeader( getLogger()); + ZipListingHelper.listHeader(getLogger()); } - in.seek( centralEnd.centralStartOffset); + in.seek(centralEnd.centralStartOffset); for (int i = 0; i < centralEnd.totalCentralEntries; i++) { ZioEntry entry = ZioEntry.read(this); - zioEntries.put( entry.getName(), entry); - if (debug) ZipListingHelper.listEntry( getLogger(), entry); + zioEntries.put(entry.getName(), entry); + if (debug) ZipListingHelper.listEntry(getLogger(), entry); } - } - catch (Throwable t) { + } catch (Throwable t) { t.printStackTrace(); - } + } } @Override public void close() { - if (in != null) try { in.close(); } catch( Throwable t) {} + if (in != null) try { + in.close(); + } catch (Throwable t) { + } } public long getFilePointer() throws IOException { - return in.getFilePointer(); + return in.getFilePointer(); } - public void seek( long position) throws IOException { + public void seek(long position) throws IOException { in.seek(position); } public byte readByte() throws IOException { return in.readByte(); } - - public int readInt() throws IOException{ + + public int readInt() throws IOException { int result = 0; for (int i = 0; i < 4; i++) { result |= (in.readUnsignedByte() << (8 * i)); @@ -207,7 +205,7 @@ public class ZipInput implements Closeable return result; } - public String readString( int length) throws IOException { + public String readString(int length) throws IOException { byte[] buffer = new byte[length]; for (int i = 0; i < length; i++) { @@ -216,7 +214,7 @@ public class ZipInput implements Closeable return new String(buffer); } - public byte[] readBytes( int length) throws IOException { + public byte[] readBytes(int length) throws IOException { byte[] buffer = new byte[length]; for (int i = 0; i < length; i++) { @@ -225,8 +223,8 @@ public class ZipInput implements Closeable return buffer; } - public int read( byte[] b, int offset, int length) throws IOException { - return in.read( b, offset, length); + public int read(byte[] b, int offset, int length) throws IOException { + return in.read(b, offset, length); } } diff --git a/app/src/main/java/kellinwood/zipio/ZipListingHelper.java b/app/src/main/java/kellinwood/zipio/ZipListingHelper.java index 5e583b6b3..62812fda2 100644 --- a/app/src/main/java/kellinwood/zipio/ZipListingHelper.java +++ b/app/src/main/java/kellinwood/zipio/ZipListingHelper.java @@ -13,41 +13,39 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package kellinwood.zipio; -import java.util.Date; +import kellinwood.logging.LoggerInterface; + import java.text.DateFormat; import java.text.SimpleDateFormat; -import kellinwood.logging.LoggerInterface; -import kellinwood.logging.LoggerManager; +import java.util.Date; /** * */ -public class ZipListingHelper -{ +public class ZipListingHelper { static DateFormat dateFormat = new SimpleDateFormat("MM-dd-yy HH:mm"); - - public static void listHeader(LoggerInterface log) - { + + public static void listHeader(LoggerInterface log) { log.debug(" Length Method Size Ratio Date Time CRC-32 Name"); log.debug("-------- ------ ------- ----- ---- ---- ------ ----"); - + } - public static void listEntry(LoggerInterface log, ZioEntry entry) - { + public static void listEntry(LoggerInterface log, ZioEntry entry) { int ratio = 0; if (entry.getSize() > 0) ratio = (100 * (entry.getSize() - entry.getCompressedSize())) / entry.getSize(); log.debug(String.format("%8d %6s %8d %4d%% %s %08x %s", - entry.getSize(), - entry.getCompression() == 0 ? "Stored" : "Defl:N", - entry.getCompressedSize(), - ratio, - dateFormat.format( new Date( entry.getTime())), - entry.getCrc32(), - entry.getName())); + entry.getSize(), + entry.getCompression() == 0 ? "Stored" : "Defl:N", + entry.getCompressedSize(), + ratio, + dateFormat.format(new Date(entry.getTime())), + entry.getCrc32(), + entry.getName())); } } diff --git a/app/src/main/java/kellinwood/zipio/ZipOutput.java b/app/src/main/java/kellinwood/zipio/ZipOutput.java index 77627d971..d371bf2de 100644 --- a/app/src/main/java/kellinwood/zipio/ZipOutput.java +++ b/app/src/main/java/kellinwood/zipio/ZipOutput.java @@ -13,24 +13,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package kellinwood.zipio; +import kellinwood.logging.LoggerInterface; +import kellinwood.logging.LoggerManager; + import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; -import java.util.List; -import java.util.LinkedList; import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; import java.util.Set; -import kellinwood.logging.LoggerInterface; -import kellinwood.logging.LoggerManager; /** * */ -public class ZipOutput -{ +public class ZipOutput { static LoggerInterface log; @@ -40,71 +41,68 @@ public class ZipOutput List entriesWritten = new LinkedList(); Set namesWritten = new HashSet(); - - public ZipOutput( String filename) throws IOException - { + + public ZipOutput(String filename) throws IOException { this.outputFilename = filename; - File ofile = new File( outputFilename); + File ofile = new File(outputFilename); init(ofile); } - - public ZipOutput( File outputFile) throws IOException - { + + public ZipOutput(File outputFile) throws IOException { this.outputFilename = outputFile.getAbsolutePath(); File ofile = outputFile; init(ofile); } - - private void init( File ofile) throws IOException - { + + private void init(File ofile) throws IOException { if (ofile.exists()) ofile.delete(); - out = new FileOutputStream( ofile); - if (getLogger().isDebugEnabled()) ZipListingHelper.listHeader( getLogger()); - + out = new FileOutputStream(ofile); + if (getLogger().isDebugEnabled()) ZipListingHelper.listHeader(getLogger()); + } - public ZipOutput( OutputStream os) throws IOException - { + public ZipOutput(OutputStream os) throws IOException { out = os; } - + private static LoggerInterface getLogger() { if (log == null) log = LoggerManager.getLogger(ZipOutput.class.getName()); return log; } - public void write( ZioEntry entry) throws IOException { + public void write(ZioEntry entry) throws IOException { String entryName = entry.getName(); - if (namesWritten.contains( entryName)) { + if (namesWritten.contains(entryName)) { getLogger().warning("Skipping duplicate file in output: " + entryName); return; } - entry.writeLocalEntry( this); - entriesWritten.add( entry); - namesWritten.add( entryName); - if (getLogger().isDebugEnabled()) ZipListingHelper.listEntry( getLogger(), entry); - + entry.writeLocalEntry(this); + entriesWritten.add(entry); + namesWritten.add(entryName); + if (getLogger().isDebugEnabled()) ZipListingHelper.listEntry(getLogger(), entry); + } - - public void close() throws IOException - { + public void close() throws IOException { CentralEnd centralEnd = new CentralEnd(); - - centralEnd.centralStartOffset = (int)getFilePointer(); - centralEnd.numCentralEntries = centralEnd.totalCentralEntries = (short)entriesWritten.size(); - + + centralEnd.centralStartOffset = (int) getFilePointer(); + centralEnd.numCentralEntries = centralEnd.totalCentralEntries = (short) entriesWritten.size(); + for (ZioEntry entry : entriesWritten) { - entry.write( this); + entry.write(this); } - - centralEnd.centralDirectorySize = (int)(getFilePointer() - centralEnd.centralStartOffset); + + centralEnd.centralDirectorySize = (int) (getFilePointer() - centralEnd.centralStartOffset); centralEnd.fileComment = ""; - - centralEnd.write( this); - - if (out != null) try { out.close(); } catch( Throwable t) {} + + centralEnd.write(this); + + if (out != null) try { + out.close(); + } catch (Throwable t) { + } } public int getFilePointer() throws IOException { @@ -112,44 +110,44 @@ public class ZipOutput } - public void writeInt( int value) throws IOException{ + public void writeInt(int value) throws IOException { byte[] data = new byte[4]; for (int i = 0; i < 4; i++) { - data[i] = (byte)(value & 0xFF); + data[i] = (byte) (value & 0xFF); value = value >> 8; } - out.write( data); + out.write(data); filePointer += 4; } - public void writeShort( short value) throws IOException { + public void writeShort(short value) throws IOException { byte[] data = new byte[2]; for (int i = 0; i < 2; i++) { - data[i] = (byte)(value & 0xFF); - value = (short)(value >> 8); + data[i] = (byte) (value & 0xFF); + value = (short) (value >> 8); } - out.write( data); + out.write(data); filePointer += 2; } - public void writeString( String value) throws IOException { + public void writeString(String value) throws IOException { byte[] data = value.getBytes(); - out.write( data); + out.write(data); filePointer += data.length; } - public void writeBytes( byte[] value) throws IOException { + public void writeBytes(byte[] value) throws IOException { - out.write( value); + out.write(value); filePointer += value.length; } - public void writeBytes( byte[] value, int offset, int length) throws IOException { + public void writeBytes(byte[] value, int offset, int length) throws IOException { - out.write( value, offset, length); + out.write(value, offset, length); filePointer += length; - } + } }