2 // StrongName.cs - Strong Name Implementation
5 // Sebastien Pouliot (spouliot@motus.com)
7 // (C) 2003 Motus Technologies Inc. (http://www.motus.com)
12 using System.Security.Cryptography;
14 using Mono.Security.Cryptography;
16 namespace Mono.Security {
25 internal class StrongNameSignature {
27 private byte[] signature;
28 private UInt32 signaturePosition;
29 private UInt32 signatureLength;
30 private UInt32 metadataPosition;
31 private UInt32 metadataLength;
33 private UInt32 cliFlagPosition;
40 public byte[] Signature {
41 get { return signature; }
42 set { signature = value; }
45 public UInt32 MetadataPosition {
46 get { return metadataPosition; }
47 set { metadataPosition = value; }
50 public UInt32 MetadataLength {
51 get { return metadataLength; }
52 set { metadataLength = value; }
55 public UInt32 SignaturePosition {
56 get { return signaturePosition; }
57 set { signaturePosition = value; }
60 public UInt32 SignatureLength {
61 get { return signatureLength; }
62 set { signatureLength = value; }
65 // delay signed -> flag = 0x01
66 // strongsigned -> flag = 0x09
68 get { return cliFlag; }
69 set { cliFlag = value; }
72 public UInt32 CliFlagPosition {
73 get { return cliFlagPosition; }
74 set { cliFlagPosition = value; }
78 internal enum StrongNameOptions {
84 private byte[] publicKey;
85 private byte[] keyToken;
86 private string tokenAlgorithm;
92 public StrongName (byte[] data)
95 throw new ArgumentNullException ("data");
97 RSA = CryptoConvert.FromCapiKeyBlob (data);
99 throw new ArgumentException ("data isn't a correctly encoded RSA public key");
102 public StrongName (RSA rsa) : base ()
105 throw new ArgumentNullException ("rsa");
110 private void InvalidateCache ()
118 // if none then we create a new keypair
120 rsa = (RSA) RSA.Create ();
129 public byte[] PublicKey {
131 if (publicKey == null) {
132 byte[] keyPair = CryptoConvert.ToCapiKeyBlob (rsa, false);
133 publicKey = new byte [32 + 128]; // always 1024 bits
135 // The first 12 bytes are documented at:
136 // http://msdn.microsoft.com/library/en-us/cprefadd/html/grfungethashfromfile.asp
137 // ALG_ID - Signature
138 publicKey [0] = keyPair [4];
139 publicKey [1] = keyPair [5];
140 publicKey [2] = keyPair [6];
141 publicKey [3] = keyPair [7];
142 // ALG_ID - Hash (SHA1 == 0x8004)
143 publicKey [4] = 0x04;
144 publicKey [5] = 0x80;
145 publicKey [6] = 0x00;
146 publicKey [7] = 0x00;
147 // Length of Public Key (in bytes)
148 byte[] lastPart = BitConverter.GetBytes (publicKey.Length - 12);
149 publicKey [8] = lastPart [0];
150 publicKey [9] = lastPart [1];
151 publicKey [10] = lastPart [2];
152 publicKey [11] = lastPart [3];
153 // Ok from here - Same structure as keypair - expect for public key
154 publicKey [12] = 0x06; // PUBLICKEYBLOB
155 // we can copy this part
156 Array.Copy (keyPair, 1, publicKey, 13, publicKey.Length - 13);
157 // and make a small adjustment
158 publicKey [23] = 0x31; // (RSA1 not RSA2)
164 public byte[] PublicKeyToken {
166 if (keyToken != null)
168 byte[] publicKey = PublicKey;
169 if (publicKey == null)
171 HashAlgorithm ha = SHA1.Create (TokenAlgorithm);
172 byte[] hash = ha.ComputeHash (publicKey);
173 // we need the last 8 bytes in reverse order
174 keyToken = new byte [8];
175 Array.Copy (hash, (hash.Length - 8), keyToken, 0, 8);
176 Array.Reverse (keyToken, 0, 8);
181 public string TokenAlgorithm {
183 if (tokenAlgorithm == null)
184 tokenAlgorithm = "SHA1";
185 return tokenAlgorithm;
188 string algo = value.ToUpper ();
189 if ((algo == "SHA1") || (algo == "MD5")) {
190 tokenAlgorithm = value;
194 throw new ArgumentException ("Unsupported hash algorithm for token");
198 public byte[] GetBytes ()
200 return CryptoConvert.ToCapiPrivateKeyBlob (rsa);
203 private UInt32 RVAtoPosition (UInt32 r, int sections, byte[] headers)
205 for (int i=0; i < sections; i++) {
206 UInt32 p = BitConverter.ToUInt32 (headers, i * 40 + 20);
207 UInt32 s = BitConverter.ToUInt32 (headers, i * 40 + 12);
208 int l = (int) BitConverter.ToUInt32 (headers, i * 40 + 8);
209 if ((s <= r) && (r < s + l)) {
216 internal StrongNameSignature StrongHash (Stream stream, StrongNameOptions options)
218 StrongNameSignature info = new StrongNameSignature ();
220 HashAlgorithm hash = HashAlgorithm.Create (TokenAlgorithm);
221 CryptoStream cs = new CryptoStream (Stream.Null, hash, CryptoStreamMode.Write);
223 // MS-DOS Header - always 128 bytes
224 // ref: Section 24.2.1, Partition II Metadata
225 byte[] mz = new byte [128];
226 stream.Read (mz, 0, 128);
227 if (BitConverter.ToUInt16 (mz, 0) != 0x5a4d)
229 UInt32 peHeader = BitConverter.ToUInt32 (mz, 60);
230 cs.Write (mz, 0, 128);
231 if (peHeader != 128) {
232 byte[] mzextra = new byte [peHeader - 128];
233 stream.Read (mzextra, 0, mzextra.Length);
234 cs.Write (mzextra, 0, mzextra.Length);
237 // PE File Header - always 248 bytes
238 // ref: Section 24.2.2, Partition II Metadata
239 byte[] pe = new byte [248];
240 stream.Read (pe, 0, 248);
241 if (BitConverter.ToUInt32 (pe, 0) != 0x4550)
243 if (BitConverter.ToUInt16 (pe, 4) != 0x14c)
245 // MUST zeroize both CheckSum and Security Directory
246 byte[] v = new byte [8];
247 Buffer.BlockCopy (v, 0, pe, 88, 4);
248 Buffer.BlockCopy (v, 0, pe, 152, 8);
249 cs.Write (pe, 0, 248);
251 UInt16 numSection = BitConverter.ToUInt16 (pe, 6);
252 int sectionLength = (numSection * 40);
253 byte[] sectionHeaders = new byte [sectionLength];
254 stream.Read (sectionHeaders, 0, sectionLength);
255 cs.Write (sectionHeaders, 0, sectionLength);
257 UInt32 cliHeaderRVA = BitConverter.ToUInt32 (pe, 232);
258 UInt32 cliHeaderPos = RVAtoPosition (cliHeaderRVA, numSection, sectionHeaders);
259 int cliHeaderSiz = (int) BitConverter.ToUInt32 (pe, 236);
262 // ref: Section 24.3.3, Partition II Metadata
263 byte[] cli = new byte [cliHeaderSiz];
264 stream.Position = cliHeaderPos;
265 stream.Read (cli, 0, cliHeaderSiz);
267 UInt32 strongNameSignatureRVA = BitConverter.ToUInt32 (cli, 32);
268 info.SignaturePosition = RVAtoPosition (strongNameSignatureRVA, numSection, sectionHeaders);
269 info.SignatureLength = BitConverter.ToUInt32 (cli, 36);
271 UInt32 metadataRVA = BitConverter.ToUInt32 (cli, 8);
272 info.MetadataPosition = RVAtoPosition (metadataRVA, numSection, sectionHeaders);
273 info.MetadataLength = BitConverter.ToUInt32 (cli, 12);
275 if (options == StrongNameOptions.Metadata) {
278 byte[] metadata = new byte [info.MetadataLength];
279 stream.Position = info.MetadataPosition;
280 stream.Read (metadata, 0, metadata.Length);
281 info.Hash = hash.ComputeHash (metadata);
285 // now we hash every section EXCEPT the signature block
286 for (int i=0; i < numSection; i++) {
287 UInt32 start = BitConverter.ToUInt32 (sectionHeaders, i * 40 + 20);
288 int length = (int) BitConverter.ToUInt32 (sectionHeaders, i * 40 + 16);
289 byte[] section = new byte [length];
290 stream.Position = start;
291 stream.Read (section, 0, length);
292 if ((start <= info.SignaturePosition) && (info.SignaturePosition < start + length)) {
293 // hash before the signature
294 int before = (int)(info.SignaturePosition - start);
296 cs.Write (section, 0, before);
299 info.Signature = new byte [info.SignatureLength];
300 Buffer.BlockCopy (section, before, info.Signature, 0, (int)info.SignatureLength);
301 Array.Reverse (info.Signature);
302 // hash after the signature
303 int s = (int)(before + info.SignatureLength);
304 int after = (int)(length - s);
306 cs.Write (section, s, after);
310 cs.Write (section, 0, length);
314 info.Hash = hash.Hash;
318 // return the same result as the undocumented and unmanaged GetHashFromAssemblyFile
319 public byte[] Hash (string fileName)
321 FileStream fs = File.OpenRead (fileName);
322 StrongNameSignature sn = StrongHash (fs, StrongNameOptions.Metadata);
328 public bool Sign (string fileName)
331 StrongNameSignature sn;
332 FileStream fs = File.OpenRead (fileName);
334 sn = StrongHash (fs, StrongNameOptions.Signature);
342 byte[] signature = null;
344 RSAPKCS1SignatureFormatter sign = new RSAPKCS1SignatureFormatter (rsa);
345 sign.SetHashAlgorithm (TokenAlgorithm);
346 signature = sign.CreateSignature (sn.Hash);
347 Array.Reverse (signature);
354 fs = File.OpenWrite (fileName);
355 fs.Position = sn.SignaturePosition;
356 fs.Write (signature, 0, signature.Length);
367 public bool Verify (string fileName)
370 StrongNameSignature sn;
371 FileStream fs = File.OpenRead (fileName);
373 sn = StrongHash (fs, StrongNameOptions.Signature);
382 RSAPKCS1SignatureDeformatter vrfy = new RSAPKCS1SignatureDeformatter (rsa);
383 vrfy.SetHashAlgorithm (TokenAlgorithm);
384 result = vrfy.VerifySignature (sn.Hash, sn.Signature);