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.Closeable;
020import java.io.File;
021import java.io.IOException;
022import java.nio.charset.Charset;
023import java.security.MessageDigest;
024import java.util.List;
025
026import org.bouncycastle.asn1.ASN1Object;
027import org.bouncycastle.asn1.cms.ContentInfo;
028import org.bouncycastle.cms.CMSSignedData;
029
030import net.jsign.asn1.authenticode.AuthenticodeObjectIdentifiers;
031import net.jsign.cat.CatalogFile;
032import net.jsign.mscab.MSCabinetFile;
033import net.jsign.msi.MSIFile;
034import net.jsign.pe.PEFile;
035import net.jsign.script.JScript;
036import net.jsign.script.PowerShellScript;
037import net.jsign.script.PowerShellXMLScript;
038import net.jsign.script.VBScript;
039import net.jsign.script.WindowsScript;
040
041/**
042 * A file that can be signed with Authenticode.
043 *
044 * @author Emmanuel Bourg
045 */
046public interface Signable extends Closeable {
047
048    /**
049     * Creates the ContentInfo structure to be signed.
050     *
051     * @param digestAlgorithm the digest algorithm to use
052     * @return the ContentInfo structure in ASN.1 format
053     * @throws IOException if an I/O error occurs
054     * @since 4.2
055     */
056    default ContentInfo createContentInfo(DigestAlgorithm digestAlgorithm) throws IOException {
057        return new ContentInfo(AuthenticodeObjectIdentifiers.SPC_INDIRECT_DATA_OBJID, createIndirectData(digestAlgorithm));
058    }
059
060    /**
061     * Computes the digest of the file.
062     * 
063     * @param digest the message digest to update
064     * @return the digest of the file
065     * @throws IOException if an I/O error occurs
066     */
067    byte[] computeDigest(MessageDigest digest) throws IOException;
068
069    /**
070     * Creates the SpcIndirectDataContent structure containing the digest of the file.
071     * 
072     * @param digestAlgorithm the digest algorithm to use
073     * @return the SpcIndirectDataContent structure in ASN.1 format
074     * @throws IOException if an I/O error occurs
075     */
076    ASN1Object createIndirectData(DigestAlgorithm digestAlgorithm) throws IOException;
077
078    /**
079     * Returns the Authenticode signatures on the file.
080     * 
081     * @return the signatures
082     * @throws IOException if an I/O error occurs
083     */
084    List<CMSSignedData> getSignatures() throws IOException;
085
086    /**
087     * Sets the signature of the file, overwriting the previous one.
088     * 
089     * @param signature the signature to put
090     * @throws IOException if an I/O error occurs
091     */
092    void setSignature(CMSSignedData signature) throws IOException;
093
094    /**
095     * Saves the file.
096     * 
097     * @throws IOException if an I/O error occurs
098     */
099    void save() throws IOException;
100
101    /**
102     * Returns a signable object for the file specified.
103     *
104     * @param file the file that is intended to to be signed
105     * @return the signable object for the specified file
106     * @throws IOException if an I/O error occurs
107     * @throws UnsupportedOperationException if the file specified isn't supported
108     */
109    static Signable of(File file) throws IOException {
110        return of(file, null);
111    }
112
113    /**
114     * Returns a signable object for the file specified.
115     *
116     * @param file     the file that is intended to to be signed
117     * @param encoding the character encoding (for text files only).
118     *                 If the file has a byte order mark this parameter is ignored.
119     * @return the signable object for the specified file
120     * @throws IOException if an I/O error occurs
121     * @throws UnsupportedOperationException if the file specified isn't supported
122     */
123    static Signable of(File file, Charset encoding) throws IOException {
124        if (PEFile.isPEFile(file)) {
125            return new PEFile(file);
126
127        } else if (MSIFile.isMSIFile(file)) {
128            return new MSIFile(file);
129
130        } else if (MSCabinetFile.isMSCabinetFile(file)) {
131            return new MSCabinetFile(file);
132
133        } else if (CatalogFile.isCatalogFile(file)) {
134            return new CatalogFile(file);
135
136        } else if (file.getName().endsWith(".ps1")
137                || file.getName().endsWith(".psd1")
138                || file.getName().endsWith(".psm1")) {
139            return new PowerShellScript(file, encoding);
140
141        } else if (file.getName().endsWith(".ps1xml")) {
142            return new PowerShellXMLScript(file, encoding);
143
144        } else if (file.getName().endsWith(".vbs")
145                || file.getName().endsWith(".vbe")) {
146            return new VBScript(file, encoding);
147
148        } else if (file.getName().endsWith(".js")
149                || file.getName().endsWith(".jse")) {
150            return new JScript(file, encoding);
151
152        } else if (file.getName().endsWith(".wsf")) {
153            return new WindowsScript(file, encoding);
154
155        } else {
156            throw new UnsupportedOperationException("Unsupported file: " + file);
157        }
158    }
159}