Click here to Skip to main content
15,892,059 members
Articles / Programming Languages / C#

Parsing strong name signatures generated with sn.exe

Rate me:
Please Sign up or sign in to vote.
4.58/5 (14 votes)
4 May 20042 min read 115.1K   1.6K   25  
This article shows how to parse Assembly "strong name keyfiles" generated with sn.exe
//------------------------------------------------------------------------
// (c) 2002-2004 by Per Anderson.  All rights reserved.
//------------------------------------------------------------------------

using System;
using System.IO;
using System.Text;
using System.Xml; 
using System.Reflection;
using System.Security.Cryptography; 
using System.Collections;


namespace Bullfrog.Compress.RSA
{

	/// <summary>
	/// NOTE:  These methods assume 1024 bit keys, the same 
	/// as exported from sn.exe
	/// See also CryptExportKey() Win32 API 		 		 
	/// </summary>
	public class RSA1024Util
	{
		public RSA1024Util()
		{
			// This will generate a new key 
			CtorFromXml(null); 
		}

		public RSA1024Util(string xmlkey) 
		{			
			CtorFromXml(xmlkey); 			
		} 

		public RSA1024Util(byte[] snkbuf)
		{
			CtorFromSnkBuf(snkbuf); 
		}
	
		/// <summary>
		/// NOTE:  Passing null is allowed to generate new keys 
		/// </summary>
		/// <param name="xmlkey"></param>
		protected void CtorFromXml(string xmlkey)
		{
			_rsa = new RSACryptoServiceProvider(1024);			
			if ((xmlkey == null) || (xmlkey.Length < 1))
				return; 
			
			_rsa.FromXmlString(xmlkey); 				
		}

		protected void CtorFromSnkBuf(byte[] snkbuf)
		{
			if ((snkbuf == null) || (snkbuf.Length < 1))
			{
				CtorFromXml(null);
				return; 
			}

			RSAParameters param = FigureParams(snkbuf); 

			// Must set KeyNumber to AT_SIGNATURE for strong 
			// name keypair to correctly be imported.  			
			CspParameters cp = new CspParameters(); 
			cp.KeyNumber = 2; // AT_SIGNATURE 

			_rsa = new RSACryptoServiceProvider(1024, cp); 				
			_rsa.ImportParameters(param); 

		}

				
		protected RSACryptoServiceProvider _rsa; 		
		protected RSACryptoServiceProvider RSA 
		{
			get
			{				
				return _rsa; 
			}
		}


		protected Encoding _pt_enc = null; 
		public Encoding PlainTextEnc 
		{
			get
			{
				if (_pt_enc == null)
					_pt_enc = new UnicodeEncoding();
				return _pt_enc; 
			}
			set
			{
				_pt_enc = value; 
			}
		}
		

		protected SHA1CryptoServiceProvider _sha = null; 
		protected SHA1CryptoServiceProvider SHA 
		{
			get
			{
				if (_sha == null)
					_sha = new SHA1CryptoServiceProvider(); 
				return _sha; 
			}
		}


		/// <summary>
		/// NOTE:  Output is bytes in UTF8 or Unicode encoding.  
		/// (Encoder set in PlainTextEnc) 
		/// </summary>		
		public byte[] PlainTextToByte(string s)
		{
			if ((s == null) || (s.Length < 1)) return null; 
			return PlainTextEnc.GetBytes(s);			
		}

		/// <summary>
		/// NOTE:  Uses UTF8 or Unicode encoding.  
		/// Pass UTF8/Unicode bytes. 
		/// (Encoder set in PlainTextEnc) 
		/// </summary>				
		public string ByteToPlainText(byte[] bytes)
		{
			if ((bytes == null) || (bytes.Length < 1)) return null;
			return PlainTextEnc.GetString(bytes); 			
		}

		
		/// <summary>
		/// NOTE:  Uses Base64 Encoding.  
		/// Pass base64 encoded string representing original bytes.
		/// </summary>
		public byte[] Base64StringToByte(string s)
		{
			if ((s == null) || (s.Length < 1)) return null;
			return Convert.FromBase64String(s); 
		}

		
		/// <summary>
		/// NOTE:  Uses Base64 Encoding.  
		/// Returns base64 encoded string representing passed bytes.
		/// </summary>
		public string ByteToBase64String(byte[] bytes)
		{
			if ((bytes == null) || (bytes.Length < 1)) return null;
			return Convert.ToBase64String(bytes);
		}


		protected static byte[] BlockCopy(byte[] source, int idx, int size)
		{
			if ((source == null) || (source.Length < (idx + size))) 
				return null;

			byte[] ret = new byte[size];
			Buffer.BlockCopy(source, idx, ret, 0, size); 
			return ret; 
		}
		

		public string PrivKeyXml
		{
			get
			{
				return RSA.ToXmlString(/*includepriv*/ true); 
			}
		}

		public string PubKeyXml
		{
			get
			{				
				return RSA.ToXmlString(/*includepriv*/ false); 
			}
		}


