diff options
Diffstat (limited to 'src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/packaging/sign/ManifestEntry.java')
-rw-r--r-- | src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/packaging/sign/ManifestEntry.java | 243 |
1 files changed, 243 insertions, 0 deletions
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/packaging/sign/ManifestEntry.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/packaging/sign/ManifestEntry.java new file mode 100644 index 0000000..b7223ec --- /dev/null +++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/packaging/sign/ManifestEntry.java @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.motorolamobility.studio.android.certmanager.packaging.sign; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Iterator; +import java.util.jar.Attributes; + +import org.bouncycastle.util.encoders.Base64Encoder; + +import com.motorola.studio.android.common.log.StudioLogger; + +/** + * A Class representing a Manifest Entry. + */ +public class ManifestEntry +{ + /** + * New line string according Jar specification + */ + public static final String MANIFEST_NEW_LINE = "\r\n"; + + public static final String ENTRY_NAME_ATTRIBUTE = "Name: "; + + public static final String UTF8_CHARSET = "UTF-8"; + + /** + * Safe line size limit according Jar Specification + */ + public static final int SAFE_LIMIT = 72; + + private final String name; + + private final Attributes attributes; + + /** + * Create a new ManifestEntry with the desired name and attributes + * @param name + * @param attr + */ + public ManifestEntry(String name, Attributes attr) + { + this.name = name; + this.attributes = attr; + } + + /** + * Get the manifest as it will be written in the Manifest file + * @return a byte array representing the manifest entry + */ + public byte[] toManifestEntryBytes() + { + byte[] result = null; + DataOutputStream dataOut = null; + ByteArrayOutputStream stream = null; + try + { + stream = new ByteArrayOutputStream(); + dataOut = new DataOutputStream(stream); + String nameField = wrap72bytes(ENTRY_NAME_ATTRIBUTE + name); + dataOut.writeBytes(nameField); + dataOut.writeBytes(MANIFEST_NEW_LINE); + dataOut.writeBytes(getAttributesString()); + dataOut.writeBytes(MANIFEST_NEW_LINE); + dataOut.writeBytes(MANIFEST_NEW_LINE); + result = stream.toString().getBytes(UTF8_CHARSET); + + } + catch (IOException e) + { + StudioLogger.error(ManifestEntry.class, "Error getting manifest like bytes"); + } + finally + { + try + { + if (dataOut != null) + { + dataOut.close(); + } + if (stream != null) + { + stream.close(); + } + } + catch (IOException e) + { + StudioLogger + .error("Could not close stream while writing manifest" + e.getMessage()); + } + } + + return result; + } + + /** + * Digest the entire entry + * @return a byte array with the SHA-1 sum of this entry + */ + public byte[] digest() + { + byte[] digested = null; + try + { + MessageDigest digester = MessageDigest.getInstance("SHA-1"); + digester.reset(); + digester.update(toManifestEntryBytes()); + digested = digester.digest(); + } + catch (NoSuchAlgorithmException e) + { + StudioLogger.error(ManifestEntry.class, "Error digesting manifest bytes"); + } + return digested; + } + + /** + * Get this Entry attributes as it will be written in the Manifest file + * @return + */ + private String getAttributesString() + { + StringBuilder builder = new StringBuilder(); + Iterator<Object> it = attributes.keySet().iterator(); + while (it.hasNext()) + { + Object next = it.next(); + String line = wrap72bytes(next + ": " + attributes.get(next)); + if (it.hasNext()) + { + line += MANIFEST_NEW_LINE; + } + builder.append(line); + } + return builder.toString(); + } + + /** + * Wrap a string into another string with at most 72 bytes per line + * According Jar spec, each line must have at most 72 bytes + * @param line + * @return the wrapped string + */ + public static String wrap72bytes(String line) + { + String returnString = line; + + if (line.length() > SAFE_LIMIT) + { + int maximumLength = SAFE_LIMIT - MANIFEST_NEW_LINE.length(); + StringBuilder wrapped = new StringBuilder(); + int index = maximumLength; + String first = line.substring(0, index); //get first SAFE_LIMIT - MANIFEST_NEW_LINE.length() bytes + first += MANIFEST_NEW_LINE; + wrapped.append(first); + while (index < line.length()) + { + String medium = " "; + if ((index + maximumLength) < line.length()) + { + medium += line.substring(index, index + maximumLength); //get the maximum length minus the blank space size + } + else + { + medium += line.substring(index); + } + wrapped.append(medium); + index += maximumLength; + } + returnString = wrapped.toString(); + } + + return returnString; + } + + /** + * Get this entry name + * @return this entry name + */ + public String getName() + { + return name; + } + + /** + * Get this entry ready to be written in the Signature File + * @return this entry ready to be written in the Signature File + * @throws IOException if some error occurs during encoding + */ + public String toDigestedManifestEntry() throws IOException + { + Base64Encoder encoder = new Base64Encoder(); + StringBuilder builder = new StringBuilder(); + ByteArrayOutputStream output = null; + + try + { + output = new ByteArrayOutputStream(); + + builder.append(wrap72bytes(ENTRY_NAME_ATTRIBUTE + name)); + builder.append(MANIFEST_NEW_LINE); + builder.append(ISignConstants.SHA1_DIGEST + ": "); + + byte[] digest = digest(); + encoder.encode(digest, 0, digest.length, output); + + builder.append(output.toString()); + builder.append(MANIFEST_NEW_LINE); + } + finally + { + if (output != null) + { + try + { + output.close(); + } + catch (IOException e) + { + StudioLogger.error("Could not close stream: " + e.getMessage()); + } + } + } + return builder.toString(); + } +} |