2
Copyright (C) 2012 Jeroen Frijters
4
This software is provided 'as-is', without any express or implied
5
warranty. In no event will the authors be held liable for any damages
6
arising from the use of this software.
8
Permission is granted to anyone to use this software for any purpose,
9
including commercial applications, and to alter it and redistribute it
10
freely, subject to the following restrictions:
12
1. The origin of this software must not be misrepresented; you must not
13
claim that you wrote the original software. If you use this software
14
in a product, an acknowledgment in the product documentation would be
15
appreciated but is not required.
16
2. Altered source versions must be plainly marked as such, and must not be
17
misrepresented as being the original software.
18
3. This notice may not be removed or altered from any source distribution.
27
using System.Security.Cryptography;
28
using System.Security.Cryptography.Pkcs;
29
using System.Security.Cryptography.X509Certificates;
31
namespace IKVM.Reflection.Reader
33
// This code is based on trial-and-error and some inspiration from the Mono.Security library.
34
// It almost certainly has bugs and/or design flaws.
35
static class Authenticode
37
private const ushort IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b;
38
private const ushort IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b;
39
private const int WIN_CERT_REVISION_2_0 = 0x0200;
40
private const int WIN_CERT_TYPE_PKCS_SIGNED_DATA = 0x0002;
42
internal static X509Certificate GetSignerCertificate(Stream stream)
44
stream.Seek(60, SeekOrigin.Begin);
45
BinaryReader br = new BinaryReader(stream);
46
int peSignatureOffset = br.ReadInt32();
47
int checksumOffset = peSignatureOffset + 24 + 64;
48
// seek to the IMAGE_OPTIONAL_HEADER
49
stream.Seek(peSignatureOffset + 24, SeekOrigin.Begin);
50
int certificateTableDataDirectoryOffset;
51
switch (br.ReadUInt16())
53
case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
54
certificateTableDataDirectoryOffset = peSignatureOffset + 24 + (64 + 4 * 8) + 8 * 4;
56
case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
57
certificateTableDataDirectoryOffset = peSignatureOffset + 24 + (64 + 4 * 8 + 16) + 8 * 4;
60
throw new BadImageFormatException();
62
stream.Seek(certificateTableDataDirectoryOffset, SeekOrigin.Begin);
63
int certificateTableOffset = br.ReadInt32();
64
int certificateTableLength = br.ReadInt32();
66
stream.Seek(certificateTableOffset, SeekOrigin.Begin);
67
int dwLength = br.ReadInt32();
68
short wRevision = br.ReadInt16();
69
short wCertificateType = br.ReadInt16();
70
if (wRevision != WIN_CERT_REVISION_2_0)
74
if (wCertificateType != WIN_CERT_TYPE_PKCS_SIGNED_DATA)
78
byte[] buf = new byte[certificateTableLength - 8];
79
stream.Read(buf, 0, buf.Length);
81
SignedCms cms = new SignedCms();
85
cms.CheckSignature(false);
87
catch (CryptographicException)
91
SignerInfo signerInfo = cms.SignerInfos[0];
93
int[] offsets = new int[] { checksumOffset, certificateTableDataDirectoryOffset, certificateTableOffset };
94
int[] lengths = new int[] { 4, 8, certificateTableLength };
95
byte[] actualHash = ComputeHashWithSkip(stream, signerInfo.DigestAlgorithm.FriendlyName, offsets, lengths);
96
byte[] requiredHash = DecodeASN1(cms.ContentInfo.Content, 0, 1, 1);
98
if (requiredHash == null || actualHash.Length != requiredHash.Length)
103
for (int i = 0; i < actualHash.Length; i++)
105
if (actualHash[i] != requiredHash[i])
111
return signerInfo.Certificate;
114
private static byte[] ComputeHashWithSkip(Stream stream, string hashAlgorithm, int[] skipOffsets, int[] skipLengths)
116
using (HashAlgorithm hash = HashAlgorithm.Create(hashAlgorithm))
118
using (CryptoStream cs = new CryptoStream(Stream.Null, hash, CryptoStreamMode.Write))
120
stream.Seek(0, SeekOrigin.Begin);
121
byte[] buf = new byte[8192];
122
HashChunk(stream, cs, buf, skipOffsets[0]);
123
stream.Seek(skipLengths[0], SeekOrigin.Current);
124
for (int i = 1; i < skipOffsets.Length; i++)
126
HashChunk(stream, cs, buf, skipOffsets[i] - (skipOffsets[i - 1] + skipLengths[i - 1]));
127
stream.Seek(skipLengths[i], SeekOrigin.Current);
129
HashChunk(stream, cs, buf, (int)stream.Length - (skipOffsets[skipOffsets.Length - 1] + skipLengths[skipLengths.Length - 1]));
135
private static void HashChunk(Stream stream, CryptoStream cs, byte[] buf, int length)
139
int read = stream.Read(buf, 0, Math.Min(buf.Length, length));
140
cs.Write(buf, 0, read);
145
private static byte[] DecodeASN1(byte[] buf, params int[] indexes)
147
return DecodeASN1(buf, 0, buf.Length, 0, indexes);
150
private static byte[] DecodeASN1(byte[] buf, int pos, int end, int depth, int[] indexes)
152
for (int index = 0; pos < end; index++)
154
int tag = buf[pos++];
155
int length = buf[pos++];
158
int lenlen = length & 0x7F;
160
for (int i = 0; i < lenlen; i++)
162
length = length * 256 + buf[pos++];
165
if (indexes[depth] == index)
167
if (depth == indexes.Length - 1)
169
byte[] data = new byte[length];
170
Buffer.BlockCopy(buf, pos, data, 0, length);
173
if ((tag & 0x20) == 0)
177
return DecodeASN1(buf, pos, pos + length, depth + 1, indexes);
185
#endif // !NO_AUTHENTICODE