This code worked on fx 1.1 and now I get a "Keyset does not exist" error
when trying to SignData with RSA under FX2.0.
Smells like some security error, but can't debug it as I think error is
thrown in win32. If you create a new RSA, it works. But creating RSA from
an SNK does not work.
string privSnk = @"v:\wsesimplet cpdll\wsesimple tcpdll.snk"; // Use any snk
file.
System.Security .Cryptography.R SACryptoService Provider rsa =
SnkUtil.GetRSAF romSnkFile(priv Snk);
byte[] data = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
byte[] sig = rsa.SignData(da ta, new SHA1CryptoServi ceProvider());
// Error on line 4 (i.e. rsa.SignData)
System.Security .Cryptography.C ryptographicExc eption was unhandled
Message="Keyset does not exist\r\n"
Source="mscorli b"
StackTrace:
at
System.Security .Cryptography.C ryptographicExc eption.ThrowCry ptogaphicExcept ion(Int32
hr)
....
// SnkUtil Class
using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Reflecti on;
using System.Security .Cryptography;
using System.Collecti ons;
namespace WSESimpleTCPDLL
{
/// <summary>
/// One static method to get an RSACryptoServic eProvider from a *.snk file.
/// NOTE: These methods assume 1024 bit keys, the same as exported from
sn.exe.
/// </summary>
public sealed class SnkUtil
{
#region Fields
private const int magic_priv_idx = 0x08;
private const int magic_pub_idx = 0x14;
private const int magic_size = 4;
#endregion
#region Constructors
private SnkUtil()
{
}
#endregion
#region Public Methods
/// <summary>
/// Returns RSA object from *.snk key file.
/// </summary>
/// <param name="path">Pat h to snk file.</param>
/// <returns>RSACry ptoServiceProvi der</returns>
public static RSACryptoServic eProvider GetRSAFromSnkFi le(string path)
{
if ( path == null )
throw new ArgumentNullExc eption("path");
byte[] snkBytes = GetFileBytes(pa th);
if ( snkBytes == null )
throw new Exception("Inva lid SNK file.");
RSACryptoServic eProvider rsa = GetRSAFromSnkBy tes(snkBytes);
return rsa;
}
public static RSACryptoServic eProvider GetPublicKeyFro mAssembly(Assem bly
assembly)
{
if ( assembly == null )
throw new ArgumentNullExc eption("assembl y");
byte[] pubkey = assembly.GetNam e().GetPublicKe y();
if ( pubkey.Length == 0 )
throw new Exception("No public key in assembly.");
RSAParameters p = SnkUtil.GetRSAP arameters(pubke y);
RSACryptoServic eProvider rsa = new RSACryptoServic eProvider();
rsa.ImportParam eters(p);
return rsa;
}
/// <summary>
/// Returns RSAParameters from byte[].
/// Example to get rsa public key from assembly:
/// byte[] pubkey =
System.Reflecti on.Assembly.Get ExecutingAssemb ly().GetName(). GetPublicKey();
/// RSAParameters p = SnkUtil.GetRSAP arameters(pubke y);
/// </summary>
/// <param name="keypair"> </param>
/// <returns></returns>
public static RSAParameters GetRSAParameter s(byte[] keyBytes)
{
RSAParameters ret = new RSAParameters() ;
if ((keyBytes == null) || (keyBytes.Lengt h < 1))
throw new ArgumentNullExc eption("keyByte s");
bool pubonly = SnkBufIsPubLeng th(keyBytes);
if ((pubonly) && (!CheckRSA1(key Bytes)))
return ret;
if ((!pubonly) && (!CheckRSA2(key Bytes)))
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.bitle n/8]; == MOD; Size 128
int mod_idx = exp_idx + exp_size;
int mod_size = 128;
//BYTE prime1[rsapubkey.bitle n/16]; == P; Size 64
int p_idx = mod_idx + mod_size;
int p_size = 64;
//BYTE prime2[rsapubkey.bitle n/16]; == Q; Size 64
int q_idx = p_idx + p_size;
int q_size = 64;
//BYTE exponent1[rsapubkey.bitle n/16]; == DP; Size 64
int dp_idx = q_idx + q_size;
int dp_size = 64;
//BYTE exponent2[rsapubkey.bitle n/16]; == DQ; Size 64
int dq_idx = dp_idx + dp_size;
int dq_size = 64;
//BYTE coefficient[rsapubkey.bitle n/16]; == InverseQ; Size 64
int invq_idx = dq_idx + dq_size;
int invq_size = 64;
//BYTE privateExponent[rsapubkey.bitle n/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(keyBy tes, exp_idx, exp_size);
Array.Reverse(r et.Exponent);
ret.Modulus = BlockCopy(keyBy tes, mod_idx, mod_size);
Array.Reverse(r et.Modulus);
if (pubonly) return ret;
// Figure private params
// Must reverse order (little vs. big endian issue)
ret.P = BlockCopy(keyBy tes, p_idx, p_size);
Array.Reverse(r et.P);
ret.Q = BlockCopy(keyBy tes, q_idx, q_size);
Array.Reverse(r et.Q);
ret.DP = BlockCopy(keyBy tes, dp_idx, dp_size);
Array.Reverse(r et.DP);
ret.DQ = BlockCopy(keyBy tes, dq_idx, dq_size);
Array.Reverse(r et.DQ);
ret.InverseQ = BlockCopy(keyBy tes, invq_idx, invq_size);
Array.Reverse(r et.InverseQ);
ret.D = BlockCopy(keyBy tes, d_idx, d_size);
Array.Reverse(r et.D);
return ret;
}
#endregion
#region Private Methods
private static byte[] GetFileBytes(st ring path)
{
using ( FileStream fs = new FileStream(path , FileMode.Open,
FileAccess.Read ) )
using ( BinaryReader br = new BinaryReader(fs ) )
{
byte[] bytes = br.ReadBytes((i nt)fs.Length);
return bytes;
}
}
private static RSACryptoServic eProvider GetRSAFromSnkBy tes(byte[]
snkBytes)
{
if ( snkBytes == null )
throw new ArgumentNullExc eption("snkByte s");
RSAParameters param = GetRSAParameter s(snkBytes);
// Must set KeyNumber to AT_SIGNATURE for strong
// name keypair to be correctly imported.
CspParameters cp = new CspParameters() ;
cp.KeyNumber = 2; // AT_SIGNATURE
RSACryptoServic eProvider rsa = new RSACryptoServic eProvider(1024, cp);
rsa.ImportParam eters(param);
return rsa;
}
private static byte[] BlockCopy(byte[] source, int idx, int size)
{
if ( (source == null) || (source.Length < (idx + size)) )
return null;
byte[] ret = new byte[size];
Buffer.BlockCop y(source, idx, ret, 0, size);
return ret;
}
/// <summary>
/// Returns true if buffer length is public key size.
/// </summary>
/// <param name="keypair"> </param>
/// <returns></returns>
private static bool SnkBufIsPubLeng th(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>
private 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(pubk ey, check, magic_pub_idx);
}
/// <summary>
/// Check that RSA2 is in header (public and private key).
/// </summary>
/// <param name="keypair"> </param>
/// <returns></returns>
private 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(pubk ey, check, magic_priv_idx) ;
}
private static bool CheckMagic(byte[] keypair, byte[] check, int idx)
{
byte[] magic = BlockCopy(keypa ir, 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;
}
#endregion
}
}
--
William Stacey [MVP]