Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/tools/jar/SignatureFile.java
38918 views
/*1* Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.tools.jar;2627import java.io.*;28import java.util.*;29import java.security.*;3031import sun.net.www.MessageHeader;32import java.util.Base64;333435import sun.security.pkcs.*;36import sun.security.x509.AlgorithmId;3738/**39* <p>A signature file as defined in the <a40* href="manifest.html">Manifest and Signature Format</a>. It has41* essentially the same structure as a Manifest file in that it is a42* set of RFC 822 headers (sections). The first section contains meta43* data relevant to the entire file (i.e "Signature-Version:1.0") and44* each subsequent section contains data relevant to specific entries:45* entry sections.46*47* <p>Each entry section contains the name of an entry (which must48* have a counterpart in the manifest). Like the manifest it contains49* a hash, the hash of the manifest section corresponding to the50* name. Since the manifest entry contains the hash of the data, this51* is equivalent to a signature of the data, plus the attributes of52* the manifest entry.53*54* <p>This signature file format deal with PKCS7 encoded DSA signature55* block. It should be straightforward to extent to support other56* algorithms.57*58* @author David Brown59* @author Benjamin Renaud */6061public class SignatureFile {6263/* Are we debugging? */64static final boolean debug = false;6566/* list of headers that all pertain to a particular file in the67* archive */68private Vector<MessageHeader> entries = new Vector<>();6970/* Right now we only support SHA hashes */71static final String[] hashes = {"SHA"};7273static final void debug(String s) {74if (debug)75System.out.println("sig> " + s);76}7778/*79* The manifest we're working with. */80private Manifest manifest;8182/*83* The file name for the file. This is the raw name, i.e. the84* extention-less 8 character name (such as MYSIGN) which wil be85* used to build the signature filename (MYSIGN.SF) and the block86* filename (MYSIGN.DSA) */87private String rawName;8889/* The digital signature block corresponding to this signature90* file. */91private PKCS7 signatureBlock;929394/**95* Private constructor which takes a name a given signature96* file. The name must be extension-less and less or equal to 897* character in length. */98private SignatureFile(String name) throws JarException {99100entries = new Vector<>();101102if (name != null) {103if (name.length() > 8 || name.indexOf('.') != -1) {104throw new JarException("invalid file name");105}106rawName = name.toUpperCase(Locale.ENGLISH);107}108}109110/**111* Private constructor which takes a name a given signature file112* and a new file predicate. If it is a new file, a main header113* will be added. */114private SignatureFile(String name, boolean newFile)115throws JarException {116117this(name);118119if (newFile) {120MessageHeader globals = new MessageHeader();121globals.set("Signature-Version", "1.0");122entries.addElement(globals);123}124}125126/**127* Constructs a new Signature file corresponding to a given128* Manifest. All entries in the manifest are signed.129*130* @param manifest the manifest to use.131*132* @param name for this signature file. This should133* be less than 8 characters, and without a suffix (i.e.134* without a period in it.135*136* @exception JarException if an invalid name is passed in.137*/138public SignatureFile(Manifest manifest, String name)139throws JarException {140141this(name, true);142143this.manifest = manifest;144Enumeration<MessageHeader> enum_ = manifest.entries();145while (enum_.hasMoreElements()) {146MessageHeader mh = enum_.nextElement();147String entryName = mh.findValue("Name");148if (entryName != null) {149add(entryName);150}151}152}153154/**155* Constructs a new Signature file corresponding to a given156* Manifest. Specific entries in the manifest are signed.157*158* @param manifest the manifest to use.159*160* @param entries the entries to sign.161*162* @param filename for this signature file. This should163* be less than 8 characters, and without a suffix (i.e.164* without a period in it.165*166* @exception JarException if an invalid name is passed in.167*/168public SignatureFile(Manifest manifest, String[] entries,169String filename)170throws JarException {171this(filename, true);172this.manifest = manifest;173add(entries);174}175176/**177* Construct a Signature file from an input stream.178*179* @exception IOException if an invalid name is passed in or if a180* stream exception occurs.181*/182public SignatureFile(InputStream is, String filename)183throws IOException {184this(filename);185while (is.available() > 0) {186MessageHeader m = new MessageHeader(is);187entries.addElement(m);188}189}190191/**192* Construct a Signature file from an input stream.193*194* @exception IOException if an invalid name is passed in or if a195* stream exception occurs.196*/197public SignatureFile(InputStream is) throws IOException {198this(is, null);199}200201public SignatureFile(byte[] bytes) throws IOException {202this(new ByteArrayInputStream(bytes));203}204205/**206* Returns the name of the signature file, ending with a ".SF"207* suffix */208public String getName() {209return "META-INF/" + rawName + ".SF";210}211212/**213* Returns the name of the block file, ending with a block suffix214* such as ".DSA". */215public String getBlockName() {216String suffix = "DSA";217if (signatureBlock != null) {218SignerInfo info = signatureBlock.getSignerInfos()[0];219suffix = info.getDigestEncryptionAlgorithmId().getName();220String temp = AlgorithmId.getEncAlgFromSigAlg(suffix);221if (temp != null) suffix = temp;222}223return "META-INF/" + rawName + "." + suffix;224}225226/**227* Returns the signature block associated with this file.228*/229public PKCS7 getBlock() {230return signatureBlock;231}232233/**234* Sets the signature block associated with this file.235*/236public void setBlock(PKCS7 block) {237this.signatureBlock = block;238}239240/**241* Add a set of entries from the current manifest.242*/243public void add(String[] entries) throws JarException {244for (int i = 0; i < entries.length; i++) {245add (entries[i]);246}247}248249/**250* Add a specific entry from the current manifest.251*/252public void add(String entry) throws JarException {253MessageHeader mh = manifest.getEntry(entry);254if (mh == null) {255throw new JarException("entry " + entry + " not in manifest");256}257MessageHeader smh;258try {259smh = computeEntry(mh);260} catch (IOException e) {261throw new JarException(e.getMessage());262}263entries.addElement(smh);264}265266/**267* Get the entry corresponding to a given name. Returns null if268*the entry does not exist.269*/270public MessageHeader getEntry(String name) {271Enumeration<MessageHeader> enum_ = entries();272while(enum_.hasMoreElements()) {273MessageHeader mh = enum_.nextElement();274if (name.equals(mh.findValue("Name"))) {275return mh;276}277}278return null;279}280281/**282* Returns the n-th entry. The global header is a entry 0. */283public MessageHeader entryAt(int n) {284return entries.elementAt(n);285}286287/**288* Returns an enumeration of the entries.289*/290public Enumeration<MessageHeader> entries() {291return entries.elements();292}293294/**295* Given a manifest entry, computes the signature entry for this296* manifest entry.297*/298private MessageHeader computeEntry(MessageHeader mh) throws IOException {299MessageHeader smh = new MessageHeader();300301String name = mh.findValue("Name");302if (name == null) {303return null;304}305smh.set("Name", name);306307try {308for (int i = 0; i < hashes.length; ++i) {309MessageDigest dig = getDigest(hashes[i]);310ByteArrayOutputStream baos = new ByteArrayOutputStream();311PrintStream ps = new PrintStream(baos);312mh.print(ps);313byte[] headerBytes = baos.toByteArray();314byte[] digest = dig.digest(headerBytes);315smh.set(hashes[i] + "-Digest", Base64.getMimeEncoder().encodeToString(digest));316}317return smh;318} catch (NoSuchAlgorithmException e) {319throw new JarException(e.getMessage());320}321}322323private Hashtable<String, MessageDigest> digests = new Hashtable<>();324325private MessageDigest getDigest(String algorithm)326throws NoSuchAlgorithmException {327MessageDigest dig = digests.get(algorithm);328if (dig == null) {329dig = MessageDigest.getInstance(algorithm);330digests.put(algorithm, dig);331}332dig.reset();333return dig;334}335336337/**338* Add a signature file at current position in a stream339*/340public void stream(OutputStream os) throws IOException {341342/* the first header in the file should be the global one.343* It should say "SignatureFile-Version: x.x"; barf if not344*/345MessageHeader globals = entries.elementAt(0);346if (globals.findValue("Signature-Version") == null) {347throw new JarException("Signature file requires " +348"Signature-Version: 1.0 in 1st header");349}350351PrintStream ps = new PrintStream(os);352globals.print(ps);353354for (int i = 1; i < entries.size(); ++i) {355MessageHeader mh = entries.elementAt(i);356mh.print(ps);357}358}359}360361362