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) { 044 size = (int) peFile.readDWord(index, 0); 045 revision = peFile.readWord(index, 4); 046 type = peFile.readWord(index, 6); 047 content = new byte[size - 8]; 048 peFile.read(content, index, 8); 049 } 050 051 public CertificateTableEntry(CMSSignedData signature) throws IOException { 052 setSignature(signature); 053 } 054 055 public int getSize() { 056 return size; 057 } 058 059 public CMSSignedData getSignature() throws CMSException { 060 if (type != CertificateType.PKCS_SIGNED_DATA.getValue()) { 061 throw new UnsupportedOperationException("Unsupported certificate type: " + type); 062 } 063 064 if (revision != 0x0200) { 065 throw new UnsupportedOperationException("Unsupported certificate revision: " + revision); 066 } 067 068 if (signature == null) { 069 try { 070 signature = new CMSSignedData((CMSProcessable) null, ContentInfo.getInstance(new ASN1InputStream(content).readObject())); 071 } catch (IOException e) { 072 throw new IllegalArgumentException("Failed to construct ContentInfo from byte[]: ", e); 073 } 074 } 075 076 return signature; 077 } 078 079 public void setSignature(CMSSignedData signature) throws IOException { 080 this.signature = signature; 081 byte[] content = signature.toASN1Structure().getEncoded("DER"); 082 this.content = pad(content, 8); 083 this.size = this.content.length + 8; 084 this.type = CertificateType.PKCS_SIGNED_DATA.getValue(); 085 } 086 087 private byte[] pad(byte[] data, int multiple) { 088 if (data.length % multiple == 0) { 089 return data; 090 } else { 091 byte[] copy = new byte[data.length + (multiple - data.length % multiple)]; 092 System.arraycopy(data, 0, copy, 0, data.length); 093 return copy; 094 } 095 } 096 097 public byte[] toBytes() { 098 ByteBuffer buffer = ByteBuffer.allocate(size); 099 buffer.order(ByteOrder.LITTLE_ENDIAN); 100 buffer.putInt(buffer.limit()); 101 buffer.putShort((short) 0x0200); 102 buffer.putShort(CertificateType.PKCS_SIGNED_DATA.getValue()); 103 buffer.put(content); 104 105 return buffer.array(); 106 } 107}