diff options
Diffstat (limited to 'Mono.Security.Cryptography')
-rw-r--r-- | Mono.Security.Cryptography/CryptoConvert.cs | 243 | ||||
-rw-r--r-- | Mono.Security.Cryptography/CryptoService.cs | 208 |
2 files changed, 451 insertions, 0 deletions
diff --git a/Mono.Security.Cryptography/CryptoConvert.cs b/Mono.Security.Cryptography/CryptoConvert.cs new file mode 100644 index 0000000..26a4ba2 --- /dev/null +++ b/Mono.Security.Cryptography/CryptoConvert.cs @@ -0,0 +1,243 @@ +// +// CryptoConvert.cs - Crypto Convertion Routines +// +// Author: +// Sebastien Pouliot <sebastien@ximian.com> +// +// (C) 2003 Motus Technologies Inc. (http://www.motus.com) +// Copyright (C) 2004-2006 Novell Inc. (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Security.Cryptography; + +#if !(SILVERLIGHT || READ_ONLY) + +namespace Mono.Security.Cryptography { + + static class CryptoConvert { + + static private int ToInt32LE (byte [] bytes, int offset) + { + return (bytes [offset+3] << 24) | (bytes [offset+2] << 16) | (bytes [offset+1] << 8) | bytes [offset]; + } + + 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[] Trim (byte[] array) + { + for (int i=0; i < array.Length; i++) { + if (array [i] != 0x00) { + byte[] result = new byte [array.Length - i]; + Buffer.BlockCopy (array, i, result, 0, result.Length); + return result; + } + } + return null; + } + + static RSA FromCapiPrivateKeyBlob (byte[] blob, int offset) + { + RSAParameters rsap = new RSAParameters (); + try { + if ((blob [offset] != 0x07) || // PRIVATEKEYBLOB (0x07) + (blob [offset+1] != 0x02) || // Version (0x02) + (blob [offset+2] != 0x00) || // Reserved (word) + (blob [offset+3] != 0x00) || + (ToUInt32LE (blob, offset+8) != 0x32415352)) // DWORD magic = RSA2 + throw new CryptographicException ("Invalid blob header"); + + // ALGID (CALG_RSA_SIGN, CALG_RSA_KEYX, ...) + // int algId = ToInt32LE (blob, offset+4); + + // DWORD bitlen + int bitLen = ToInt32LE (blob, offset+12); + + // DWORD public exponent + byte[] exp = new byte [4]; + Buffer.BlockCopy (blob, offset+16, exp, 0, 4); + Array.Reverse (exp); + rsap.Exponent = Trim (exp); + + int pos = offset+20; + // BYTE modulus[rsapubkey.bitlen/8]; + int byteLen = (bitLen >> 3); + rsap.Modulus = new byte [byteLen]; + Buffer.BlockCopy (blob, pos, rsap.Modulus, 0, byteLen); + Array.Reverse (rsap.Modulus); + pos += byteLen; + + // BYTE prime1[rsapubkey.bitlen/16]; + int byteHalfLen = (byteLen >> 1); + rsap.P = new byte [byteHalfLen]; + Buffer.BlockCopy (blob, pos, rsap.P, 0, byteHalfLen); + Array.Reverse (rsap.P); + pos += byteHalfLen; + + // BYTE prime2[rsapubkey.bitlen/16]; + rsap.Q = new byte [byteHalfLen]; + Buffer.BlockCopy (blob, pos, rsap.Q, 0, byteHalfLen); + Array.Reverse (rsap.Q); + pos += byteHalfLen; + + // BYTE exponent1[rsapubkey.bitlen/16]; + rsap.DP = new byte [byteHalfLen]; + Buffer.BlockCopy (blob, pos, rsap.DP, 0, byteHalfLen); + Array.Reverse (rsap.DP); + pos += byteHalfLen; + + // BYTE exponent2[rsapubkey.bitlen/16]; + rsap.DQ = new byte [byteHalfLen]; + Buffer.BlockCopy (blob, pos, rsap.DQ, 0, byteHalfLen); + Array.Reverse (rsap.DQ); + pos += byteHalfLen; + + // BYTE coefficient[rsapubkey.bitlen/16]; + rsap.InverseQ = new byte [byteHalfLen]; + Buffer.BlockCopy (blob, pos, rsap.InverseQ, 0, byteHalfLen); + Array.Reverse (rsap.InverseQ); + pos += byteHalfLen; + + // ok, this is hackish but CryptoAPI support it so... + // note: only works because CRT is used by default + // http://bugzilla.ximian.com/show_bug.cgi?id=57941 + rsap.D = new byte [byteLen]; // must be allocated + if (pos + byteLen + offset <= blob.Length) { + // BYTE privateExponent[rsapubkey.bitlen/8]; + Buffer.BlockCopy (blob, pos, rsap.D, 0, byteLen); + Array.Reverse (rsap.D); + } + } + catch (Exception e) { + throw new CryptographicException ("Invalid blob.", e); + } + + RSA rsa = null; + try { + rsa = RSA.Create (); + rsa.ImportParameters (rsap); + } + catch (CryptographicException ce) { + // this may cause problem when this code is run under + // the SYSTEM identity on Windows (e.g. ASP.NET). See + // http://bugzilla.ximian.com/show_bug.cgi?id=77559 + try { + CspParameters csp = new CspParameters (); + csp.Flags = CspProviderFlags.UseMachineKeyStore; + rsa = new RSACryptoServiceProvider (csp); + rsa.ImportParameters (rsap); + } + catch { + // rethrow original, not the later, exception if this fails + throw ce; + } + } + return rsa; + } + + static RSA FromCapiPublicKeyBlob (byte[] blob, int offset) + { + try { + if ((blob [offset] != 0x06) || // PUBLICKEYBLOB (0x06) + (blob [offset+1] != 0x02) || // Version (0x02) + (blob [offset+2] != 0x00) || // Reserved (word) + (blob [offset+3] != 0x00) || + (ToUInt32LE (blob, offset+8) != 0x31415352)) // DWORD magic = RSA1 + throw new CryptographicException ("Invalid blob header"); + + // ALGID (CALG_RSA_SIGN, CALG_RSA_KEYX, ...) + // int algId = ToInt32LE (blob, offset+4); + + // DWORD bitlen + int bitLen = ToInt32LE (blob, offset+12); + + // DWORD public exponent + RSAParameters rsap = new RSAParameters (); + rsap.Exponent = new byte [3]; + rsap.Exponent [0] = blob [offset+18]; + rsap.Exponent [1] = blob [offset+17]; + rsap.Exponent [2] = blob [offset+16]; + + int pos = offset+20; + // BYTE modulus[rsapubkey.bitlen/8]; + int byteLen = (bitLen >> 3); + rsap.Modulus = new byte [byteLen]; + Buffer.BlockCopy (blob, pos, rsap.Modulus, 0, byteLen); + Array.Reverse (rsap.Modulus); + + RSA rsa = null; + try { + rsa = RSA.Create (); + rsa.ImportParameters (rsap); + } + catch (CryptographicException) { + // this may cause problem when this code is run under + // the SYSTEM identity on Windows (e.g. ASP.NET). See + // http://bugzilla.ximian.com/show_bug.cgi?id=77559 + CspParameters csp = new CspParameters (); + csp.Flags = CspProviderFlags.UseMachineKeyStore; + rsa = new RSACryptoServiceProvider (csp); + rsa.ImportParameters (rsap); + } + return rsa; + } + catch (Exception e) { + throw new CryptographicException ("Invalid blob.", e); + } + } + + // PRIVATEKEYBLOB + // PUBLICKEYBLOB + static public RSA FromCapiKeyBlob (byte[] blob) + { + return FromCapiKeyBlob (blob, 0); + } + + static public RSA FromCapiKeyBlob (byte[] blob, int offset) + { + if (blob == null) + throw new ArgumentNullException ("blob"); + if (offset >= blob.Length) + throw new ArgumentException ("blob is too small."); + + switch (blob [offset]) { + case 0x00: + // this could be a public key inside an header + // like "sn -e" would produce + if (blob [offset + 12] == 0x06) { + return FromCapiPublicKeyBlob (blob, offset + 12); + } + break; + case 0x06: + return FromCapiPublicKeyBlob (blob, offset); + case 0x07: + return FromCapiPrivateKeyBlob (blob, offset); + } + throw new CryptographicException ("Unknown blob format."); + } + } +} + +#endif diff --git a/Mono.Security.Cryptography/CryptoService.cs b/Mono.Security.Cryptography/CryptoService.cs new file mode 100644 index 0000000..12d0d94 --- /dev/null +++ b/Mono.Security.Cryptography/CryptoService.cs @@ -0,0 +1,208 @@ +// +// CryptoService.cs +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2010 Jb Evain +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.IO; +using System.Reflection; +using System.Runtime.Serialization; +using System.Security.Cryptography; + +#if !READ_ONLY + +#if !SILVERLIGHT +using Mono.Security.Cryptography; +#endif + +using Mono.Cecil.PE; + +namespace Mono.Cecil { + + // Most of this code has been adapted + // from Jeroen Frijters' fantastic work + // in IKVM.Reflection.Emit. Thanks! + + static class CryptoService { + +#if !SILVERLIGHT + public static void StrongName (Stream stream, ImageWriter writer, StrongNameKeyPair key_pair) + { + int strong_name_pointer; + + var strong_name = CreateStrongName (key_pair, HashStream (stream, writer, out strong_name_pointer)); + PatchStrongName (stream, strong_name_pointer, strong_name); + + PatchChecksum (stream, ComputeChecksum (stream)); + } + + static void PatchStrongName (Stream stream, int strong_name_pointer, byte [] strong_name) + { + stream.Seek (strong_name_pointer, SeekOrigin.Begin); + stream.Write (strong_name, 0, strong_name.Length); + } + + static byte [] CreateStrongName (StrongNameKeyPair key_pair, byte [] hash) + { + const string hash_algo = "SHA1"; + + using (var rsa = key_pair.CreateRSA ()) { + var formatter = new RSAPKCS1SignatureFormatter (rsa); + formatter.SetHashAlgorithm (hash_algo); + + byte [] signature = formatter.CreateSignature (hash); + Array.Reverse (signature); + + return signature; + } + } + + static byte [] HashStream (Stream stream, ImageWriter writer, out int strong_name_pointer) + { + const int buffer_size = 8192; + + var text = writer.text; + var header_size = (int) writer.GetHeaderSize (); + var text_section_pointer = (int) text.PointerToRawData; + var strong_name_directory = writer.GetStrongNameSignatureDirectory (); + + if (strong_name_directory.Size == 0) + throw new InvalidOperationException (); + + strong_name_pointer = (int) (text_section_pointer + + (strong_name_directory.VirtualAddress - text.VirtualAddress)); + var strong_name_length = (int) strong_name_directory.Size; + + 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); + + stream.Seek (text_section_pointer, SeekOrigin.Begin); + CopyStreamChunk (stream, crypto_stream, buffer, (int) strong_name_pointer - text_section_pointer); + + stream.Seek (strong_name_length, SeekOrigin.Current); + CopyStreamChunk (stream, crypto_stream, buffer, (int) (stream.Length - (strong_name_pointer + strong_name_length))); + } + + return sha1.Hash; + } + + static void PatchChecksum (Stream stream, int checksum) + { + stream.Seek (0xd8, SeekOrigin.Begin); + var buffer = new ByteBuffer (4); + buffer.WriteInt32 (checksum); + + stream.Write (buffer.buffer, 0, 4); + } + + static int ComputeChecksum (Stream stream) + { + stream.Seek (0, SeekOrigin.Begin); + int count = (int) stream.Length / 4; + + var reader = new BinaryReader (stream); + long sum = 0; + for (int i = 0; i < count; i++) { + sum += reader.ReadUInt32 (); + int carry = (int) (sum >> 32); + sum &= 0xFFFFFFFFU; + sum += carry; + } + + while ((sum >> 16) != 0) + sum = (sum & 0xFFFF) + (sum >> 16); + + return (int) (sum + stream.Length); + } +#endif + static void CopyStreamChunk (Stream stream, Stream dest_stream, byte [] buffer, int length) + { + while (length > 0) { + int read = stream.Read (buffer, 0, System.Math.Min (buffer.Length, length)); + dest_stream.Write (buffer, 0, read); + length -= read; + } + } + + public static byte [] ComputeHash (string file) + { + if (!File.Exists (file)) + return Empty<byte>.Array; + + const int buffer_size = 8192; + + var sha1 = new SHA1Managed (); + + using (var stream = new FileStream (file, FileMode.Open, FileAccess.Read, FileShare.Read)) { + + var buffer = new byte [buffer_size]; + + using (var crypto_stream = new CryptoStream (Stream.Null, sha1, CryptoStreamMode.Write)) + CopyStreamChunk (stream, crypto_stream, buffer, (int) stream.Length); + } + + return sha1.Hash; + } + } + +#if !SILVERLIGHT + static partial class Mixin { + + public static RSA CreateRSA (this StrongNameKeyPair key_pair) + { + byte [] key; + string key_container; + + if (!TryGetKeyContainer (key_pair, out key, out key_container)) + return CryptoConvert.FromCapiKeyBlob (key); + + var parameters = new CspParameters { + Flags = CspProviderFlags.UseMachineKeyStore, + KeyContainerName = key_container, + KeyNumber = 2, + }; + + return new RSACryptoServiceProvider (parameters); + } + + static bool TryGetKeyContainer (ISerializable key_pair, out byte [] key, out string key_container) + { + var info = new SerializationInfo (typeof (StrongNameKeyPair), new FormatterConverter ()); + key_pair.GetObjectData (info, new StreamingContext ()); + + key = (byte []) info.GetValue ("_keyPairArray", typeof (byte [])); + key_container = info.GetString ("_keyPairContainer"); + return key_container != null; + } + } +#endif +} + +#endif |