Package com.sun.enterprise.security.ssl

Source Code of com.sun.enterprise.security.ssl.JarSigner

/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2010-2012 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License").  You
* may not use this file except in compliance with the License.  You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt.  See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license."  If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above.  However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package com.sun.enterprise.security.ssl;

import com.sun.enterprise.server.pluggable.SecuritySupport;
import com.sun.enterprise.universal.GFBase64Encoder;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.KeyStoreException;
import java.security.SignatureException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import sun.security.pkcs.ContentInfo;
import sun.security.pkcs.PKCS7;
import sun.security.pkcs.SignerInfo;
import sun.security.x509.AlgorithmId;
import sun.security.x509.X500Name;

/**
* A utility class to sign jar files.
*
* @author Sudarsan Sridhar
*/
public class JarSigner {

    private static final GFBase64Encoder b64encoder = new GFBase64Encoder();
    private final MessageDigest md;
    private final String digestAlgorithm;
    private final String keyAlgorithm;
    private static final SecuritySupport secSupp = SecuritySupport.getDefaultInstance();

    public JarSigner(String digestAlgorithm, String keyAlgorithm)
            throws NoSuchAlgorithmException {
        this.digestAlgorithm = digestAlgorithm;
        this.keyAlgorithm = keyAlgorithm;
        this.md = MessageDigest.getInstance(digestAlgorithm);
    }

    public static void main(String[] args) throws Exception {
        File in = new File(args[0]);
        File out = new File(args[1]);
        new JarSigner("SHA1", "RSA").signJar(in, out, "s1as");
    }

    /**
     * Hash the string completely.
     *
     * @param content String to be hashed.
     * @return the hash.
     */
    private String hash(String content) {
        return b64encoder.encodeBuffer(md.digest(content.getBytes())).trim();
    }

    /**
     * Hash the JarEntry completely.
     *
     * @param jf the JarFile
     * @param je the JarEntry
     * @throws IOException
     * @return resulting hash
     */
    private String hash(JarFile jf, JarEntry je) throws IOException {
        byte[] data = readJarEntry(jf, je);
        return b64encoder.encodeBuffer(md.digest(data)).trim();
    }

