diff options
author | Andrew John Hughes <gnu_andrew@member.fsf.org> | 2006-03-26 20:08:09 +0000 |
---|---|---|
committer | Andrew John Hughes <gnu_andrew@member.fsf.org> | 2006-03-26 20:08:09 +0000 |
commit | c7fc43440df4ee0406f8483ff717949056f81845 (patch) | |
tree | d1933cdedc07bb3dea4f9e8becfa47c46c928a7e /tools/gnu/classpath | |
parent | 7afa402d5269bad9cf422c4c2369c51ef76ded8b (diff) | |
download | classpath-c7fc43440df4ee0406f8483ff717949056f81845.tar.gz |
2006-03-26 Andrew John Hughes <gnu_andrew@member.fsf.org>
* Merge from Classpath HEAD --> generics for the period
2005/03/07 to the branch tag generics_merge_20050326.
Diffstat (limited to 'tools/gnu/classpath')
-rw-r--r-- | tools/gnu/classpath/tools/giop/GRMIC.java | 4 | ||||
-rw-r--r-- | tools/gnu/classpath/tools/giop/GRMIC.txt | 3 | ||||
-rw-r--r-- | tools/gnu/classpath/tools/giop/README | 3 | ||||
-rw-r--r-- | tools/gnu/classpath/tools/giop/grmic/GiopRmicCompiler.java | 15 | ||||
-rw-r--r-- | tools/gnu/classpath/tools/jarsigner/HashUtils.java | 122 | ||||
-rw-r--r-- | tools/gnu/classpath/tools/jarsigner/JarSigner.java | 169 | ||||
-rw-r--r-- | tools/gnu/classpath/tools/jarsigner/JarVerifier.java | 336 | ||||
-rw-r--r-- | tools/gnu/classpath/tools/jarsigner/Main.java | 457 | ||||
-rw-r--r-- | tools/gnu/classpath/tools/jarsigner/SFHelper.java | 373 | ||||
-rw-r--r-- | tools/gnu/classpath/tools/jarsigner/jarsigner.txt | 101 | ||||
-rw-r--r-- | tools/gnu/classpath/tools/rmi/RMIC.java | 4 | ||||
-rw-r--r-- | tools/gnu/classpath/tools/rmi/RMIC.txt | 2 |
12 files changed, 1587 insertions, 2 deletions
diff --git a/tools/gnu/classpath/tools/giop/GRMIC.java b/tools/gnu/classpath/tools/giop/GRMIC.java index 16ff96f56..bb0ef9cdc 100644 --- a/tools/gnu/classpath/tools/giop/GRMIC.java +++ b/tools/gnu/classpath/tools/giop/GRMIC.java @@ -105,6 +105,10 @@ public class GRMIC verbose = true; compiler.setVerbose(true); } + else if (c.equals("-force")) + { + compiler.setForce(true); + } else if (c.equals("-d")) { int f = i + 1; diff --git a/tools/gnu/classpath/tools/giop/GRMIC.txt b/tools/gnu/classpath/tools/giop/GRMIC.txt index fcde83895..08aaf148f 100644 --- a/tools/gnu/classpath/tools/giop/GRMIC.txt +++ b/tools/gnu/classpath/tools/giop/GRMIC.txt @@ -19,6 +19,9 @@ Usage: grmic <options> <class names> -help Print this help text -v Print version -verbose Verbose output + -force Try to generate code even if the input classes seem not + consistent with RMI specification. + and <class names> can include one or more non abstract classes that implement Remote and are accessible via current class path. diff --git a/tools/gnu/classpath/tools/giop/README b/tools/gnu/classpath/tools/giop/README index f1be7b674..94fc2f158 100644 --- a/tools/gnu/classpath/tools/giop/README +++ b/tools/gnu/classpath/tools/giop/README @@ -9,7 +9,8 @@ The list of the currently available tools: * GRMIC - RMI-IIOP stub and tie generator. * NameService - GIOP transient naming service (this tool is called tnameserv in Sun's package). -* NameService - GIOP persistent naming service (this tool is called +* NameServicePersistent + - GIOP persistent naming service (this tool is called orbd in Sun's package). * IorParser - Parses the stringified form of the interoperable object references (IOR's). diff --git a/tools/gnu/classpath/tools/giop/grmic/GiopRmicCompiler.java b/tools/gnu/classpath/tools/giop/grmic/GiopRmicCompiler.java index c19f635c6..62456fd51 100644 --- a/tools/gnu/classpath/tools/giop/grmic/GiopRmicCompiler.java +++ b/tools/gnu/classpath/tools/giop/grmic/GiopRmicCompiler.java @@ -115,6 +115,11 @@ public class GiopRmicCompiler * Verbose output */ protected boolean verbose = false; + + /** + * Force mode - do not check the exceptions + */ + protected boolean force = false; /** * Clear data, preparing for the next compilation. @@ -204,7 +209,7 @@ public class GiopRmicCompiler remEx = true; break; } - if (! remEx) + if (! remEx && !force) throw new CompilationError(m[i].getName() + ", defined in " + c.getName() + ", does not throw " @@ -483,6 +488,14 @@ public class GiopRmicCompiler { warnings = warn; } + + /** + * Set the error ignore mode. + */ + public void setForce(boolean isforce) + { + force = isforce; + } /** * Get the package name. diff --git a/tools/gnu/classpath/tools/jarsigner/HashUtils.java b/tools/gnu/classpath/tools/jarsigner/HashUtils.java new file mode 100644 index 000000000..7591b3c57 --- /dev/null +++ b/tools/gnu/classpath/tools/jarsigner/HashUtils.java @@ -0,0 +1,122 @@ +/* Utils.java -- Utility methods for JAR file signing/verification + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.jarsigner; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.util.logging.Logger; + +import gnu.java.security.hash.Sha160; +import gnu.java.security.util.Base64; +import gnu.java.util.jar.JarUtils; + +/** + * Collection of utility methods used in JAR file signing and verification. + */ +class HashUtils +{ + private static final Logger log = Logger.getLogger(HashUtils.class.getName()); + private Sha160 sha = new Sha160(); + + // default 0-arguments constructor + + /** + * @param stream the input stream to digest. + * @return a base-64 representation of the resulting SHA-1 digest of the + * contents of the designated input stream. + * @throws IOException if an I/O related exception occurs during the process. + */ + String hashStream(InputStream stream) throws IOException + { + BufferedInputStream bis = new BufferedInputStream(stream, 4096); + byte[] buffer = new byte[4096]; + int count = 0; + int n; + while ((n = bis.read(buffer)) != - 1) + if (n > 0) + { + sha.update(buffer, 0, n); + count += n; + } + + byte[] hash = sha.digest(); + log.finest("Hashed " + count + " byte(s)"); + String result = Base64.encode(hash); + return result; + } + + /** + * @param ba the byte array to digest. + * @return a base-64 representation of the resulting SHA-1 digest of the + * contents of the designated buffer. + */ + String hashByteArray(byte[] ba) throws IOException + { + sha.update(ba); + byte[] hash = sha.digest(); + log.finest("Hashed " + ba.length + " byte(s)"); + String result = Base64.encode(hash); + return result; + } + + /** + * @param name the JAR entry name + * @param entryHash the hash of the entry file which appears in the + * manifest. + * @return the base-64 encoded form of the hash of the corresponding Manifest + * JAR entry which will appear in the SF file under the entry with the + * same name. + * @throws UnsupportedEncodingException If UTF-8 character encoding is not + * supported on this platform. + */ + String hashManifestEntry(String name, String entryHash) + throws UnsupportedEncodingException + { + sha.update((JarUtils.NAME + ": " + name).getBytes("UTF-8")); + sha.update(JarUtils.CRLF); + sha.update((Main.DIGEST + ": " + entryHash).getBytes("UTF-8")); + sha.update(JarUtils.CRLF); + sha.update(JarUtils.CRLF); + byte[] sfHash = sha.digest(); + String result = Base64.encode(sfHash); + return result; + } +} diff --git a/tools/gnu/classpath/tools/jarsigner/JarSigner.java b/tools/gnu/classpath/tools/jarsigner/JarSigner.java new file mode 100644 index 000000000..8babdcc30 --- /dev/null +++ b/tools/gnu/classpath/tools/jarsigner/JarSigner.java @@ -0,0 +1,169 @@ +/* JarSigner.java -- The signing handler of the gjarsigner tool + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.jarsigner; + +import gnu.classpath.SystemProperties; +import gnu.java.util.jar.JarUtils; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.cert.CRLException; +import java.security.cert.CertificateEncodingException; +import java.util.Enumeration; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarOutputStream; +import java.util.logging.Logger; + +/** + * The JAR signing handler of the <code>gjarsigner</code> tool. + */ +public class JarSigner +{ + private static final Logger log = Logger.getLogger(JarSigner.class.getName()); + /** The owner tool of this handler. */ + private Main main; + + JarSigner(Main main) + { + super(); + + this.main = main; + } + + void start() throws IOException, CertificateEncodingException, CRLException + { + log.entering("JarSigner", "start"); + + JarFile jarFile = new JarFile(main.getJarFileName()); + SFHelper sfHelper = new SFHelper(jarFile); + + sfHelper.startSigning(); + + // 1. compute the digests + for (Enumeration e = jarFile.entries(); e.hasMoreElements(); ) + { + JarEntry je = (JarEntry) e.nextElement(); + String jeName = je.getName(); + if (jeName.equals(JarFile.MANIFEST_NAME) + || jeName.endsWith(File.separator)) + continue; + + sfHelper.updateEntry(je); + if (main.isVerbose()) + System.out.println(" signing: " + jeName); + } + + sfHelper.finishSigning(main.isSectionsOnly()); + if (main.isVerbose()) + System.out.println(" updating: " + JarFile.MANIFEST_NAME); + + // 2. write jar entries and manifest + File signedJarFile = File.createTempFile("gcp-", ".jar"); + FileOutputStream fos = new FileOutputStream(signedJarFile); + JarOutputStream outSignedJarFile = new JarOutputStream(fos, + sfHelper.getManifest()); + for (Enumeration e = jarFile.entries(); e.hasMoreElements(); ) + { + JarEntry je = (JarEntry) e.nextElement(); + String jeName = je.getName(); + if (jeName.equals(JarFile.MANIFEST_NAME) + || jeName.endsWith(File.separator)) + continue; + + log.finest("Processing " + jeName); + JarEntry newEntry = new JarEntry(jeName); + newEntry.setTime(je.getTime()); + outSignedJarFile.putNextEntry(newEntry); + InputStream jeis = jarFile.getInputStream(je); + copyFromTo(jeis, outSignedJarFile); + } + + // 3. create the .SF file + String signaturesFileName = main.getSigFileName(); + String sfFileName = JarUtils.META_INF + signaturesFileName + + JarUtils.SF_SUFFIX; + log.finest("Processing " + sfFileName); + JarEntry sfEntry = new JarEntry(sfFileName); + sfEntry.setTime(System.currentTimeMillis()); + outSignedJarFile.putNextEntry(sfEntry); + sfHelper.writeSF(outSignedJarFile); + log.info("Created .SF file"); + if (main.isVerbose()) + System.out.println(" adding: " + sfFileName); + + // 4. create the .DSA file + String dsaFileName = JarUtils.META_INF + signaturesFileName + + JarUtils.DSA_SUFFIX; + log.finest("Processing " + dsaFileName); + JarEntry dsaEntry = new JarEntry(dsaFileName); + dsaEntry.setTime(System.currentTimeMillis()); + outSignedJarFile.putNextEntry(dsaEntry); + sfHelper.writeDSA(outSignedJarFile, + main.getSignerPrivateKey(), + main.getSignerCertificateChain(), + main.isInternalSF()); + log.info("Created .DSA file"); + if (main.isVerbose()) + System.out.println(" adding: " + dsaFileName); + + // cleanup + outSignedJarFile.close(); + fos.close(); + signedJarFile.renameTo(new File(main.getSignedJarFileName())); + log.info("Renamed signed JAR file"); + if (main.isVerbose()) + System.out.println(SystemProperties.getProperty("line.separator") + + "jar signed."); + + log.exiting("JarSigner", "start"); + } + + private void copyFromTo(InputStream in, JarOutputStream out) + throws IOException + { + byte[] buffer = new byte[8192]; + int n; + while ((n = in.read(buffer)) != -1) + if (n > 0) + out.write(buffer, 0, n); + } +} diff --git a/tools/gnu/classpath/tools/jarsigner/JarVerifier.java b/tools/gnu/classpath/tools/jarsigner/JarVerifier.java new file mode 100644 index 000000000..06ebe64fe --- /dev/null +++ b/tools/gnu/classpath/tools/jarsigner/JarVerifier.java @@ -0,0 +1,336 @@ +/* JarVerifier.java -- The verification handler of the gjarsigner tool + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.jarsigner; + +import gnu.java.security.OID; +import gnu.java.security.Registry; +import gnu.java.security.pkcs.PKCS7SignedData; +import gnu.java.security.pkcs.SignerInfo; +import gnu.java.security.sig.ISignature; +import gnu.java.security.sig.ISignatureCodec; +import gnu.java.security.sig.dss.DSSSignature; +import gnu.java.security.sig.dss.DSSSignatureX509Codec; +import gnu.java.security.sig.rsa.RSAPKCS1V1_5Signature; +import gnu.java.security.sig.rsa.RSAPKCS1V1_5SignatureX509Codec; +import gnu.java.security.util.Util; +import gnu.java.util.jar.JarUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.security.PublicKey; +import java.security.cert.Certificate; +import java.security.cert.CRLException; +import java.security.cert.CertificateException; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.logging.Logger; +import java.util.zip.ZipException; + +/** + * The JAR verification handler of the <code>gjarsigner</code> tool. + */ +public class JarVerifier +{ + private static final Logger log = Logger.getLogger(JarVerifier.class.getName()); + /** The owner tool of this handler. */ + private Main main; + private HashUtils util = new HashUtils(); + /** The JAR file to verify. */ + private JarFile jarFile; + /** Map of jar entry names to their hash. */ + private Map entryHashes = new HashMap(); + + JarVerifier(Main main) + { + super(); + + this.main = main; + } + + void start() throws IOException, CRLException, CertificateException + { + log.entering("JarVerifier", "start"); + + String jarFileName = main.getJarFileName(); + jarFile = new JarFile(jarFileName); + + // 1. find all signature (.SF) files + List sfFiles = new ArrayList(); + for (Enumeration e = jarFile.entries(); e.hasMoreElements(); ) + { + JarEntry je = (JarEntry) e.nextElement(); + String jeName = je.getName(); + if (! (jeName.startsWith(JarUtils.META_INF) + && jeName.endsWith(JarUtils.SF_SUFFIX))) + continue; + + // only interested in .SF files in, and not deeper than, META-INF + String[] jeNameParts = jeName.split("/"); + if (jeNameParts.length != 2) + continue; + + String sfName = jeNameParts[1]; + String sigFileName = sfName.substring(0, sfName.length() - 3); + sfFiles.add(sigFileName); + } + + // 2. verify each one + if (sfFiles.isEmpty()) + System.out.println("jar is not signed.--no signature files found."); + else + { + int limit = sfFiles.size(); + int count = 0; + for (Iterator it = sfFiles.iterator(); it.hasNext(); ) + { + String alias = (String) it.next(); + if (verifySF(alias)) + if (verifySFEntries(alias)) + count++; + } + + if (count == 0) + System.out.println("jar verification failed."); + else if (count != limit) + System.out.println("jar partially verified --" + count + " of " + + limit + " signers."); + else + System.out.println("jar verified --" + limit + " signer(s)."); + } + + log.exiting("JarVerifier", "start"); + } + + /** + * @param sigFileName the name of the signature file; i.e. the name to use for + * both the .SF and .DSA files. + * @return <code>true</code> if the designated file-name (usually a key-store + * <i>alias</i> name) has been successfully checked as the signer of the + * corresponding <code>.SF</code> file. Returns <code>false</code> otherwise. + * @throws IOException + * @throws ZipException + * @throws CertificateException + * @throws CRLException + */ + private boolean verifySF(String sigFileName) throws CRLException, + CertificateException, ZipException, IOException + { + log.entering("JarVerifier", "verifySF"); + log.finest("About to verify signature of " + sigFileName + "..."); + + // 1. find the corresponding .DSA file for this .SF file + JarEntry dsaEntry = jarFile.getJarEntry(JarUtils.META_INF + sigFileName + + JarUtils.DSA_SUFFIX); + if (dsaEntry == null) + throw new SecurityException("Signature Block missing for " + sigFileName); + + // 2. read the .DSA file contents as a PKCS7 SignedData + InputStream in = jarFile.getInputStream(dsaEntry); + PKCS7SignedData pkcs7SignedData = new PKCS7SignedData(in); + + // 4. get the encrypted digest octet string from the first SignerInfo + // this octet string is the digital signature of the .SF file contents + Set signerInfos = pkcs7SignedData.getSignerInfos(); + if (signerInfos == null || signerInfos.isEmpty()) + throw new SecurityException("At least one SignerInfo element MUST be " + + "present in a Signature Block (.DSA file)"); + SignerInfo signerInfo = (SignerInfo) signerInfos.iterator().next(); + byte[] encryptedDigest = signerInfo.getEncryptedDigest(); + if (encryptedDigest == null) + throw new SecurityException("Missing EncryptedDigest in Signature Block " + + "(.DSA file) first SignerInfo element"); + log.finest("\n" + Util.dumpString(encryptedDigest, "--- signedSFBytes ")); + + // 5. get the signer public key + Certificate cert = pkcs7SignedData.getCertificates()[0]; + PublicKey verifierKey = cert.getPublicKey(); + log.finest("--- verifier public key = " + verifierKey); + + // 6. verify the signature file signature + OID digestEncryptionAlgorithmOID = signerInfo.getDigestEncryptionAlgorithmId(); + ISignature signatureAlgorithm; + ISignatureCodec signatureCodec; + if (digestEncryptionAlgorithmOID.equals(Main.DSA_SIGNATURE_OID)) + { + signatureAlgorithm = new DSSSignature(); + signatureCodec = new DSSSignatureX509Codec(); + } + else + { + signatureAlgorithm = new RSAPKCS1V1_5Signature(Registry.MD5_HASH); + signatureCodec = new RSAPKCS1V1_5SignatureX509Codec(); + } + + Map signatureAttributes = new HashMap(); + signatureAttributes.put(ISignature.VERIFIER_KEY, verifierKey); + signatureAlgorithm.setupVerify(signatureAttributes); + + Object herSignature = signatureCodec.decodeSignature(encryptedDigest); + + // 7. verify the signature file contents + JarEntry sfEntry = jarFile.getJarEntry(JarUtils.META_INF + sigFileName + + JarUtils.SF_SUFFIX); + in = jarFile.getInputStream(sfEntry); + byte[] buffer = new byte[2048]; + int n; + while ((n = in.read(buffer)) != -1) + if (n > 0) + signatureAlgorithm.update(buffer, 0, n); + + boolean result = signatureAlgorithm.verify(herSignature); + log.info("Signature block [" + sigFileName + "] is " + + (result ? "" : "NOT ") + "OK"); + + log.exiting("JarVerifier", "verifySF", new Boolean(result)); + return result; + } + + /** + * This method is called after at least one signer (usually a key-store + * <code>alias</code> name) was found to be trusted; i.e. his/her signature + * block in the corresponding <code>.DSA</code> file was successfully + * verified using his/her public key. + * <p> + * This method, uses the contents of the corresponding <code>.SF</code> file + * to compute and verify the hashes of the manifest entries in the JAR file. + * + * @param alias the name of the signature file; i.e. the name to use for both + * the .SF and .DSA files. + * @return <code>true</code> if all the entries in the corresponding + * <code>.SF</code> file have the same hash values as their + * alter-ego in the <i>manifest</i> file of the JAR file inquestion. + * @throws IOException if an I/O related exception occurs during the process. + */ + private boolean verifySFEntries(String alias) throws IOException + { + log.entering("JarVerifier", "verifySFEntries"); + + // 1. read the signature file + JarEntry jarEntry = jarFile.getJarEntry(JarUtils.META_INF + alias + + JarUtils.SF_SUFFIX); + InputStream in = jarFile.getInputStream(jarEntry); + Attributes attr = new Attributes(); + Map entries = new HashMap(); + JarUtils.readSFManifest(attr, entries, in); + + // 2. The .SF file by default includes a header containing a hash of the + // entire manifest file. When the header is present, then the verification + // can check to see whether or not the hash in the header indeed matches + // the hash of the manifest file. + boolean result = false; + String hash = attr.getValue(Main.DIGEST_MANIFEST_ATTR); + if (hash != null) + result = verifyManifest(hash); + + // A verification is still considered successful if none of the files that + // were in the JAR file when the signature was generated have been changed + // since then, which is the case if the hashes in the non-header sections + // of the .SF file equal the hashes of the corresponding sections in the + // manifest file. + // + // 3. Read each file in the JAR file that has an entry in the .SF file. + // While reading, compute the file's digest, and then compare the result + // with the digest for this file in the manifest section. The digests + // should be the same, or verification fails. + if (! result) + for (Iterator it = entries.keySet().iterator(); it.hasNext();) + { + Entry me = (Entry) it.next(); + String name = (String) me.getKey(); + attr = (Attributes) me.getValue(); + hash = attr.getValue(Main.DIGEST_ATTR); + result = verifySFEntry(name, hash); + if (! result) + break; + } + + log.exiting("JarVerifier", "verifySFEntries", new Boolean(result)); + return result; + } + + /** + * @param hash Base-64 encoded form of the manifest's digest. + * @return <code>true</code> if our computation of the manifest's hash + * matches the given value; <code>false</code> otherwise. + * @throws IOException if unable to acquire the JAR's manifest entry. + */ + private boolean verifyManifest(String hash) throws IOException + { + return verifySFEntry(JarFile.MANIFEST_NAME, hash); + } + + /** + * @param name the name of a JAR entry to verify. + * @param hash Base-64 encoded form of the designated entry's digest. + * @return <code>true</code> if our computation of the JAR entry's hash + * matches the given value; <code>false</code> otherwise. + * @throws IOException if an exception occurs while returning the entry's + * input stream. + */ + private boolean verifySFEntry(String name, String hash) throws IOException + { + String expectedValue = getEntryHash(JarFile.MANIFEST_NAME); + boolean result = expectedValue.equalsIgnoreCase(hash); + log.finest("Is " + name + " OK? " + result); + return result; + } + + private String getEntryHash(String entryName) throws IOException + { + String result = (String) entryHashes.get(entryName); + if (result == null) + { + JarEntry manifest = jarFile.getJarEntry(entryName); + InputStream in = jarFile.getInputStream(manifest); + result = util.hashStream(in); + entryHashes.put(entryName, result); + } + + return result; + } +} diff --git a/tools/gnu/classpath/tools/jarsigner/Main.java b/tools/gnu/classpath/tools/jarsigner/Main.java new file mode 100644 index 000000000..df5c3bb08 --- /dev/null +++ b/tools/gnu/classpath/tools/jarsigner/Main.java @@ -0,0 +1,457 @@ +/* Main.java -- JAR signing and verification tool not unlike jarsigner + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.jarsigner; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.security.Key; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.UnrecoverableKeyException; +import java.security.cert.CRLException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.util.Locale; +import java.util.jar.Attributes.Name; +import java.util.logging.Logger; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; + +import gnu.classpath.SystemProperties; +import gnu.java.security.OID; +import gnu.java.security.Registry; +import gnu.javax.security.auth.callback.ConsoleCallbackHandler; + +/** + * The GNU Classpath implementation of the <i>jarsigner</i> tool. + * <p> + * The <i>jarsigner</i> tool is used to sign and verify JAR (Java ARchive) + * files. + * <p> + * This implementation is intended to be compatible with the behaviour + * described in the public documentation of the same tool included in JDK 1.4. + */ +public class Main +{ + private static final Logger log = Logger.getLogger(Main.class.getName()); + private static final String HELP_PATH = "jarsigner/jarsigner.txt"; + private static final Locale EN_US_LOCALE = new Locale("en", "US"); + static final String DIGEST = "SHA1-Digest"; + static final String DIGEST_MANIFEST = "SHA1-Digest-Manifest"; + static final Name DIGEST_ATTR = new Name(DIGEST); + static final Name DIGEST_MANIFEST_ATTR = new Name(DIGEST_MANIFEST); + static final OID DSA_SIGNATURE_OID = new OID(Registry.DSA_OID_STRING); + static final OID RSA_SIGNATURE_OID = new OID(Registry.RSA_OID_STRING); + + private boolean verify; + private String ksURL; + private String ksType; + private String password; + private String ksPassword; + private String sigFileName; + private String signedJarFileName; + private boolean verbose; + private boolean certs; + private boolean internalSF; + private boolean sectionsOnly; + private String providerClassName; + private String jarFileName; + private String alias; + + private Provider provider; + private char[] ksPasswordChars; + private KeyStore store; + private char[] passwordChars; + private PrivateKey signerPrivateKey; + private Certificate[] signerCertificateChain; + + private Main(String[] args) throws KeyStoreException, InstantiationException, + IllegalAccessException, ClassNotFoundException, NoSuchAlgorithmException, + CertificateException, IOException, UnrecoverableKeyException, + UnsupportedCallbackException + { + super(); + + processArgs(args); + } + + public static final void main(String[] args) throws IOException, + CRLException, CertificateException + { + log.entering("Main", "main", args); + + Main tool; + try + { + tool = new Main(args); + tool.start(); + } + catch (SecurityException x) + { + log.throwing("Main", "main", x); + System.err.println("jarsigner: " + x.getMessage()); + } + catch (Exception x) + { + log.throwing("Main", "main", x); + System.err.println("jarsigner error: " + x); + } + + log.exiting("Main", "main"); + // System.exit(0); + } + + // helper methods ----------------------------------------------------------- + + /** + * @param args + * @throws KeyStoreException + * @throws ClassNotFoundException + * @throws IllegalAccessException + * @throws InstantiationException + * @throws IOException + * @throws CertificateException + * @throws NoSuchAlgorithmException + * @throws UnsupportedCallbackException + * @throws UnrecoverableKeyException + */ + private void processArgs(String[] args) throws KeyStoreException, + InstantiationException, IllegalAccessException, ClassNotFoundException, + NoSuchAlgorithmException, CertificateException, IOException, + UnrecoverableKeyException, UnsupportedCallbackException + { + log.entering("Main", "processArgs", args); + + int limit = args.length; + log.finest("args.length=" + limit); + int i = 0; + String opt; + while (i < limit) + { + opt = args[i++]; + log.finest("args[" + (i - 1) + "]=" + opt); + if (opt == null || opt.length() == 0) + continue; + + if ("-verify".equals(opt)) // -verify + verify = true; + else if ("-keystore".equals(opt)) // -keystore URL + ksURL = args[i++]; + else if ("-storetype".equals(opt)) // -storetype STORE_TYPE + ksType = args[i++]; + else if ("-storepass".equals(opt)) // -storepass PASSWORD + ksPassword = args[i++]; + else if ("-keypass".equals(opt)) // -keypass PASSWORD + password = args[i++]; + else if ("-sigfile".equals(opt)) // -sigfile NAME + sigFileName = args[i++]; + else if ("-signedjar".equals(opt)) // -signedjar FILE_NAME + signedJarFileName = args[i++]; + else if ("-verbose".equals(opt)) // -verbose + verbose = true; + else if ("-certs".equals(opt)) // -certs + certs = true; + else if ("-internalsf".equals(opt)) // -internalsf + internalSF = true; + else if ("-sectionsonly".equals(opt)) // -sectionsonly + sectionsOnly = true; + else if ("-provider".equals(opt)) // -provider PROVIDER_CLASS_NAME + providerClassName = args[i++]; + else + { + jarFileName = opt; + if (! verify) + alias = args[i++]; + + break; + } + } + + if (i < limit) // more options than needed + log.warning("Last argument is assumed at index #" + (i - 1) + + ". Remaining arguments (" + args[i] + + "...) will be ignored"); + + setupCommonParams(); + if (verify) + { + log.info("Will verify with the following parameters:"); + log.info(" jar-file = '" + jarFileName + "'"); + log.info("Options:"); + log.info(" provider = '" + providerClassName + "'"); + log.info(" verbose ? " + verbose); + log.info(" certs ? " + certs); + log.info(" internalsf ? " + internalSF); + log.info(" sectionsonly ? " + sectionsOnly); + } + else // sign + { + setupSigningParams(); + + log.info("Will sign with the following parameters:"); + log.info(" jar-file = '" + jarFileName + "'"); + log.info(" alias = '" + alias + "'"); + log.info("Options:"); + log.info(" keystore = '" + ksURL + "'"); + log.info(" storetype = '" + ksType + "'"); + log.info(" storepass = '" + ksPassword + "'"); + log.info(" keypass = '" + password + "'"); + log.info(" sigfile = '" + sigFileName + "'"); + log.info(" signedjar = '" + signedJarFileName + "'"); + log.info(" provider = '" + providerClassName + "'"); + log.info(" verbose ? " + verbose); + log.info(" internalsf ? " + internalSF); + log.info(" sectionsonly ? " + sectionsOnly); + } + + log.exiting("Main", "processArgs"); + } + + private void start() throws IOException, CRLException, CertificateException + { + log.entering("Main", "start"); + + if (verify) + { + JarVerifier jv = new JarVerifier(this); + jv.start(); + } + else + { + JarSigner js = new JarSigner(this); + js.start(); + } + + log.exiting("Main", "start"); + } + + private void setupCommonParams() throws InstantiationException, + IllegalAccessException, ClassNotFoundException, IOException + { + log.entering("Main", "setupCommonParams"); + + File jar = new File(jarFileName); + if (! jar.exists()) + throw new FileNotFoundException(jarFileName); + + if (jar.isDirectory()) + throw new IOException("JAR file [" + jarFileName + + "] is NOT a file object"); + if (! jar.canRead()) + throw new IOException("JAR file [" + jarFileName + "] is NOT readable"); + + if (providerClassName != null && providerClassName.length() > 0) + provider = (Provider) Class.forName(providerClassName).newInstance(); + + if (! verbose && certs) + { + log.warning("Option <certs> is set but <verbose> is not. Ignored"); + certs = false; + } + + log.exiting("Main", "setupCommonParams"); + } + + private void setupSigningParams() throws KeyStoreException, IOException, + NoSuchAlgorithmException, CertificateException, + UnsupportedCallbackException, UnrecoverableKeyException + { + log.entering("Main", "setupSigningParams"); + + if (ksURL == null || ksURL.trim().length() == 0) + { + String userHome = SystemProperties.getProperty("user.home"); + if (userHome == null || userHome.trim().length() == 0) + throw new SecurityException("Option '-keystore' is not defined or" + + " is an empty string, and 'user.home'" + + " is unknown"); + ksURL = "file:" + userHome.trim() + "/.keystore"; + } + else + { + ksURL = ksURL.trim(); + if (ksURL.indexOf(":") == -1) + ksURL = "file:" + ksURL; + } + + if (ksType == null || ksType.trim().length() == 0) + ksType = KeyStore.getDefaultType(); + else + ksType = ksType.trim(); + + if (provider != null) + store = KeyStore.getInstance(ksType, provider); + else + store = KeyStore.getInstance(ksType); + + if (ksPassword == null) + { + // ask the user to provide one + CallbackHandler handler = new ConsoleCallbackHandler(); + PasswordCallback pcb = new PasswordCallback("Enter keystore password: ", + false); + handler.handle(new Callback[] { pcb }); + ksPasswordChars = pcb.getPassword(); + } + else + ksPasswordChars = ksPassword.toCharArray(); + + URL url = new URL(ksURL); + InputStream stream = url.openStream(); + store.load(stream, ksPasswordChars); + + if (! store.containsAlias(alias)) + throw new SecurityException("Designated alias [" + alias + + "] MUST be known to the key store in use"); + if (! store.isKeyEntry(alias)) + throw new SecurityException("Designated alias [" + alias + + "] MUST be an Alias of a Key Entry"); + Key key; + if (password == null) + { + passwordChars = ksPasswordChars; + try + { + key = store.getKey(alias, passwordChars); + } + catch (UnrecoverableKeyException x) + { + // ask the user to provide one + CallbackHandler handler = new ConsoleCallbackHandler(); + PasswordCallback pcb = new PasswordCallback("Enter key password for " + + alias + ": ", false); + handler.handle(new Callback[] { pcb }); + passwordChars = pcb.getPassword(); + // take 2 + key = store.getKey(alias, passwordChars); + } + } + else + { + passwordChars = password.toCharArray(); + key = store.getKey(alias, passwordChars); + } + + if (! (key instanceof PrivateKey)) + throw new SecurityException("Key associated with " + alias + + " MUST be a private key"); + signerPrivateKey = (PrivateKey) key; + signerCertificateChain = store.getCertificateChain(alias); + log.finest(String.valueOf(signerCertificateChain)); + + if (sigFileName == null) + sigFileName = alias; + + sigFileName = sigFileName.toUpperCase(EN_US_LOCALE); + if (sigFileName.length() > 8) + sigFileName = sigFileName.substring(0, 8); + + char[] chars = sigFileName.toCharArray(); + for (int i = 0; i < chars.length; i++) + { + char c = chars[i]; + if (! (Character.isLetter(c) + || Character.isDigit(c) + || c == '_' + || c == '-')) + chars[i] = '_'; + } + + sigFileName = new String(chars); + + if (signedJarFileName == null) + signedJarFileName = jarFileName; + + log.exiting("Main", "setupSigningParams"); + } + + boolean isVerbose() + { + return verbose; + } + + boolean isCerts() + { + return certs; + } + + String getSigFileName() + { + return this.sigFileName; + } + + String getJarFileName() + { + return this.jarFileName; + } + + boolean isSectionsOnly() + { + return this.sectionsOnly; + } + + boolean isInternalSF() + { + return this.internalSF; + } + + PrivateKey getSignerPrivateKey() + { + return this.signerPrivateKey; + } + + Certificate[] getSignerCertificateChain() + { + return signerCertificateChain; + } + + String getSignedJarFileName() + { + return this.signedJarFileName; + } +} diff --git a/tools/gnu/classpath/tools/jarsigner/SFHelper.java b/tools/gnu/classpath/tools/jarsigner/SFHelper.java new file mode 100644 index 000000000..cbf5f356b --- /dev/null +++ b/tools/gnu/classpath/tools/jarsigner/SFHelper.java @@ -0,0 +1,373 @@ +/* SFHelper -- A .SF file helper + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.jarsigner; + +import gnu.java.security.OID; +import gnu.java.security.Registry; +import gnu.java.security.der.DER; +import gnu.java.security.der.DERValue; +import gnu.java.security.pkcs.PKCS7Data; +import gnu.java.security.pkcs.PKCS7SignedData; +import gnu.java.security.pkcs.SignerInfo; +import gnu.java.security.sig.ISignature; +import gnu.java.security.sig.ISignatureCodec; +import gnu.java.security.sig.dss.DSSSignature; +import gnu.java.security.sig.dss.DSSSignatureX509Codec; +import gnu.java.security.sig.rsa.RSAPKCS1V1_5Signature; +import gnu.java.security.sig.rsa.RSAPKCS1V1_5SignatureX509Codec; +import gnu.java.security.util.Util; +import gnu.java.util.jar.JarUtils; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.math.BigInteger; +import java.security.PrivateKey; +import java.security.cert.CRLException; +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509CRL; +import java.security.interfaces.DSAPrivateKey; +import java.security.interfaces.RSAPrivateKey; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; +import java.util.logging.Logger; + +import javax.security.auth.x500.X500Principal; +import java.security.cert.X509Certificate; + +/** + * A helper class for the .SF file found in signed jars. + */ +public class SFHelper +{ + // Constants and fields + // -------------------------------------------------------------------------- + + private static final Logger log = Logger.getLogger(SFHelper.class.getName()); + private static final int READY = 0; + private static final int STARTED = 1; + private static final int FINISHED = 2; + private static final int SF_GENERATED = 3; + private static final int DSA_GENERATED = 4; + /** http://asn1.elibel.tm.fr/cgi-bin/oid/display?oid=1.3.14.3.2.26&action=display */ + private static final OID hashAlgorithmIdentifierSHA1 = new OID("1.3.14.3.2.26"); + + private int state; + private JarFile jar; + private Manifest manifest; + private Attributes sfMainAttributes; + private Map sfEntries; + private byte[] sfBytes; + private HashUtils util; + + // Constructor(s) + // -------------------------------------------------------------------------- + + /** + * @param jar the JAR archive the .SF file belongs to. + */ + public SFHelper(JarFile jar) + { + super(); + + this.jar = jar; + this.state = READY; + } + + // Class methods + // -------------------------------------------------------------------------- + + // Instance methods + // -------------------------------------------------------------------------- + + /** + * Writes the contents of the <code>.SF</code> file to the designated JAR + * output stream. Line-endings are platform-independent and consist of the + * 2-codepoint sequence <code>0x0D</code> and <code>0x0A</code>. + * + * @param jar the JAR output stream to write a <code>.SF</code> file to. + * @throws IOException if an I/O related exception occurs during the process. + */ + void writeSF(JarOutputStream jar) throws IOException + { + if (this.state != FINISHED) + throw new IllegalStateException("Helper is NOT finished"); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + JarUtils.writeSFManifest(sfMainAttributes, sfEntries, baos); + sfBytes = baos.toByteArray(); + log.finest("\n" + Util.dumpString(sfBytes, "+++ sfBytes ")); + jar.write(sfBytes); + jar.flush(); + + this.state = SF_GENERATED; + } + + /** + * The contents of the .DSA file is the DER encoded form of a PKCS#7 + * ContentInfo of the type SignedData. + * <p> + * The ContentInfo ASN.1 syntax is as described in the "PKCS#7 Cryptographic + * Message Syntax Standard" (RSA Labs) specifications: + * <pre> + * ContentInfo ::= SEQUENCE { + * contentType ContentType, + * content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL + * } + * + * ContentType ::= OBJECT IDENTIFIER + * </pre> + * <p> + * The ContentType is an OID which determines the type of the contents field + * that follows it. For the .DSA file the OID is "1.2.840.113549.1.7.2", while + * the content field is the byte array representing the DER encoded form of a + * SignedData content-type. The ASN.1 syntax of the SignedData type is as + * follows: + * <pre> + * SignedData ::= SEQUENCE { + * version Version, -- always 1 for PKCS#7 1.5 + * digestAlgorithms DigestAlgorithmIdentifiers, + * contentInfo ContentInfo, + * certificates [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL, + * crls [1] IMPLICIT CertificateRevocationLists OPTIONAL, + * signerInfos SignerInfos + * } + * + * DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier + * + * SignerInfos ::= SET OF SignerInfo + * </pre> + * <p> + * Finally the SignerInfo is a per-signer structure. Its ASN.1 syntax looks + * like so: + * <pre> + * SignerInfo ::= SEQUENCE { + * version Version, -- always 1 for PKCS#7 1.5 + * issuerAndSerialNumber IssuerAndSerialNumber, + * digestAlgorithm DigestAlgorithmIdentifier, + * authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL, + * digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier, + * encryptedDigest EncryptedDigest, + * unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL + * } + * + * EncryptedDigest ::= OCTET STRING + * </pre> + * + * @param jar the JAR output stream to write a <code>.DSA</code> file to. + * @param signerKey the private key to sign with. + * @param certificates the possibly null signer certificate chain. + * @param internalSF if <code>true</code> then include the .SF file contents + * in the signed .DSA file; otherwise don't. + * @throws IOException if an I/O related exception occurs during the process. + * @throws CRLException + * @throws CertificateEncodingException + */ + void writeDSA(JarOutputStream jar, PrivateKey signerKey, + Certificate[] certificates, boolean internalSF) + throws IOException, CertificateEncodingException, CRLException + { + if (this.state != SF_GENERATED) + throw new IllegalStateException(".SF file has NOT been generated"); + + log.finest("+++ signer private key = " + signerKey); + ISignature signatureAlgorithm; + ISignatureCodec signatureCodec; + OID digestEncryptionAlgorithmOID; + if (signerKey instanceof DSAPrivateKey) + { + signatureAlgorithm = new DSSSignature(); + signatureCodec = new DSSSignatureX509Codec(); + digestEncryptionAlgorithmOID = Main.DSA_SIGNATURE_OID; + } + else if (signerKey instanceof RSAPrivateKey) + { + signatureAlgorithm = new RSAPKCS1V1_5Signature(Registry.MD5_HASH); + signatureCodec = new RSAPKCS1V1_5SignatureX509Codec(); + digestEncryptionAlgorithmOID = Main.RSA_SIGNATURE_OID; + } + else + throw new SecurityException("Unknown or unsupported private key algorithm"); + + Map signatureAttributes = new HashMap(); + signatureAttributes.put(ISignature.SIGNER_KEY, signerKey); + signatureAlgorithm.setupSign(signatureAttributes); + signatureAlgorithm.update(sfBytes, 0, sfBytes.length); + Object signature = signatureAlgorithm.sign(); + byte[] signedSFBytes = signatureCodec.encodeSignature(signature); + log.finest("\n" + Util.dumpString(signedSFBytes, "+++ signedSFBytes ")); + + Set digestAlgorithms = new HashSet(); + List digestAlgorithm = new ArrayList(2); + DERValue derDigestAlgorithmOID = new DERValue(DER.OBJECT_IDENTIFIER, + hashAlgorithmIdentifierSHA1); + DERValue derDigestAlgorithmParams = new DERValue(DER.NULL, null); + digestAlgorithm.add(derDigestAlgorithmOID); + digestAlgorithm.add(derDigestAlgorithmParams); + DERValue derDigestAlgorithm = new DERValue(DER.CONSTRUCTED | DER.SEQUENCE, + digestAlgorithm); + digestAlgorithms.add(derDigestAlgorithm); + + // TODO (rsn): test with internalsf == true + PKCS7Data data = internalSF ? new PKCS7Data(sfBytes) : null; + + X509CRL[] crls = null; + + Set signerInfos = new HashSet(); + X509Certificate cert = (X509Certificate) certificates[0]; + X500Principal issuer = cert.getIssuerX500Principal(); + BigInteger serialNumber = cert.getSerialNumber(); + byte[] authenticatedAttributes = null; + byte[] encryptedDigest = signedSFBytes; + byte[] unauthenticatedAttributes = null; + SignerInfo signerInfo = new SignerInfo(issuer, + serialNumber, + hashAlgorithmIdentifierSHA1, + authenticatedAttributes, + digestEncryptionAlgorithmOID, + encryptedDigest, + unauthenticatedAttributes); + signerInfos.add(signerInfo); + + PKCS7SignedData dsaContents = new PKCS7SignedData(digestAlgorithms, + data, + certificates, + crls, + signerInfos); + dsaContents.encode(jar); + + jar.flush(); + this.state = DSA_GENERATED; + } + + Manifest getManifest() + { + return this.manifest; + } + + // own methods -------------------------------------------------------------- + + void startSigning() throws IOException + { + if (this.state != READY) + throw new IllegalStateException("Helper is NOT ready"); + + Manifest oldManifest = jar.getManifest(); + this.manifest = oldManifest == null ? new Manifest() + : new Manifest(oldManifest); + this.sfMainAttributes = new Attributes(); + this.sfEntries = new HashMap(); + util = new HashUtils(); + + this.state = STARTED; + } + + /** + * Hashes the designated JAR entry (the file itself); adds the resulting hash + * as an attribute to the manifest, and computes the hash of the added (to + * the Manifest) two headers and add the result as an attribute of the + * corresponding entry in the .SF file. + */ + void updateEntry(JarEntry entry) throws IOException + { + if (this.state != STARTED) + throw new IllegalStateException("Helper is NOT started"); + + String name = entry.getName(); + InputStream jeis = jar.getInputStream(entry); + String hash = util.hashStream(jeis); + log.finer("Hash of " + name + " = " + hash); + + Attributes mainfestAttributes = manifest.getAttributes(name); + if (mainfestAttributes == null) + { + mainfestAttributes = new Attributes(); + manifest.getEntries().put(name, mainfestAttributes); + } + + mainfestAttributes.putValue(Main.DIGEST_ATTR, hash); + + // hash the newly added 2-header block and add it as an attribute to .SF + + String sfHash = util.hashManifestEntry(name, hash); + Attributes sfAttributes = (Attributes) sfEntries.get(name); + if (sfAttributes == null) + { + sfAttributes = new Attributes(); + sfEntries.put(name, sfAttributes); + } + + sfAttributes.putValue(Main.DIGEST_ATTR, sfHash); + log.finest("Name: " + name); + log.finest(Main.DIGEST + ": " + sfHash); + log.finest(""); + } + + /** + * @param sectionsOnly whether to compute, in addition to the files, the hash + * of the mainfest itself (<code>false</code>) or not (<code>true</code>). + */ + void finishSigning(boolean sectionsOnly) throws IOException + { + if (state != STARTED) + throw new IllegalStateException("Helper is NOT started"); + + if (sectionsOnly) + return; + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + manifest.write(baos); + baos.flush(); + String manifestHash = util.hashByteArray(baos.toByteArray()); + log.fine("Hashed Manifest " + manifestHash); + sfMainAttributes.putValue(Main.DIGEST_MANIFEST_ATTR, manifestHash); + + this.state = FINISHED; + } +} diff --git a/tools/gnu/classpath/tools/jarsigner/jarsigner.txt b/tools/gnu/classpath/tools/jarsigner/jarsigner.txt new file mode 100644 index 000000000..167f6301e --- /dev/null +++ b/tools/gnu/classpath/tools/jarsigner/jarsigner.txt @@ -0,0 +1,101 @@ +Java ARchive (JAR) file signing and verification tool. + +Copyright 2006 Free Software Foundation, Inc. +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +Please report bugs at http://www.gnu.org/software/classpath/bugs.html + +Usage: + jarsigner [options] jar-file alias + jarsigner -verify [options] jar-file + + When the first form is used, the tool signs the designated <jar-file>. The + second form, on the other hand, is used to verify a previously signed JAR + file. + + The <jar-file> is the JAR file to process; i.e. to sign if the first syntax + form is used, or to verify if the second syntax form is used instead. + + <alias> is the Keystore alias to use for signing the <jar-file>. + + + When the tool is used for signing a JAR file, the possible <options> include: + -keystore URL + Indicates to the tool that the Keystore located at the + designated <URL> must be used. When this option is missing, + the tool, by default, will look for a Keystore named + ".keystore" in the current User's home directory; i.e. the + value of the System property named "user.home". + + If the <URL> is malformed --e.g. missing protocol element-- the + tool will attempt to use the <URL> value as a file-name (with + absolute or relative path-name) of a Keystore --as if the + protocol was "file:". + + -storetype STORE_TYPE + Designates the type of Keystore to expect. The default value + is that of the property "keystore.type" in the security + properties file, which is obtained by invoking the static + method call getDefaultType() in java.security.KeyStore. + + -storepass PASSWORD + Designates the <PASSWORD> to use when accessing the Keystore. + If this option is missing, the User will be prompted to provide + one. + + -keypass PASSWORD + Designates the <PASSWORD> protecting the private key to use, + from the Keystore, for signing the JAR file. If this option is + missing, the User will be prompted to provide one. + + -sigfile NAME + Designates a literal that will be used to construct file names + for the .SF and .DSA signature files which will be generated + and placed in the MET-INF directory of the signed JAR. + Permissible characters for <NAME> must be in the range + "a-zA-Z0-9_-". All characters will be converted by the tool to + upper-case ones. + + If this option is missing, the first eight characters of the + <alias> argument will be used. When this is the case, any + character in <alias> that is outside the permissible range of + characters will be replaced by an underscore. + + -signedjar FILE_NAME + If present, <FILE_NAME> will be used as the name of the signed + JAR. If this option is not present, then the signed JAR will + be named the same as <jar-file>; i.e. the input JAR will be + replaced with the signed one. + + + When the tool is used for verifying a JAR file, the possible options include: + -verify Indicates that the tool is to be used for verification purposes. + + -certs This option is used in conjunction with the -verbose option. + When present, along with the -verbose option, the tool will + print more detailed information about the certificates of the + signer(s) being processed. + + + Other options, common to both signing and verification include: + -verbose Specifies that the tool should generate more messages, during + its processing. + + -internalsf When present, the tool will include --which otherwise it does + not-- the .SF file in the .DSA generated file. + + -sectionsonly + When present, the tool will include in the .SF generated file + --which otherwise it does not-- a header containing a hash of + the whole manifest file. When that header is included, the + tool can quickly check, during verification, if the hash (in + the header) matches or not the manifest file. + + -provider PROVIDER_CLASS_NAME + Designates an implementation of the Provider interface to use + for obtaining cryptographic algorithm implementations required + by this tool to perform its functions; specifically the + implementation of the Security Provider capable of managing a + Key Store of the designated, or default, type. + diff --git a/tools/gnu/classpath/tools/rmi/RMIC.java b/tools/gnu/classpath/tools/rmi/RMIC.java index 09021dd3f..ffcd0db0e 100644 --- a/tools/gnu/classpath/tools/rmi/RMIC.java +++ b/tools/gnu/classpath/tools/rmi/RMIC.java @@ -114,6 +114,10 @@ public class RMIC verbose = true; compiler.setVerbose(true); } + else if (c.equals("-force")) + { + compiler.setForce(true); + } else if (c.equals("-d")) { int f = i + 1; diff --git a/tools/gnu/classpath/tools/rmi/RMIC.txt b/tools/gnu/classpath/tools/rmi/RMIC.txt index df1de9ea6..7ec371e9a 100644 --- a/tools/gnu/classpath/tools/rmi/RMIC.txt +++ b/tools/gnu/classpath/tools/rmi/RMIC.txt @@ -16,6 +16,8 @@ Usage: rmic <options> <class names> -help Print this help text -v Print version -verbose Verbose output + -force Try to generate code even if the input classes seem not + consistent with RMI specification. -1.2 Generate v 1.2 stubs (default)* |