		// For reading SNK Files 
		public static bool SnkBufIsPubLength(byte[] keypair)
		{
			if (keypair == null) return false; 
			return (keypair.Length == 160); 
		}

		/// <summary>
		/// Check that RSA1 is in header (public key only) 
		/// </summary>
		/// <param name="keypair"></param>
		/// <returns></returns>
		public static bool CheckRSA1(byte[] pubkey)
		{
			// Check that RSA1 is in header 
			//                             R     S     A     1 
			byte[] check = new byte[] { 0x52, 0x53, 0x41, 0x31 }; 
			return CheckMagic(pubkey, check, _magic_pub_idx); 
		}

		/// <summary>
		/// Check that RSA2 is in header (public and private key) 
		/// </summary>
		/// <param name="keypair"></param>
		/// <returns></returns>
		public static bool CheckRSA2(byte[] pubkey)
		{
			// Check that RSA2 is in header 
			//                             R     S     A     2 
			byte[] check = new byte[] { 0x52, 0x53, 0x41, 0x32 }; 
			return CheckMagic(pubkey, check, _magic_priv_idx); 
		}


		protected static bool CheckMagic(byte[] keypair, byte[] check, int idx)
		{			
			byte[] magic = BlockCopy(keypair, idx, _magic_size); 
			if (magic == null) return false; 

			for (int i = 0; i < _magic_size; i++)
			{
				if (check[i] != magic[i])
					return false; 					
			}

			return true; 
		}
		
		// Magic
		// Will be RSA1 or RSA2 
		public const int _magic_priv_idx = 0x08; 
		public const int _magic_pub_idx = 0x14; 
		public const int _magic_size = 4; 
			
		public static RSAParameters FigureParams(
			byte[] keypair)
		{
			RSAParameters ret = new RSAParameters(); 

			if ((keypair == null) || (keypair.Length < 1)) 
				return ret; 				
			
			bool pubonly = SnkBufIsPubLength(keypair); 

			if ((pubonly) && (!CheckRSA1(keypair)))
				return ret; 
						
			if ((!pubonly) && (!CheckRSA2(keypair)))
				return ret; 
					
			int magic_idx = pubonly ? _magic_pub_idx : _magic_priv_idx;
			

			// Bitlen is stored here, but note this 
			// class is only set up for 1024 bit length keys 
			int bitlen_idx = magic_idx + _magic_size; 
			int bitlen_size = 4;  // DWORD

			// Exponent 
			// In read file, will usually be { 1, 0, 1, 0 } or 65537
			int exp_idx = bitlen_idx + bitlen_size; 
			int exp_size = 4; 


			//BYTE modulus[rsapubkey.bitlen/8]; == MOD; Size 128 
			int mod_idx = exp_idx + exp_size;
			int mod_size = 128; 	

			//BYTE prime1[rsapubkey.bitlen/16]; == P; Size 64 
			int p_idx = mod_idx + mod_size; 
			int p_size = 64; 

			//BYTE prime2[rsapubkey.bitlen/16]; == Q; Size 64 		
			int q_idx = p_idx + p_size; 
			int q_size = 64; 

			//BYTE exponent1[rsapubkey.bitlen/16]; == DP; Size 64
			int dp_idx = q_idx + q_size; 
			int dp_size = 64; 

			//BYTE exponent2[rsapubkey.bitlen/16]; == DQ; Size 64 		
			int dq_idx = dp_idx + dp_size; 
			int dq_size = 64; 

			//BYTE coefficient[rsapubkey.bitlen/16]; == InverseQ; Size 64
			int invq_idx = dq_idx + dq_size; 
			int invq_size = 64; 

			//BYTE privateExponent[rsapubkey.bitlen/8]; == D; Size 128 
			int d_idx = invq_idx + invq_size; 
			int d_size = 128; 
							

			// Figure public params 
			// Must reverse order (little vs. big endian issue)
			ret.Exponent = BlockCopy(keypair, exp_idx, exp_size); 
			Array.Reverse(ret.Exponent); 
			ret.Modulus = BlockCopy(keypair, mod_idx, mod_size); 
			Array.Reverse(ret.Modulus); 
					
			if (pubonly) return ret; 

			// Figure private params 			
			// Must reverse order (little vs. big endian issue)
			ret.P = BlockCopy(keypair, p_idx, p_size); 
			Array.Reverse(ret.P); 

			ret.Q = BlockCopy(keypair, q_idx, q_size); 
			Array.Reverse(ret.Q); 

			ret.DP = BlockCopy(keypair, dp_idx, dp_size); 
			Array.Reverse(ret.DP); 

			ret.DQ = BlockCopy(keypair, dq_idx, dq_size); 
			Array.Reverse(ret.DQ); 

			ret.InverseQ = BlockCopy(keypair, invq_idx, invq_size); 
			Array.Reverse(ret.InverseQ); 

			ret.D = BlockCopy(keypair, d_idx, d_size); 
			Array.Reverse(ret.D); 
			
			return ret; 
		}

	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
Per Anderson is the founder of Sunfrog Technologies LLC, http://sunfrog-tech.com .

Comments and Discussions