    /**
     * Signs a jar.
     *
     * @param input input jar file
     * @param output output jar file
     * @param alias signing alias in the keystore
     */
    public void signJar(File input, File output, String alias)
            throws IOException, KeyStoreException, NoSuchAlgorithmException,
            InvalidKeyException, UnrecoverableKeyException, SignatureException {

        JarFile jf = new JarFile(input);
        ZipOutputStream zout = null;
        try {
            Enumeration<JarEntry> jes;
            // manifestEntries is content of META-INF/MANIFEST.MF
            StringBuilder manifestEntries = new StringBuilder();

            byte[] manifestContent = null;
            byte[] sigFileContent = getExistingSignatureFile(jf);
            boolean signed = (sigFileContent != null);

            if (!signed) {
                jes = jf.entries();// manifestHeader is header of META-INF/MANIFEST.MF, initialized to default
                Manifest manifest = retrieveManifest(jf);
                StringBuilder manifestHeader = new StringBuilder();
                appendAttributes(manifestHeader, manifest, null);
                // sigFileEntries is content of META-INF/ME.SF
                StringBuilder sigFileEntries = new StringBuilder();
                while (jes.hasMoreElements()) {
                    JarEntry je = jes.nextElement();
                    String name = je.getName();
                    if ((je.isDirectory() && manifest.getAttributes(name) == null)
                            || name.equals(JarFile.MANIFEST_NAME)) {
                        continue;
                    }

                    StringBuilder me = new StringBuilder();
                    StringBuilder currentLine = new StringBuilder();
                    // Create digest lines in MANIFEST.MF
                    currentLine.append("Name: ").append(name);
                    appendLine(me, currentLine);
                    currentLine.setLength(0);
                    me.append(digestAlgorithm).append("-Digest: ").append(hash(jf, je)).append("\r\n");
                    appendAttributes(me, manifest, name);
                    // Create digest lines in ME.SF
                    currentLine.append("Name: ").append(name);
                    appendLine(sigFileEntries, currentLine);
                    currentLine.setLength(0);
                    sigFileEntries.append(digestAlgorithm).append("-Digest: ").append(hash(me.toString())).append("\r\n\r\n");
                    manifestEntries.append(me);
                }


                // META-INF/ME.SF
                StringBuilder sigFile = new StringBuilder("Signature-Version: 1.0\r\n").append(digestAlgorithm).append("-Digest-Manifest-Main-Attributes: ").append(hash(manifestHeader.toString())).append("\r\n").append("Created-By: ").append(System.getProperty("java.version")).append(" (").append(System.getProperty("java.vendor")).append(")\r\n");
                // Combine header and content of MANIFEST.MF, and rehash
                manifestHeader.append(manifestEntries);
                sigFile.append(digestAlgorithm).append("-Digest-Manifest: ").append(hash(manifestHeader.toString())).append("\r\n\r\n");

                // Combine header and content of ME.SF
                sigFile.append(sigFileEntries);
                manifestContent = manifestHeader.toString().getBytes();
                sigFileContent = sigFile.toString().getBytes();
            } else {
                manifestContent = readJarEntry(jf,
                        jf.getJarEntry(JarFile.MANIFEST_NAME));
            }
            X509Certificate[] certChain = null;
            PrivateKey privKey = null;
            KeyStore[] ks = secSupp.getKeyStores();
            for (int i = 0; i < ks.length; i++) {
                privKey = secSupp.getPrivateKeyForAlias(alias, i);
                if (privKey != null) {
                    Certificate[] cs = ks[i].getCertificateChain(alias);
                    certChain = new X509Certificate[cs.length];
                    for (int j = 0; j < cs.length; j++) {
                        certChain[j] = (X509Certificate) cs[j];
                    }
                }
            }

            // Sign ME.SF
            Signature sig = Signature.getInstance(digestAlgorithm + "with" + keyAlgorithm);
            sig.initSign(privKey);
            sig.update(sigFileContent);

            // Create PKCS7 block
            PKCS7 pkcs7 = new PKCS7(
                    new AlgorithmId[]{AlgorithmId.get(digestAlgorithm)},
                    new ContentInfo(sigFileContent),
                    certChain,
                    new SignerInfo[]{new SignerInfo(
                        (X500Name) certChain[0].getIssuerDN(),
                        certChain[0].getSerialNumber(),
                        AlgorithmId.get(digestAlgorithm),
                        AlgorithmId.get(keyAlgorithm),
                        sig.sign())
                    });
            ByteArrayOutputStream bout = new ByteArrayOutputStream();
            pkcs7.encodeSignedData(bout);

            // Write output
            zout = new ZipOutputStream(
                    new FileOutputStream(output));
            zout.putNextEntry((signed)
                    ? getZipEntry(jf.getJarEntry(JarFile.MANIFEST_NAME))
                    : new ZipEntry(JarFile.MANIFEST_NAME));
            zout.write(manifestContent);

            zout.putNextEntry(new ZipEntry("META-INF/"
                    + alias.toUpperCase(Locale.US) + ".SF"));
            zout.write(sigFileContent);

            zout.putNextEntry(new ZipEntry("META-INF/"
                    + alias.toUpperCase(Locale.US) + "." + keyAlgorithm));
            zout.write(bout.toByteArray());

            jes = jf.entries();
            while (jes.hasMoreElements()) {
                JarEntry je = jes.nextElement();
                String name = je.getName();
                if (!name.equals(JarFile.MANIFEST_NAME)) {
                    zout.putNextEntry(getZipEntry(je));
                    byte[] data = readJarEntry(jf, je);
                    zout.write(data);
                }
            }

        } finally {
            jf.close();
      if (zout != null) {
                zout.close();
      }
        }
    }

