001: /**
002: * JavaGuard -- an obfuscation package for Java classfiles.
003: *
004: * Copyright (c) 2002 Thorsten Heit (theit@gmx.de)
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2 of the License, or (at your option) any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: *
020: * The author may be contacted at theit@gmx.de.
021: *
022: *
023: * $Id: ManifestContainer.java,v 1.2 2002/05/27 12:35:10 glurk Exp $
024: */package net.sf.javaguard;
025:
026: import java.io.*;
027: import java.util.jar.Attributes;
028: import java.util.jar.Manifest;
029: import java.security.MessageDigest;
030: import net.sf.javaguard.classfile.ClassConstants;
031:
032: /** Manifest file container for the obfuscator. Manages the Manifest file entry
033: * names and attributes taken from at least one input stream so they can be
034: * manipulated by the obfuscator when the obfuscated contents are written to
035: * the output stream.
036: *
037: * @author <a href="mailto:theit@gmx.de">Thorsten Heit</a>
038: */
039: public class ManifestContainer {
040: /** Used when encoding a byte array as a Base64 string. */
041: private static final char[] base64 = { 'A', 'B', 'C', 'D', 'E',
042: 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q',
043: 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c',
044: 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
045: 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
046: '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' };
047: /** Used when encoding a byte array as a Base64 string. */
048: private static final char pad = '=';
049: /** Manifest file version number. */
050: private static final String MANIFEST_VERSION_VALUE = "1.0";
051: /** Manifest file digest algorithm tag. */
052: private static final String MANIFEST_DIGESTALG_TAG = "Digest-Algorithms";
053: /** Manifest file 'Created-By' tag. */
054: private static final String MANIFEST_CREATEDBY_TAG = "Created-By";
055:
056: /** Holds the Manifest. */
057: private Manifest manifest = null;
058:
059: /** Encode a byte[] as a Base64 string (see RFC1521, Section 5.2).
060: * @param b the byte array
061: * @return the Base64 string for the byte array
062: */
063: public static String toBase64(byte[] b) {
064: StringBuffer sb = new StringBuffer();
065: for (int ptr = 0; ptr < b.length; ptr += 3) {
066: sb.append(base64[(b[ptr] >> 2) & 0x3F]);
067: if (ptr + 1 < b.length) {
068: sb.append(base64[((b[ptr] << 4) & 0x30)
069: | ((b[ptr + 1] >> 4) & 0x0F)]);
070: if (ptr + 2 < b.length) {
071: sb.append(base64[((b[ptr + 1] << 2) & 0x3C)
072: | ((b[ptr + 2] >> 6) & 0x03)]);
073: sb.append(base64[b[ptr + 2] & 0x3F]);
074: } else {
075: sb.append(base64[(b[ptr + 1] << 2) & 0x3C]);
076: sb.append(pad);
077: }
078: } else {
079: sb.append(base64[((b[ptr] << 4) & 0x30)]);
080: sb.append(pad);
081: sb.append(pad);
082: }
083: }
084: return sb.toString();
085: }
086:
087: /** Creates a new empty manifest file container.
088: */
089: public ManifestContainer() {
090: }
091:
092: /** Creates a new manifest file container and adds the given manifest to the
093: * internal manifest container.
094: * @param manifest the manifest
095: */
096: public ManifestContainer(Manifest mf) {
097: addManifest(manifest);
098: }
099:
100: /** Adds the given Manifest to the manifest container. The entry names and
101: * attributes read will be merged in with the current manifest entries.
102: * @param mf the manifest to add
103: */
104: public void addManifest(Manifest mf) {
105: if (null != mf) {
106: try {
107: ByteArrayOutputStream os = new ByteArrayOutputStream();
108: mf.write(os);
109: byte[] bytes = os.toByteArray();
110: os.close();
111: ByteArrayInputStream is = new ByteArrayInputStream(
112: bytes);
113: getManifest().read(is);
114: is.close();
115: } catch (IOException ioex) {
116: // shouldn't occur because we're not really writing to a disk
117: System.err.println("Unexpected I/O exception:"
118: + ioex.getMessage());
119: ioex.printStackTrace();
120: }
121: }
122: }
123:
124: /** Writes the Manifest to the specified OutputStream.
125: * Attributes.Name.MANIFEST_VERSION must be set in
126: * MainAttributes prior to invoking this method.
127: * @param os the output stream
128: * @throws IOException if an I/O error has occurred
129: */
130: public void write(OutputStream os) throws IOException {
131: // Make sure that there's always a version header tag
132: String vername = Attributes.Name.MANIFEST_VERSION.toString();
133: String version = getManifest().getMainAttributes().getValue(
134: vername);
135: if (null == version) {
136: version = MANIFEST_VERSION_VALUE;
137: getManifest().getMainAttributes()
138: .putValue(vername, version);
139: }
140: // Add a 'Created-By' entry to the main attributes
141: getManifest().getMainAttributes().putValue(
142: MANIFEST_CREATEDBY_TAG, Version.getProgramName());
143:
144: getManifest().write(os);
145: }
146:
147: /** Update an entry in the manifest file.
148: * @param inName the file name of the original file
149: * @param outName the output file name
150: * @param digests the message digests
151: */
152: public void updateManifest(String oldName, String newName,
153: MessageDigest[] digests) {
154: Attributes attrs = (Attributes) getManifest().getAttributes(
155: oldName);
156: if (null == attrs) {
157: attrs = new Attributes();
158: }
159:
160: // Create fresh digest entries
161: if (digests != null && digests.length > 0) {
162: // Digest-Algorithms header
163: StringBuffer sb = new StringBuffer();
164: for (int i = 0; i < digests.length; i++) {
165: if (i > 0) {
166: sb.append(" ");
167: }
168: sb.append(digests[i].getAlgorithm());
169: }
170: attrs.putValue(MANIFEST_DIGESTALG_TAG, sb.toString());
171:
172: // add the message digests to the attributes list
173: for (int i = 0; i < digests.length; i++) {
174: attrs.putValue(digests[i].getAlgorithm() + "-Digest",
175: toBase64(digests[i].digest()));
176: }
177: }
178: // replace the attributes
179: getManifest().getEntries().remove(oldName);
180: getManifest().getEntries().put(newName, attrs);
181:
182: // check whether the main attributes contain a "Main-Class" entry that
183: // matches the old name if it is a class
184: if (oldName.endsWith(ClassConstants.CLASS_EXT)) {
185: Attributes mainAttrs = getManifest().getMainAttributes();
186: if (null != mainAttrs) {
187: String str = mainAttrs
188: .getValue(Attributes.Name.MAIN_CLASS);
189: int len = ClassConstants.CLASS_EXT.length();
190: if (null != str
191: && str.equals(oldName.substring(0, oldName
192: .length()
193: - len))) {
194: mainAttrs.put(Attributes.Name.MAIN_CLASS, newName
195: .substring(0, newName.length() - len));
196: }
197: }
198: }
199: }
200:
201: /** Returns the current Manifest file parser.
202: * @return current Manifest file parser
203: */
204: private Manifest getManifest() {
205: if (null == manifest) {
206: manifest = new Manifest();
207: }
208: return manifest;
209: }
210: }
|