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}