    /**
     * Retrieve manifest from jar, create a default template if none exists.
     *
     * @param jf The jar file
     * @return The Manifest
     * @throws IOException
     */
    private Manifest retrieveManifest(JarFile jf) throws IOException {
        Manifest manifest = jf.getManifest();
        if (manifest == null) {
            manifest = new Manifest();
            Attributes mainAttributes = manifest.getMainAttributes();
            mainAttributes.putValue(Attributes.Name.MANIFEST_VERSION.toString(), "1.0");
            mainAttributes.putValue("Created-By", System.getProperty("java.version")
                    + " (" + System.getProperty("java.vendor") + ")");
        }
        Map<String, Attributes> entriesMap = manifest.getEntries();
        for (Iterator<String> entries = entriesMap.keySet().iterator(); entries.hasNext();) {
            if (jf.getJarEntry(entries.next()) == null) {
                entries.remove();
            }
        }
        return manifest;
    }

    /**
     * Add attributes for the current entry.
     *
     * @param manifestEntry - The StringBuilder to which the attributes are to be added.
     * @param manifest - The Jar Manifest Entry
     * @param entry - The named entry in the manifest. null means the Main Attribute section.
     * @return manifestEntry with attributes added.
     */
    private static StringBuilder appendAttributes(StringBuilder manifestEntry,
            Manifest manifest, String entry) {
        Attributes attributes = (entry == null)
                ? manifest.getMainAttributes() : manifest.getAttributes(entry);
        StringBuilder line = new StringBuilder();
        if (attributes != null) {
            for (Map.Entry attr : attributes.entrySet()) {
                line.append(attr.getKey().toString()).append(": ").append((String) attr.getValue());
                appendLine(manifestEntry, line);
                line.setLength(0);
            }
        }
        return manifestEntry.append("\r\n");
    }

    /**
     * Process a long manifest line and add continuation if required
     *
     * @param sb - The output string
     * @param line - The line to be processed.
     * @return sb with the line added.
     */
    private static StringBuilder appendLine(StringBuilder sb, StringBuilder line) {
        int begin = 0;
        for (int end = 70; line.length() - begin > 70; end += 69) {
            sb.append(line.subSequence(begin, end)).append("\r\n ");
            begin = end;
        }
        return sb.append(line.subSequence(begin, line.length())).append("\r\n");
    }

    /**
     * If jar is signed, return existing Signature file, else return null.
     *
     * @param jf The jar file
     * @return Signature file
     * @throws IOException
     */
    private static byte[] getExistingSignatureFile(JarFile jf) throws IOException {
        Enumeration<JarEntry> entries = jf.entries();
        JarEntry je = null;
        while (entries.hasMoreElements()) {
            JarEntry cje = entries.nextElement();
            if (cje.getName().startsWith("META-INF/") && cje.getName().endsWith(".SF")) {
                je = cje;
                break;
            }
        }
        return readJarEntry(jf, je);
    }

    /**
     * Read completely the bytes from Entry je of jarfile jf.
     *
     * @param jf the jar file
     * @param je the jar entry
     * @return bytes from je.
     * @throws IOException
     */
    private static byte[] readJarEntry(JarFile jf, JarEntry je) throws IOException {
        if (je == null) {
            return null;
        }
        byte[] data = new byte[(int) je.getSize()];
        InputStream jis = jf.getInputStream(je);
        int current;
        int idx = 0;
        while ((current = jis.read()) > -1) {
            data[idx++] = (byte) current;
        }
        return data;
    }

    /**
     * Get the ZipEntry for the given JarEntry. Added in order to suppress the
     * compressedSize field as it was causing errors
     *
     * @param je The jar entry.
     * @return ZipEntry with fields populated from the JarEntry.
     */
    private static ZipEntry getZipEntry(JarEntry je) {
        ZipEntry ze = new ZipEntry(je.getName());

        ze.setComment(je.getComment());
        ze.setCrc(je.getCrc());
        ze.setExtra(je.getExtra());
        ze.setMethod(je.getMethod());
        ze.setSize(je.getSize());
        ze.setTime(je.getTime());

        return ze;
    }
}
TOP

Related Classes of com.sun.enterprise.security.ssl.JarSigner

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.