(DISTFILES): Comment out a few missing files.
[mono-project.git] / mcs / class / corlib / Mono.Security / StrongName.cs
blob1252319fc060edc7f18fac6e55e793aafedda7f3
1 //
2 // StrongName.cs - Strong Name Implementation
3 //
4 // Author:
5 // Sebastien Pouliot (sebastien@ximian.com)
6 //
7 // (C) 2003 Motus Technologies Inc. (http://www.motus.com)
8 // (C) 2004 Novell (http://www.novell.com)
9 //
12 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 //
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 //
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System;
35 using System.Configuration.Assemblies;
36 using System.Globalization;
37 using System.IO;
38 using System.Reflection;
39 using System.Security.Cryptography;
41 using Mono.Security.Cryptography;
43 namespace Mono.Security {
45 #if INSIDE_CORLIB
46 internal
47 #else
48 public
49 #endif
50 sealed class StrongName {
52 internal class StrongNameSignature {
53 private byte[] hash;
54 private byte[] signature;
55 private UInt32 signaturePosition;
56 private UInt32 signatureLength;
57 private UInt32 metadataPosition;
58 private UInt32 metadataLength;
59 private byte cliFlag;
60 private UInt32 cliFlagPosition;
62 public byte[] Hash {
63 get { return hash; }
64 set { hash = value; }
67 public byte[] Signature {
68 get { return signature; }
69 set { signature = value; }
72 public UInt32 MetadataPosition {
73 get { return metadataPosition; }
74 set { metadataPosition = value; }
77 public UInt32 MetadataLength {
78 get { return metadataLength; }
79 set { metadataLength = value; }
82 public UInt32 SignaturePosition {
83 get { return signaturePosition; }
84 set { signaturePosition = value; }
87 public UInt32 SignatureLength {
88 get { return signatureLength; }
89 set { signatureLength = value; }
92 // delay signed -> flag = 0x01
93 // strongsigned -> flag = 0x09
94 public byte CliFlag {
95 get { return cliFlag; }
96 set { cliFlag = value; }
99 public UInt32 CliFlagPosition {
100 get { return cliFlagPosition; }
101 set { cliFlagPosition = value; }
105 internal enum StrongNameOptions {
106 Metadata,
107 Signature
110 private RSA rsa;
111 private byte[] publicKey;
112 private byte[] keyToken;
113 private string tokenAlgorithm;
115 public StrongName ()
119 public StrongName (byte[] data)
121 if (data == null)
122 throw new ArgumentNullException ("data");
124 // check for ECMA key
125 if (data.Length == 16) {
126 int i = 0;
127 int sum = 0;
128 while (i < data.Length)
129 sum += data [i++];
130 if (sum == 4) {
131 // it is the ECMA key
132 publicKey = (byte[]) data.Clone ();
135 else {
136 RSA = CryptoConvert.FromCapiKeyBlob (data);
137 if (rsa == null)
138 throw new ArgumentException ("data isn't a correctly encoded RSA public key");
142 public StrongName (RSA rsa)
144 if (rsa == null)
145 throw new ArgumentNullException ("rsa");
147 RSA = rsa;
150 private void InvalidateCache ()
152 publicKey = null;
153 keyToken = null;
156 public bool CanSign {
157 get {
158 if (rsa == null)
159 return false;
160 #if INSIDE_CORLIB
161 // the easy way
162 if (RSA is RSACryptoServiceProvider) {
163 // available as internal for corlib
164 return !(rsa as RSACryptoServiceProvider).PublicOnly;
166 else
167 #endif
168 if (RSA is RSAManaged) {
169 return !(rsa as RSAManaged).PublicOnly;
171 else {
172 // the hard way
173 try {
174 RSAParameters p = rsa.ExportParameters (true);
175 return ((p.D != null) && (p.P != null) && (p.Q != null));
177 catch (CryptographicException) {
178 return false;
184 public RSA RSA {
185 get {
186 // if none then we create a new keypair
187 if (rsa == null)
188 rsa = (RSA) RSA.Create ();
189 return rsa;
191 set {
192 rsa = value;
193 InvalidateCache ();
197 public byte[] PublicKey {
198 get {
199 if (publicKey == null) {
200 byte[] keyPair = CryptoConvert.ToCapiKeyBlob (rsa, false);
201 publicKey = new byte [32 + 128]; // always 1024 bits
203 // The first 12 bytes are documented at:
204 // http://msdn.microsoft.com/library/en-us/cprefadd/html/grfungethashfromfile.asp
205 // ALG_ID - Signature
206 publicKey [0] = keyPair [4];
207 publicKey [1] = keyPair [5];
208 publicKey [2] = keyPair [6];
209 publicKey [3] = keyPair [7];
210 // ALG_ID - Hash (SHA1 == 0x8004)
211 publicKey [4] = 0x04;
212 publicKey [5] = 0x80;
213 publicKey [6] = 0x00;
214 publicKey [7] = 0x00;
215 // Length of Public Key (in bytes)
216 byte[] lastPart = BitConverterLE.GetBytes (publicKey.Length - 12);
217 publicKey [8] = lastPart [0];
218 publicKey [9] = lastPart [1];
219 publicKey [10] = lastPart [2];
220 publicKey [11] = lastPart [3];
221 // Ok from here - Same structure as keypair - expect for public key
222 publicKey [12] = 0x06; // PUBLICKEYBLOB
223 // we can copy this part
224 Buffer.BlockCopy (keyPair, 1, publicKey, 13, publicKey.Length - 13);
225 // and make a small adjustment
226 publicKey [23] = 0x31; // (RSA1 not RSA2)
228 return (byte[]) publicKey.Clone ();
232 public byte[] PublicKeyToken {
233 get {
234 if (keyToken == null) {
235 byte[] publicKey = PublicKey;
236 if (publicKey == null)
237 return null;
238 HashAlgorithm ha = SHA1.Create (TokenAlgorithm);
239 byte[] hash = ha.ComputeHash (publicKey);
240 // we need the last 8 bytes in reverse order
241 keyToken = new byte [8];
242 Buffer.BlockCopy (hash, (hash.Length - 8), keyToken, 0, 8);
243 Array.Reverse (keyToken, 0, 8);
245 return (byte[]) keyToken.Clone ();
249 public string TokenAlgorithm {
250 get {
251 if (tokenAlgorithm == null)
252 tokenAlgorithm = "SHA1";
253 return tokenAlgorithm;
255 set {
256 string algo = value.ToUpper (CultureInfo.InvariantCulture);
257 if ((algo == "SHA1") || (algo == "MD5")) {
258 tokenAlgorithm = value;
259 InvalidateCache ();
261 else
262 throw new ArgumentException ("Unsupported hash algorithm for token");
266 public byte[] GetBytes ()
268 return CryptoConvert.ToCapiPrivateKeyBlob (RSA);
271 private UInt32 RVAtoPosition (UInt32 r, int sections, byte[] headers)
273 for (int i=0; i < sections; i++) {
274 UInt32 p = BitConverterLE.ToUInt32 (headers, i * 40 + 20);
275 UInt32 s = BitConverterLE.ToUInt32 (headers, i * 40 + 12);
276 int l = (int) BitConverterLE.ToUInt32 (headers, i * 40 + 8);
277 if ((s <= r) && (r < s + l)) {
278 return p + r - s;
281 return 0;
284 internal StrongNameSignature StrongHash (Stream stream, StrongNameOptions options)
286 StrongNameSignature info = new StrongNameSignature ();
288 HashAlgorithm hash = HashAlgorithm.Create (TokenAlgorithm);
289 CryptoStream cs = new CryptoStream (Stream.Null, hash, CryptoStreamMode.Write);
291 // MS-DOS Header - always 128 bytes
292 // ref: Section 24.2.1, Partition II Metadata
293 byte[] mz = new byte [128];
294 stream.Read (mz, 0, 128);
295 if (BitConverterLE.ToUInt16 (mz, 0) != 0x5a4d)
296 return null;
297 UInt32 peHeader = BitConverterLE.ToUInt32 (mz, 60);
298 cs.Write (mz, 0, 128);
299 if (peHeader != 128) {
300 byte[] mzextra = new byte [peHeader - 128];
301 stream.Read (mzextra, 0, mzextra.Length);
302 cs.Write (mzextra, 0, mzextra.Length);
305 // PE File Header - always 248 bytes
306 // ref: Section 24.2.2, Partition II Metadata
307 byte[] pe = new byte [248];
308 stream.Read (pe, 0, 248);
309 if (BitConverterLE.ToUInt32 (pe, 0) != 0x4550)
310 return null;
311 if (BitConverterLE.ToUInt16 (pe, 4) != 0x14c)
312 return null;
313 // MUST zeroize both CheckSum and Security Directory
314 byte[] v = new byte [8];
315 Buffer.BlockCopy (v, 0, pe, 88, 4);
316 Buffer.BlockCopy (v, 0, pe, 152, 8);
317 cs.Write (pe, 0, 248);
319 UInt16 numSection = BitConverterLE.ToUInt16 (pe, 6);
320 int sectionLength = (numSection * 40);
321 byte[] sectionHeaders = new byte [sectionLength];
322 stream.Read (sectionHeaders, 0, sectionLength);
323 cs.Write (sectionHeaders, 0, sectionLength);
325 UInt32 cliHeaderRVA = BitConverterLE.ToUInt32 (pe, 232);
326 UInt32 cliHeaderPos = RVAtoPosition (cliHeaderRVA, numSection, sectionHeaders);
327 int cliHeaderSiz = (int) BitConverterLE.ToUInt32 (pe, 236);
329 // CLI Header
330 // ref: Section 24.3.3, Partition II Metadata
331 byte[] cli = new byte [cliHeaderSiz];
332 stream.Position = cliHeaderPos;
333 stream.Read (cli, 0, cliHeaderSiz);
335 UInt32 strongNameSignatureRVA = BitConverterLE.ToUInt32 (cli, 32);
336 info.SignaturePosition = RVAtoPosition (strongNameSignatureRVA, numSection, sectionHeaders);
337 info.SignatureLength = BitConverterLE.ToUInt32 (cli, 36);
339 UInt32 metadataRVA = BitConverterLE.ToUInt32 (cli, 8);
340 info.MetadataPosition = RVAtoPosition (metadataRVA, numSection, sectionHeaders);
341 info.MetadataLength = BitConverterLE.ToUInt32 (cli, 12);
343 if (options == StrongNameOptions.Metadata) {
344 cs.Close ();
345 hash.Initialize ();
346 byte[] metadata = new byte [info.MetadataLength];
347 stream.Position = info.MetadataPosition;
348 stream.Read (metadata, 0, metadata.Length);
349 info.Hash = hash.ComputeHash (metadata);
350 return info;
353 // now we hash every section EXCEPT the signature block
354 for (int i=0; i < numSection; i++) {
355 UInt32 start = BitConverterLE.ToUInt32 (sectionHeaders, i * 40 + 20);
356 int length = (int) BitConverterLE.ToUInt32 (sectionHeaders, i * 40 + 16);
357 byte[] section = new byte [length];
358 stream.Position = start;
359 stream.Read (section, 0, length);
360 if ((start <= info.SignaturePosition) && (info.SignaturePosition < start + length)) {
361 // hash before the signature
362 int before = (int)(info.SignaturePosition - start);
363 if (before > 0) {
364 cs.Write (section, 0, before);
366 // copy signature
367 info.Signature = new byte [info.SignatureLength];
368 Buffer.BlockCopy (section, before, info.Signature, 0, (int)info.SignatureLength);
369 Array.Reverse (info.Signature);
370 // hash after the signature
371 int s = (int)(before + info.SignatureLength);
372 int after = (int)(length - s);
373 if (after > 0) {
374 cs.Write (section, s, after);
377 else
378 cs.Write (section, 0, length);
381 cs.Close ();
382 info.Hash = hash.Hash;
383 return info;
386 // return the same result as the undocumented and unmanaged GetHashFromAssemblyFile
387 public byte[] Hash (string fileName)
389 FileStream fs = File.OpenRead (fileName);
390 StrongNameSignature sn = StrongHash (fs, StrongNameOptions.Metadata);
391 fs.Close ();
393 return sn.Hash;
396 public bool Sign (string fileName)
398 bool result = false;
399 StrongNameSignature sn;
400 using (FileStream fs = File.OpenRead (fileName)) {
401 sn = StrongHash (fs, StrongNameOptions.Signature);
402 fs.Close ();
404 if (sn.Hash == null)
405 return false;
407 byte[] signature = null;
408 try {
409 RSAPKCS1SignatureFormatter sign = new RSAPKCS1SignatureFormatter (rsa);
410 sign.SetHashAlgorithm (TokenAlgorithm);
411 signature = sign.CreateSignature (sn.Hash);
412 Array.Reverse (signature);
414 catch (CryptographicException) {
415 return false;
418 using (FileStream fs = File.OpenWrite (fileName)) {
419 fs.Position = sn.SignaturePosition;
420 fs.Write (signature, 0, signature.Length);
421 fs.Close ();
422 result = true;
424 return result;
427 public bool Verify (string fileName)
429 StrongNameSignature sn;
430 using (FileStream fs = File.OpenRead (fileName)) {
431 sn = StrongHash (fs, StrongNameOptions.Signature);
432 fs.Close ();
434 if (sn.Hash == null) {
435 return false;
438 try {
439 AssemblyHashAlgorithm algorithm = AssemblyHashAlgorithm.SHA1;
440 if (tokenAlgorithm == "MD5")
441 algorithm = AssemblyHashAlgorithm.MD5;
442 return Verify (rsa, algorithm, sn.Hash, sn.Signature);
444 catch (CryptographicException) {
445 // no exception allowed
446 return false;
450 #if INSIDE_CORLIB
451 static object lockObject = new object ();
452 static bool initialized = false;
454 // We don't want a dependency on StrongNameManager in Mono.Security.dll
455 static public bool IsAssemblyStrongnamed (string assemblyName)
457 if (!initialized) {
458 lock (lockObject) {
459 if (!initialized) {
460 string config = Environment.GetMachineConfigPath ();
461 StrongNameManager.LoadConfig (config);
462 initialized = true;
467 try {
468 // this doesn't load the assembly (well it unloads it ;)
469 // http://weblogs.asp.net/nunitaddin/posts/9991.aspx
470 AssemblyName an = AssemblyName.GetAssemblyName (assemblyName);
471 if (an == null)
472 return false;
474 byte[] publicKey = StrongNameManager.GetMappedPublicKey (an.GetPublicKeyToken ());
475 if ((publicKey == null) || (publicKey.Length < 12)) {
476 // no mapping
477 publicKey = an.GetPublicKey ();
478 if ((publicKey == null) || (publicKey.Length < 12))
479 return false;
482 // Note: MustVerify is based on the original token (by design). Public key
483 // remapping won't affect if the assembly is verified or not.
484 if (!StrongNameManager.MustVerify (an)) {
485 return true;
488 RSA rsa = CryptoConvert.FromCapiPublicKeyBlob (publicKey, 12);
489 StrongName sn = new StrongName (rsa);
490 bool result = sn.Verify (assemblyName);
491 return result;
493 catch {
494 // no exception allowed
495 return false;
499 // TODO
500 // we would get better performance if the runtime hashed the
501 // assembly - as we wouldn't have to load it from disk a
502 // second time. The runtime already have implementations of
503 // SHA1 (and even MD5 if required someday).
504 static public bool VerifySignature (byte[] publicKey, int algorithm, byte[] hash, byte[] signature)
506 try {
507 RSA rsa = CryptoConvert.FromCapiPublicKeyBlob (publicKey);
508 return Verify (rsa, (AssemblyHashAlgorithm) algorithm, hash, signature);
510 catch {
511 // no exception allowed
512 return false;
515 #endif
516 static private bool Verify (RSA rsa, AssemblyHashAlgorithm algorithm, byte[] hash, byte[] signature)
518 RSAPKCS1SignatureDeformatter vrfy = new RSAPKCS1SignatureDeformatter (rsa);
519 switch (algorithm) {
520 case AssemblyHashAlgorithm.MD5:
521 vrfy.SetHashAlgorithm ("MD5");
522 break;
523 case AssemblyHashAlgorithm.SHA1:
524 case AssemblyHashAlgorithm.None:
525 default:
526 vrfy.SetHashAlgorithm ("SHA1");
527 break;
529 return vrfy.VerifySignature (hash, signature);