001/** 002 * Copyright 2016 Emmanuel Bourg 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.pe; 018 019import java.io.IOException; 020import java.nio.ByteBuffer; 021import java.nio.ByteOrder; 022 023import org.bouncycastle.asn1.ASN1InputStream; 024import org.bouncycastle.asn1.cms.ContentInfo; 025import org.bouncycastle.cms.CMSException; 026import org.bouncycastle.cms.CMSProcessable; 027import org.bouncycastle.cms.CMSSignedData; 028 029/** 030 * Entry of the certificate table. 031 * 032 * @author Emmanuel Bourg 033 * @since 1.3 034 */ 035public class CertificateTableEntry { 036 037 private int size; 038 private int revision; 039 private int type; 040 private byte[] content; 041 private CMSSignedData signature; 042 043 CertificateTableEntry(PEFile peFile, long index) throws IOException { 044 size = (int) peFile.readDWord(index, 0); 045 if (size < 8 || size > peFile.channel.size() - index) { 046 throw new IOException("Invalid certificate table size: " + size); 047 } 048 revision = peFile.readWord(index, 4); 049 type = peFile.readWord(index, 6); 050 content = new byte[size - 8]; 051 peFile.read(content, index, 8); 052 } 053 054 public CertificateTableEntry(CMSSignedData signature) throws IOException { 055 setSignature(signature); 056 } 057 058 public int getSize() { 059 return size; 060 } 061 062 public CMSSignedData getSignature() throws CMSException { 063 if (type != CertificateType.PKCS_SIGNED_DATA.getValue()) { 064 throw new UnsupportedOperationException("Unsupported certificate type: " + type); 065 } 066 067 if (revision != 0x0200) { 068 throw new UnsupportedOperationException("Unsupported certificate revision: " + revision); 069 } 070 071 if (signature == null) { 072 try { 073 signature = new CMSSignedData((CMSProcessable) null, ContentInfo.getInstance(new ASN1InputStream(content).readObject())); 074 } catch (IOException e) { 075 throw new IllegalArgumentException("Failed to construct ContentInfo from byte[]: ", e); 076 } 077 } 078 079 return signature; 080 } 081 082 public void setSignature(CMSSignedData signature) throws IOException { 083 this.signature = signature; 084 byte[] content = signature.toASN1Structure().getEncoded("DER"); 085 this.content = pad(content, 8); 086 this.size = this.content.length + 8; 087 this.type = CertificateType.PKCS_SIGNED_DATA.getValue(); 088 } 089 090 private byte[] pad(byte[] data, int multiple) { 091 if (data.length % multiple == 0) { 092 return data; 093 } else { 094 byte[] copy = new byte[data.length + (multiple - data.length % multiple)]; 095 System.arraycopy(data, 0, copy, 0, data.length); 096 return copy; 097 } 098 } 099 100 public byte[] toBytes() { 101 ByteBuffer buffer = ByteBuffer.allocate(size); 102 buffer.order(ByteOrder.LITTLE_ENDIAN); 103 buffer.putInt(buffer.limit()); 104 buffer.putShort((short) 0x0200); 105 buffer.putShort(CertificateType.PKCS_SIGNED_DATA.getValue()); 106 buffer.put(content); 107 108 return buffer.array(); 109 } 110}