001/**
002 * Copyright 2019 Emmanuel Bourg and contributors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package net.jsign;
018
019import java.io.File;
020import java.io.IOException;
021import java.nio.charset.Charset;
022import java.security.MessageDigest;
023import java.util.List;
024
025import org.bouncycastle.asn1.ASN1Object;
026import org.bouncycastle.cms.CMSSignedData;
027
028import net.jsign.mscab.MSCabinetFile;
029import net.jsign.msi.MSIFile;
030import net.jsign.pe.PEFile;
031import net.jsign.script.JScript;
032import net.jsign.script.PowerShellScript;
033import net.jsign.script.PowerShellXMLScript;
034import net.jsign.script.VBScript;
035import net.jsign.script.WindowsScript;
036
037/**
038 * A file that can be signed with Authenticode.
039 *
040 * @author Emmanuel Bourg
041 */
042public interface Signable {
043
044    /**
045     * Computes the digest of the file.
046     * 
047     * @param digest the message digest to update
048     * @return the digest of the file
049     * @throws IOException if an I/O error occurs
050     */
051    byte[] computeDigest(MessageDigest digest) throws IOException;
052
053    /**
054     * Creates the SpcIndirectDataContent structure containing the digest of the file.
055     * 
056     * @param digestAlgorithm the digest algorithm to use
057     * @return the SpcIndirectDataContent structure in ASN.1 format
058     * @throws IOException if an I/O error occurs
059     */
060    ASN1Object createIndirectData(DigestAlgorithm digestAlgorithm) throws IOException;
061
062    /**
063     * Returns the Authenticode signatures on the file.
064     * 
065     * @return the signatures
066     * @throws IOException if an I/O error occurs
067     */
068    List<CMSSignedData> getSignatures() throws IOException;
069
070    /**
071     * Sets the signature of the file, overwriting the previous one.
072     * 
073     * @param signature the signature to put
074     * @throws IOException if an I/O error occurs
075     */
076    void setSignature(CMSSignedData signature) throws IOException;
077
078    /**
079     * Saves the file.
080     * 
081     * @throws IOException if an I/O error occurs
082     */
083    void save() throws IOException;
084
085    /**
086     * Returns a signable object for the file specified.
087     *
088     * @param file the file that is intended to to be signed
089     * @return the signable object for the specified file
090     * @throws IOException if an I/O error occurs
091     * @throws UnsupportedOperationException if the file specified isn't supported
092     */
093    static Signable of(File file) throws IOException {
094        return of(file, null);
095    }
096
097    /**
098     * Returns a signable object for the file specified.
099     *
100     * @param file     the file that is intended to to be signed
101     * @param encoding the character encoding (for text files only).
102     *                 If the file has a byte order mark this parameter is ignored.
103     * @return the signable object for the specified file
104     * @throws IOException if an I/O error occurs
105     * @throws UnsupportedOperationException if the file specified isn't supported
106     */
107    static Signable of(File file, Charset encoding) throws IOException {
108        if (PEFile.isPEFile(file)) {
109            return new PEFile(file);
110
111        } else if (MSIFile.isMSIFile(file)) {
112            return new MSIFile(file);
113
114        } else if (MSCabinetFile.isMSCabinetFile(file)) {
115            return new MSCabinetFile(file);
116
117        } else if (file.getName().endsWith(".ps1")
118                || file.getName().endsWith(".psd1")
119                || file.getName().endsWith(".psm1")) {
120            return new PowerShellScript(file, encoding);
121
122        } else if (file.getName().endsWith(".ps1xml")) {
123            return new PowerShellXMLScript(file, encoding);
124
125        } else if (file.getName().endsWith(".vbs")
126                || file.getName().endsWith(".vbe")) {
127            return new VBScript(file, encoding);
128
129        } else if (file.getName().endsWith(".js")
130                || file.getName().endsWith(".jse")) {
131            return new JScript(file, encoding);
132
133        } else if (file.getName().endsWith(".wsf")) {
134            return new WindowsScript(file, encoding);
135
136        } else {
137            throw new UnsupportedOperationException("Unsupported file: " + file);
138        }
139    }
140}