diff --git a/Mono.Cecil/AssemblyWriter.cs b/Mono.Cecil/AssemblyWriter.cs index dd9951612..3ea1088b3 100644 --- a/Mono.Cecil/AssemblyWriter.cs +++ b/Mono.Cecil/AssemblyWriter.cs @@ -102,8 +102,8 @@ static void Write (ModuleDefinition module, Disposable stream, WriterPar if (symbol_writer_provider == null && parameters.WriteSymbols) symbol_writer_provider = new DefaultSymbolWriterProvider (); - if (parameters.StrongNameKeyPair != null && name != null) { - name.PublicKey = parameters.StrongNameKeyPair.PublicKey; + if (parameters.HasStrongNameKey && name != null) { + name.PublicKey = CryptoService.GetPublicKey (parameters); module.Attributes |= ModuleAttributes.StrongNameSigned; } @@ -125,8 +125,8 @@ static void Write (ModuleDefinition module, Disposable stream, WriterPar stream.value.SetLength (0); writer.WriteImage (); - if (parameters.StrongNameKeyPair != null) - CryptoService.StrongName (stream.value, writer, parameters.StrongNameKeyPair); + if (parameters.HasStrongNameKey) + CryptoService.StrongName (stream.value, writer, parameters); } } finally { module.metadata_builder = null; diff --git a/Mono.Cecil/ModuleDefinition.cs b/Mono.Cecil/ModuleDefinition.cs index d98064f5a..707a1358f 100644 --- a/Mono.Cecil/ModuleDefinition.cs +++ b/Mono.Cecil/ModuleDefinition.cs @@ -183,6 +183,8 @@ public sealed class WriterParameters { Stream symbol_stream; ISymbolWriterProvider symbol_writer_provider; bool write_symbols; + byte [] key_blob; + string key_container; SR.StrongNameKeyPair key_pair; public uint? Timestamp { @@ -205,6 +207,20 @@ public bool WriteSymbols { set { write_symbols = value; } } + public bool HasStrongNameKey { + get { return key_pair != null || key_blob != null || key_container != null; } + } + + public byte [] StrongNameKeyBlob { + get { return key_blob; } + set { key_blob = value; } + } + + public string StrongNameKeyContainer { + get { return key_container; } + set { key_container = value; } + } + public SR.StrongNameKeyPair StrongNameKeyPair { get { return key_pair; } set { key_pair = value; } diff --git a/Mono.Security.Cryptography/CryptoConvert.cs b/Mono.Security.Cryptography/CryptoConvert.cs index a37d3b90c..cd68f99e2 100644 --- a/Mono.Security.Cryptography/CryptoConvert.cs +++ b/Mono.Security.Cryptography/CryptoConvert.cs @@ -44,6 +44,16 @@ static private uint ToUInt32LE (byte [] bytes, int offset) return (uint)((bytes [offset+3] << 24) | (bytes [offset+2] << 16) | (bytes [offset+1] << 8) | bytes [offset]); } + static private byte [] GetBytesLE (int val) + { + return new byte [] { + (byte) (val & 0xff), + (byte) ((val >> 8) & 0xff), + (byte) ((val >> 16) & 0xff), + (byte) ((val >> 24) & 0xff) + }; + } + static private byte[] Trim (byte[] array) { for (int i=0; i < array.Length; i++) { @@ -240,5 +250,41 @@ static public RSA FromCapiKeyBlob (byte[] blob, int offset) } throw new CryptographicException ("Unknown blob format."); } + + static public byte[] ToCapiPublicKeyBlob (RSA rsa) + { + RSAParameters p = rsa.ExportParameters (false); + int keyLength = p.Modulus.Length; // in bytes + byte[] blob = new byte [20 + keyLength]; + + blob [0] = 0x06; // Type - PUBLICKEYBLOB (0x06) + blob [1] = 0x02; // Version - Always CUR_BLOB_VERSION (0x02) + // [2], [3] // RESERVED - Always 0 + blob [5] = 0x24; // ALGID - Always 00 24 00 00 (for CALG_RSA_SIGN) + blob [8] = 0x52; // Magic - RSA1 (ASCII in hex) + blob [9] = 0x53; + blob [10] = 0x41; + blob [11] = 0x31; + + byte[] bitlen = GetBytesLE (keyLength << 3); + blob [12] = bitlen [0]; // bitlen + blob [13] = bitlen [1]; + blob [14] = bitlen [2]; + blob [15] = bitlen [3]; + + // public exponent (DWORD) + int pos = 16; + int n = p.Exponent.Length; + while (n > 0) + blob [pos++] = p.Exponent [--n]; + // modulus + pos = 20; + byte[] part = p.Modulus; + int len = part.Length; + Array.Reverse (part, 0, len); + Buffer.BlockCopy (part, 0, blob, pos, len); + pos += len; + return blob; + } } } diff --git a/Mono.Security.Cryptography/CryptoService.cs b/Mono.Security.Cryptography/CryptoService.cs index 7a4198b5c..013e0019d 100644 --- a/Mono.Security.Cryptography/CryptoService.cs +++ b/Mono.Security.Cryptography/CryptoService.cs @@ -26,11 +26,33 @@ namespace Mono.Cecil { static class CryptoService { - public static void StrongName (Stream stream, ImageWriter writer, StrongNameKeyPair key_pair) + public static byte [] GetPublicKey (WriterParameters parameters) + { + using (var rsa = parameters.CreateRSA ()) { + var cspBlob = CryptoConvert.ToCapiPublicKeyBlob (rsa); + var publicKey = new byte [12 + cspBlob.Length]; + Buffer.BlockCopy (cspBlob, 0, publicKey, 12, cspBlob.Length); + // The first 12 bytes are documented at: + // http://msdn.microsoft.com/library/en-us/cprefadd/html/grfungethashfromfile.asp + // ALG_ID - Signature + publicKey [1] = 36; + // ALG_ID - Hash + publicKey [4] = 4; + publicKey [5] = 128; + // Length of Public Key (in bytes) + publicKey [8] = (byte) (cspBlob.Length >> 0); + publicKey [9] = (byte) (cspBlob.Length >> 8); + publicKey [10] = (byte) (cspBlob.Length >> 16); + publicKey [11] = (byte) (cspBlob.Length >> 24); + return publicKey; + } + } + + public static void StrongName (Stream stream, ImageWriter writer, WriterParameters parameters) { int strong_name_pointer; - var strong_name = CreateStrongName (key_pair, HashStream (stream, writer, out strong_name_pointer)); + var strong_name = CreateStrongName (parameters, HashStream (stream, writer, out strong_name_pointer)); PatchStrongName (stream, strong_name_pointer, strong_name); } @@ -40,11 +62,11 @@ static void PatchStrongName (Stream stream, int strong_name_pointer, byte [] str stream.Write (strong_name, 0, strong_name.Length); } - static byte [] CreateStrongName (StrongNameKeyPair key_pair, byte [] hash) + static byte [] CreateStrongName (WriterParameters parameters, byte [] hash) { const string hash_algo = "SHA1"; - using (var rsa = key_pair.CreateRSA ()) { + using (var rsa = parameters.CreateRSA ()) { var formatter = new RSAPKCS1SignatureFormatter (rsa); formatter.SetHashAlgorithm (hash_algo); @@ -74,7 +96,6 @@ static byte [] HashStream (Stream stream, ImageWriter writer, out int strong_nam var sha1 = new SHA1Managed (); var buffer = new byte [buffer_size]; using (var crypto_stream = new CryptoStream (Stream.Null, sha1, CryptoStreamMode.Write)) { - stream.Seek (0, SeekOrigin.Begin); CopyStreamChunk (stream, crypto_stream, buffer, header_size); @@ -148,12 +169,17 @@ public static Guid ComputeGuid (byte [] hash) static partial class Mixin { - public static RSA CreateRSA (this StrongNameKeyPair key_pair) + public static RSA CreateRSA (this WriterParameters writer_parameters) { byte [] key; string key_container; - if (!TryGetKeyContainer (key_pair, out key, out key_container)) + if (writer_parameters.StrongNameKeyBlob != null) + return CryptoConvert.FromCapiKeyBlob (writer_parameters.StrongNameKeyBlob); + + if (writer_parameters.StrongNameKeyContainer != null) + key_container = writer_parameters.StrongNameKeyContainer; + else if (!TryGetKeyContainer (writer_parameters.StrongNameKeyPair, out key, out key_container)) return CryptoConvert.FromCapiKeyBlob (key); var parameters = new CspParameters {