//
using System;
+using System.Configuration.Assemblies;
+using System.Globalization;
using System.IO;
+using System.Reflection;
using System.Security.Cryptography;
using Mono.Security.Cryptography;
if (data == null)
throw new ArgumentNullException ("data");
- RSA = CryptoConvert.FromCapiKeyBlob (data);
- if (rsa == null)
- throw new ArgumentException ("data isn't a correctly encoded RSA public key");
+ // check for ECMA key
+ if (data.Length == 16) {
+ int i = 0;
+ int sum = 0;
+ while (i < data.Length)
+ sum += data [i++];
+ if (sum == 4) {
+ // it is the ECMA key
+ publicKey = (byte[]) data.Clone ();
+ }
+ }
+ else {
+ RSA = CryptoConvert.FromCapiKeyBlob (data);
+ if (rsa == null)
+ throw new ArgumentException ("data isn't a correctly encoded RSA public key");
+ }
}
public StrongName (RSA rsa) : base ()
get {
if (rsa == null)
return false;
-#if INSIDE_CORLIB || NET_1_2
+#if INSIDE_CORLIB
// the easy way
if (RSA is RSACryptoServiceProvider) {
// available as internal for corlib
RSAParameters p = rsa.ExportParameters (true);
return ((p.D != null) && (p.P != null) && (p.Q != null));
}
- catch {
+ catch (CryptographicException) {
return false;
}
}
publicKey [6] = 0x00;
publicKey [7] = 0x00;
// Length of Public Key (in bytes)
- byte[] lastPart = BitConverter.GetBytes (publicKey.Length - 12);
+ byte[] lastPart = BitConverterLE.GetBytes (publicKey.Length - 12);
publicKey [8] = lastPart [0];
publicKey [9] = lastPart [1];
publicKey [10] = lastPart [2];
// Ok from here - Same structure as keypair - expect for public key
publicKey [12] = 0x06; // PUBLICKEYBLOB
// we can copy this part
- Array.Copy (keyPair, 1, publicKey, 13, publicKey.Length - 13);
+ Buffer.BlockCopy (keyPair, 1, publicKey, 13, publicKey.Length - 13);
// and make a small adjustment
publicKey [23] = 0x31; // (RSA1 not RSA2)
}
- return publicKey;
+ return (byte[]) publicKey.Clone ();
}
}
public byte[] PublicKeyToken {
get {
- if (keyToken != null)
- return keyToken;
- byte[] publicKey = PublicKey;
- if (publicKey == null)
- return null;
- HashAlgorithm ha = SHA1.Create (TokenAlgorithm);
- byte[] hash = ha.ComputeHash (publicKey);
- // we need the last 8 bytes in reverse order
- keyToken = new byte [8];
- Array.Copy (hash, (hash.Length - 8), keyToken, 0, 8);
- Array.Reverse (keyToken, 0, 8);
- return keyToken;
+ if (keyToken == null) {
+ byte[] publicKey = PublicKey;
+ if (publicKey == null)
+ return null;
+ HashAlgorithm ha = SHA1.Create (TokenAlgorithm);
+ byte[] hash = ha.ComputeHash (publicKey);
+ // we need the last 8 bytes in reverse order
+ keyToken = new byte [8];
+ Buffer.BlockCopy (hash, (hash.Length - 8), keyToken, 0, 8);
+ Array.Reverse (keyToken, 0, 8);
+ }
+ return (byte[]) keyToken.Clone ();
}
}
return tokenAlgorithm;
}
set {
- string algo = value.ToUpper ();
+ string algo = value.ToUpper (CultureInfo.InvariantCulture);
if ((algo == "SHA1") || (algo == "MD5")) {
tokenAlgorithm = value;
InvalidateCache ();
private UInt32 RVAtoPosition (UInt32 r, int sections, byte[] headers)
{
for (int i=0; i < sections; i++) {
- UInt32 p = BitConverter.ToUInt32 (headers, i * 40 + 20);
- UInt32 s = BitConverter.ToUInt32 (headers, i * 40 + 12);
- int l = (int) BitConverter.ToUInt32 (headers, i * 40 + 8);
+ UInt32 p = BitConverterLE.ToUInt32 (headers, i * 40 + 20);
+ UInt32 s = BitConverterLE.ToUInt32 (headers, i * 40 + 12);
+ int l = (int) BitConverterLE.ToUInt32 (headers, i * 40 + 8);
if ((s <= r) && (r < s + l)) {
return p + r - s;
}
// ref: Section 24.2.1, Partition II Metadata
byte[] mz = new byte [128];
stream.Read (mz, 0, 128);
- if (BitConverter.ToUInt16 (mz, 0) != 0x5a4d)
+ if (BitConverterLE.ToUInt16 (mz, 0) != 0x5a4d)
return null;
- UInt32 peHeader = BitConverter.ToUInt32 (mz, 60);
+ UInt32 peHeader = BitConverterLE.ToUInt32 (mz, 60);
cs.Write (mz, 0, 128);
if (peHeader != 128) {
byte[] mzextra = new byte [peHeader - 128];
// ref: Section 24.2.2, Partition II Metadata
byte[] pe = new byte [248];
stream.Read (pe, 0, 248);
- if (BitConverter.ToUInt32 (pe, 0) != 0x4550)
+ if (BitConverterLE.ToUInt32 (pe, 0) != 0x4550)
return null;
- if (BitConverter.ToUInt16 (pe, 4) != 0x14c)
+ if (BitConverterLE.ToUInt16 (pe, 4) != 0x14c)
return null;
// MUST zeroize both CheckSum and Security Directory
byte[] v = new byte [8];
Buffer.BlockCopy (v, 0, pe, 152, 8);
cs.Write (pe, 0, 248);
- UInt16 numSection = BitConverter.ToUInt16 (pe, 6);
+ UInt16 numSection = BitConverterLE.ToUInt16 (pe, 6);
int sectionLength = (numSection * 40);
byte[] sectionHeaders = new byte [sectionLength];
stream.Read (sectionHeaders, 0, sectionLength);
cs.Write (sectionHeaders, 0, sectionLength);
- UInt32 cliHeaderRVA = BitConverter.ToUInt32 (pe, 232);
+ UInt32 cliHeaderRVA = BitConverterLE.ToUInt32 (pe, 232);
UInt32 cliHeaderPos = RVAtoPosition (cliHeaderRVA, numSection, sectionHeaders);
- int cliHeaderSiz = (int) BitConverter.ToUInt32 (pe, 236);
+ int cliHeaderSiz = (int) BitConverterLE.ToUInt32 (pe, 236);
// CLI Header
// ref: Section 24.3.3, Partition II Metadata
stream.Position = cliHeaderPos;
stream.Read (cli, 0, cliHeaderSiz);
- UInt32 strongNameSignatureRVA = BitConverter.ToUInt32 (cli, 32);
+ UInt32 strongNameSignatureRVA = BitConverterLE.ToUInt32 (cli, 32);
info.SignaturePosition = RVAtoPosition (strongNameSignatureRVA, numSection, sectionHeaders);
- info.SignatureLength = BitConverter.ToUInt32 (cli, 36);
+ info.SignatureLength = BitConverterLE.ToUInt32 (cli, 36);
- UInt32 metadataRVA = BitConverter.ToUInt32 (cli, 8);
+ UInt32 metadataRVA = BitConverterLE.ToUInt32 (cli, 8);
info.MetadataPosition = RVAtoPosition (metadataRVA, numSection, sectionHeaders);
- info.MetadataLength = BitConverter.ToUInt32 (cli, 12);
+ info.MetadataLength = BitConverterLE.ToUInt32 (cli, 12);
if (options == StrongNameOptions.Metadata) {
cs.Close ();
// now we hash every section EXCEPT the signature block
for (int i=0; i < numSection; i++) {
- UInt32 start = BitConverter.ToUInt32 (sectionHeaders, i * 40 + 20);
- int length = (int) BitConverter.ToUInt32 (sectionHeaders, i * 40 + 16);
+ UInt32 start = BitConverterLE.ToUInt32 (sectionHeaders, i * 40 + 20);
+ int length = (int) BitConverterLE.ToUInt32 (sectionHeaders, i * 40 + 16);
byte[] section = new byte [length];
stream.Position = start;
stream.Read (section, 0, length);
{
bool result = false;
StrongNameSignature sn;
- FileStream fs = File.OpenRead (fileName);
- try {
+ using (FileStream fs = File.OpenRead (fileName)) {
sn = StrongHash (fs, StrongNameOptions.Signature);
- if (sn.Hash == null)
- return false;
- }
- finally {
fs.Close ();
}
+ if (sn.Hash == null)
+ return false;
byte[] signature = null;
try {
signature = sign.CreateSignature (sn.Hash);
Array.Reverse (signature);
}
- catch {
+ catch (CryptographicException) {
return false;
}
- try {
- fs = File.OpenWrite (fileName);
+ using (FileStream fs = File.OpenWrite (fileName)) {
fs.Position = sn.SignaturePosition;
fs.Write (signature, 0, signature.Length);
- }
- catch {
- }
- finally {
fs.Close ();
result = true;
}
public bool Verify (string fileName)
{
- bool result = false;
StrongNameSignature sn;
- FileStream fs = File.OpenRead (fileName);
- try {
+ using (FileStream fs = File.OpenRead (fileName)) {
sn = StrongHash (fs, StrongNameOptions.Signature);
- if (sn.Hash == null)
+ fs.Close ();
+ }
+ if (sn.Hash == null) {
+ Console.WriteLine ("hash is null");
+ return false;
+ }
+
+ try {
+ AssemblyHashAlgorithm algorithm = AssemblyHashAlgorithm.SHA1;
+ if (tokenAlgorithm == "MD5")
+ algorithm = AssemblyHashAlgorithm.MD5;
+ bool v = Verify (rsa, algorithm, sn.Hash, sn.Signature);
+ Console.WriteLine ("returning v: " + v);
+ return v;
+ }
+ catch (CryptographicException e) {
+ // no exception allowed
+ Console.WriteLine ("exception: " + e);
+ return false;
+ }
+ }
+
+#if INSIDE_CORLIB
+ // We don't want a dependency on StrongNameManager in Mono.Security.dll
+ static public bool IsAssemblyStrongnamed (string assemblyName)
+ {
+ try {
+ // this doesn't load the assembly (well it unloads it ;)
+ // http://weblogs.asp.net/nunitaddin/posts/9991.aspx
+ AssemblyName an = AssemblyName.GetAssemblyName (assemblyName);
+ if (an == null)
return false;
+
+ byte[] publicKey = StrongNameManager.GetMappedPublicKey (an.GetPublicKeyToken ());
+ if ((publicKey == null) || (publicKey.Length < 12)) {
+ // no mapping
+ publicKey = an.GetPublicKey ();
+ if ((publicKey == null) || (publicKey.Length < 12))
+ return false;
+ }
+
+ // Note: MustVerify is based on the original token (by design). Public key
+ // remapping won't affect if the assembly is verified or not.
+ if (!StrongNameManager.MustVerify (an)) {
+ return true;
+ }
+
+ RSA rsa = CryptoConvert.FromCapiPublicKeyBlob (publicKey, 12);
+ StrongName sn = new StrongName (rsa);
+ bool result = sn.Verify (assemblyName);
+ return result;
}
- finally {
- fs.Close ();
+ catch {
+ // no exception allowed
+ return false;
}
+ }
+ // TODO
+ // we would get better performance if the runtime hashed the
+ // assembly - as we wouldn't have to load it from disk a
+ // second time. The runtime already have implementations of
+ // SHA1 (and even MD5 if required someday).
+ static public bool VerifySignature (byte[] publicKey, int algorithm, byte[] hash, byte[] signature)
+ {
try {
- RSAPKCS1SignatureDeformatter vrfy = new RSAPKCS1SignatureDeformatter (rsa);
- vrfy.SetHashAlgorithm (TokenAlgorithm);
- result = vrfy.VerifySignature (sn.Hash, sn.Signature);
+ RSA rsa = CryptoConvert.FromCapiPublicKeyBlob (publicKey);
+ return Verify (rsa, (AssemblyHashAlgorithm) algorithm, hash, signature);
}
catch {
+ // no exception allowed
+ return false;
}
- return result;
}
- }
+#endif
+ static private bool Verify (RSA rsa, AssemblyHashAlgorithm algorithm, byte[] hash, byte[] signature)
+ {
+ RSAPKCS1SignatureDeformatter vrfy = new RSAPKCS1SignatureDeformatter (rsa);
+ switch (algorithm) {
+ case AssemblyHashAlgorithm.MD5:
+ vrfy.SetHashAlgorithm ("MD5");
+ break;
+ case AssemblyHashAlgorithm.SHA1:
+ case AssemblyHashAlgorithm.None:
+ default:
+ vrfy.SetHashAlgorithm ("SHA1");
+ break;
+ }
+ return vrfy.VerifySignature (hash, signature);
+ }
+ }
}