如何从 C# 中的文件加载 RSA 公钥
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11506891/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to load the RSA public key from file in C#
提问by Umar Jamil
I need to load the following RSA public key from a file for use with the RSACryptoServiceProvider class. How can I do this?
我需要从文件中加载以下 RSA 公钥以与 RSACryptoServiceProvider 类一起使用。我怎样才能做到这一点?
-----BEGIN PUBLIC KEY-----
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/syEKqEkMtQL0+d
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX+izR
KbGMRtur2TYklnyVkjeeHfAggo8vWQmWesnOG55vQYHbOOFoJbk0EkwEr5R/PbKm
byXPPN8zwnS5/XXXXXXXXXXXX
-----END PUBLIC KEY-----
This code works with my pub key: http://www.jensign.com/opensslkey/
此代码适用于我的公钥:http: //www.jensign.com/opensslkey/
Here is the code I am using
这是我正在使用的代码
static string RSA(string input)
{
RSACryptoServiceProvider rsa = DecodeX509PublicKey(Convert.FromBase64String(GetKey()));
return (Convert.ToBase64String(rsa.Encrypt(Encoding.ASCII.GetBytes(input), false)));
}
static string GetKey()
{
return File.ReadAllText("master.pub").Replace("-----BEGIN PUBLIC KEY-----", "").Replace("-----END PUBLIC KEY-----", "");
//.Replace("\n", "");
}
private static bool CompareBytearrays(byte[] a, byte[] b)
{
if (a.Length != b.Length)
return false;
int i = 0;
foreach (byte c in a)
{
if (c != b[i])
return false;
i++;
}
return true;
}
public static RSACryptoServiceProvider DecodeX509PublicKey(byte[] x509key)
{
// encoded OID sequence for PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1"
byte[] SeqOID = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 };
byte[] seq = new byte[15];
// --------- Set up stream to read the asn.1 encoded SubjectPublicKeyInfo blob ------
MemoryStream mem = new MemoryStream(x509key);
BinaryReader binr = new BinaryReader(mem); //wrap Memory Stream with BinaryReader for easy reading
byte bt = 0;
ushort twobytes = 0;
try
{
twobytes = binr.ReadUInt16();
if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
binr.ReadByte(); //advance 1 byte
else if (twobytes == 0x8230)
binr.ReadInt16(); //advance 2 bytes
else
return null;
seq = binr.ReadBytes(15); //read the Sequence OID
if (!CompareBytearrays(seq, SeqOID)) //make sure Sequence for OID is correct
return null;
twobytes = binr.ReadUInt16();
if (twobytes == 0x8103) //data read as little endian order (actual data order for Bit String is 03 81)
binr.ReadByte(); //advance 1 byte
else if (twobytes == 0x8203)
binr.ReadInt16(); //advance 2 bytes
else
return null;
bt = binr.ReadByte();
if (bt != 0x00) //expect null byte next
return null;
twobytes = binr.ReadUInt16();
if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
binr.ReadByte(); //advance 1 byte
else if (twobytes == 0x8230)
binr.ReadInt16(); //advance 2 bytes
else
return null;
twobytes = binr.ReadUInt16();
byte lowbyte = 0x00;
byte highbyte = 0x00;
if (twobytes == 0x8102) //data read as little endian order (actual data order for Integer is 02 81)
lowbyte = binr.ReadByte(); // read next bytes which is bytes in modulus
else if (twobytes == 0x8202)
{
highbyte = binr.ReadByte(); //advance 2 bytes
lowbyte = binr.ReadByte();
}
else
return null;
byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; //reverse byte order since asn.1 key uses big endian order
int modsize = BitConverter.ToInt32(modint, 0);
byte firstbyte = binr.ReadByte();
binr.BaseStream.Seek(-1, SeekOrigin.Current);
if (firstbyte == 0x00)
{ //if first byte (highest order) of modulus is zero, don't include it
binr.ReadByte(); //skip this null byte
modsize -= 1; //reduce modulus buffer size by 1
}
byte[] modulus = binr.ReadBytes(modsize); //read the modulus bytes
if (binr.ReadByte() != 0x02) //expect an Integer for the exponent data
return null;
int expbytes = (int)binr.ReadByte(); // should only need one byte for actual exponent data (for all useful values)
byte[] exponent = binr.ReadBytes(expbytes);
// ------- create RSACryptoServiceProvider instance and initialize with public key -----
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSAParameters RSAKeyInfo = new RSAParameters();
RSAKeyInfo.Modulus = modulus;
RSAKeyInfo.Exponent = exponent;
RSA.ImportParameters(RSAKeyInfo);
return RSA;
}
catch (Exception)
{
return null;
}
finally { binr.Close(); }
}
Just call the 'RSA' method with the text you want to encrypt and you're done.
只需使用要加密的文本调用“RSA”方法即可。
回答by Hailton
If you're talking about an X509 certificate:
如果您在谈论 X509 证书:
FileStream fs = new FileStream("your_cert_file.crt", FileMode.Open);
byte[] certBytes = new byte[fs.Length];
fs.Read(certBytes, 0, (Int32)fs.Length);
fs.Close();
System.Security.Cryptography.X509Certificates.X509Certificate x509cert =
new X509Certificate(certBytes);
Console.WriteLine(x509cert.GetPublicKey());
Console.WriteLine(x509cert.GetPublicKeyString());
EDITED after the comment of @hkproj done in "7/16/2012 15:04:58 Z":
在@hkproj 在“7/16/2012 15:04:58 Z”中发表评论后编辑:
Looking around here I found "Reading PEM RSA Public Key Only using Bouncy Castle". I guess what you want is this:
环顾四周,我发现“仅使用 Bouncy Castle 读取 PEM RSA 公钥”。我想你想要的是这个:
using (StreamReader reader = File.OpenText(@"c:\RSA.txt"))
{
Org.BouncyCastle.OpenSsl.PemReader pr =
new Org.BouncyCastle.OpenSsl.PemReader(reader);
Org.BouncyCastle.Utilities.IO.Pem.PemObject po = pr.ReadPemObject();
Console.WriteLine("PemObject, Type: " + po.Type);
Console.WriteLine("PemObject, Length: " + po.Content.Length);
}
However, with your file I get an error: System.IO.IOException : base64 data appears to be truncated.
但是,对于您的文件,我收到一个错误:System.IO.IOException : base64 data appears to be truncated.
So changing your file to something like:
因此,将您的文件更改为:
-----BEGIN PUBLIC KEY-----
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/syEKqEkMtQL0+d
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX+izR
KbGMRtur2TYklnyVkjeeHfAggo8vWQmWesnOG55vQYHbOOFoJbk0EkwEr5R/PbKm
byXPPN8zwnS5/XXXXXXXXXXXXZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
-----END PUBLIC KEY-----
The result is:
结果是:
PemObject, Type: PUBLIC KEY
PemObject, Length: 192
回答by iSamnium
Are you speaking about certificates stored in file?
您是在谈论存储在文件中的证书吗?
If you have an object like:
如果您有一个对象,例如:
X509Certificate2 certificate;
you can use following code:
您可以使用以下代码:
RSACryptoServiceProvider rsaprovider =
(RSACryptoServiceProvider)certificate.PublicKey.Key;
and then use the class RSACryptoServiceProvider (see http://msdn.microsoft.com/en-us/library/system.security.cryptography.rsacryptoserviceprovider.aspx).
然后使用 RSACryptoServiceProvider 类(参见http://msdn.microsoft.com/en-us/library/system.security.cryptography.rsacryptoserviceprovider.aspx)。
To load an X509Certificate2 use its constructor (see http://msdn.microsoft.com/en-us/library/system.security.cryptography.x509certificates.x509certificate2.aspx).
要加载 X509Certificate2,请使用其构造函数(请参阅http://msdn.microsoft.com/en-us/library/system.security.cryptography.x509certificates.x509certificate2.aspx)。
This program works fine for me:
这个程序对我来说很好用:
static void Main(string[] args)
{
try
{
X509Certificate2 certificate =
new X509Certificate2("<PFX Certificate Path", "<Certificate-Password>");
RSACryptoServiceProvider rsaprovider = (RSACryptoServiceProvider)certificate.PublicKey.Key;
}
catch(Exception e)
{
}
}
回答by Sergey
You can create an RSACryptoServiceProviderfrom a PEM file using the following class (GetRSAProviderFromPemFilemethod).
您可以RSACryptoServiceProvider使用以下类(GetRSAProviderFromPemFile方法)从 PEM 文件创建一个。
Warning:Don't just copy code from StackOverflow without verification! Especially not crypto code! This code has bugs (see comments). You may want to write and run tests before using this in production (if you really have no better option).I refuse to edit the code to fix it, as it would be just as unreliable as before without tests and an active maintainer.
警告:不要未经验证就从 StackOverflow 复制代码!尤其不是加密代码!这段代码有错误(见评论)。您可能希望在生产中使用它之前编写和运行测试(如果您真的没有更好的选择)。我拒绝编辑代码来修复它,因为如果没有测试和积极的维护者,它会像以前一样不可靠。
Source:This code seems to be taken from opensslkeyfrom this site.
Copyright (c) 2000 JavaScience Consulting, Michel Gallant.
The original package was released under a BSD-like license, so it is probably OK to use it (but you may want to double-check). There is also a NuGet packageby the same author.
资料来源:该代码似乎取自opensslkey从这个网站。版权所有 (c) 2000 JavaScience Consulting,Michel Gallant。原始软件包是在类似 BSD 的许可下发布的,因此使用它可能没问题(但您可能需要仔细检查)。同一作者还有一个NuGet 包。
Here is the copy-pasted source code originally posted to this answer:
这是最初发布到此答案的复制粘贴源代码:
RSACryptoServiceProvider provider = PemKeyUtils.GetRSAProviderFromPemFile( @"public_key.pem" );
public class PemKeyUtils
{
const String pemprivheader = "-----BEGIN RSA PRIVATE KEY-----";
const String pemprivfooter = "-----END RSA PRIVATE KEY-----";
const String pempubheader = "-----BEGIN PUBLIC KEY-----";
const String pempubfooter = "-----END PUBLIC KEY-----";
const String pemp8header = "-----BEGIN PRIVATE KEY-----";
const String pemp8footer = "-----END PRIVATE KEY-----";
const String pemp8encheader = "-----BEGIN ENCRYPTED PRIVATE KEY-----";
const String pemp8encfooter = "-----END ENCRYPTED PRIVATE KEY-----";
static bool verbose = false;
public static RSACryptoServiceProvider GetRSAProviderFromPemFile( String pemfile )
{
bool isPrivateKeyFile = true;
string pemstr = File.ReadAllText( pemfile ).Trim();
if (pemstr.StartsWith( pempubheader ) && pemstr.EndsWith( pempubfooter ))
isPrivateKeyFile = false;
byte[] pemkey;
if (isPrivateKeyFile)
pemkey = DecodeOpenSSLPrivateKey( pemstr );
else
pemkey = DecodeOpenSSLPublicKey( pemstr );
if (pemkey == null)
return null;
if (isPrivateKeyFile)
return DecodeRSAPrivateKey( pemkey );
else
return DecodeX509PublicKey( pemkey );
}
//-------- Get the binary RSA PUBLIC key --------
static byte[] DecodeOpenSSLPublicKey( String instr )
{
const String pempubheader = "-----BEGIN PUBLIC KEY-----";
const String pempubfooter = "-----END PUBLIC KEY-----";
String pemstr = instr.Trim();
byte[] binkey;
if (!pemstr.StartsWith( pempubheader ) || !pemstr.EndsWith( pempubfooter ))
return null;
StringBuilder sb = new StringBuilder( pemstr );
sb.Replace( pempubheader, "" ); //remove headers/footers, if present
sb.Replace( pempubfooter, "" );
String pubstr = sb.ToString().Trim(); //get string after removing leading/trailing whitespace
try
{
binkey = Convert.FromBase64String( pubstr );
}
catch (System.FormatException)
{ //if can't b64 decode, data is not valid
return null;
}
return binkey;
}
static RSACryptoServiceProvider DecodeX509PublicKey(byte[] x509Key)
{
// encoded OID sequence for PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1"
byte[] seqOid = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 };
// --------- Set up stream to read the asn.1 encoded SubjectPublicKeyInfo blob ------
using (var mem = new MemoryStream(x509Key))
{
using (var binr = new BinaryReader(mem)) //wrap Memory Stream with BinaryReader for easy reading
{
try
{
var twobytes = binr.ReadUInt16();
switch (twobytes)
{
case 0x8130:
binr.ReadByte(); //advance 1 byte
break;
case 0x8230:
binr.ReadInt16(); //advance 2 bytes
break;
default:
return null;
}
var seq = binr.ReadBytes(15);
if (!CompareBytearrays(seq, seqOid)) //make sure Sequence for OID is correct
return null;
twobytes = binr.ReadUInt16();
if (twobytes == 0x8103) //data read as little endian order (actual data order for Bit String is 03 81)
binr.ReadByte(); //advance 1 byte
else if (twobytes == 0x8203)
binr.ReadInt16(); //advance 2 bytes
else
return null;
var bt = binr.ReadByte();
if (bt != 0x00) //expect null byte next
return null;
twobytes = binr.ReadUInt16();
if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
binr.ReadByte(); //advance 1 byte
else if (twobytes == 0x8230)
binr.ReadInt16(); //advance 2 bytes
else
return null;
twobytes = binr.ReadUInt16();
byte lowbyte = 0x00;
byte highbyte = 0x00;
if (twobytes == 0x8102) //data read as little endian order (actual data order for Integer is 02 81)
lowbyte = binr.ReadByte(); // read next bytes which is bytes in modulus
else if (twobytes == 0x8202)
{
highbyte = binr.ReadByte(); //advance 2 bytes
lowbyte = binr.ReadByte();
}
else
return null;
byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; //reverse byte order since asn.1 key uses big endian order
int modsize = BitConverter.ToInt32(modint, 0);
byte firstbyte = binr.ReadByte();
binr.BaseStream.Seek(-1, SeekOrigin.Current);
if (firstbyte == 0x00)
{ //if first byte (highest order) of modulus is zero, don't include it
binr.ReadByte(); //skip this null byte
modsize -= 1; //reduce modulus buffer size by 1
}
byte[] modulus = binr.ReadBytes(modsize); //read the modulus bytes
if (binr.ReadByte() != 0x02) //expect an Integer for the exponent data
return null;
int expbytes = binr.ReadByte(); // should only need one byte for actual exponent data (for all useful values)
byte[] exponent = binr.ReadBytes(expbytes);
// We don't really need to print anything but if we insist to...
//showBytes("\nExponent", exponent);
//showBytes("\nModulus", modulus);
// ------- create RSACryptoServiceProvider instance and initialize with public key -----
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
RSAParameters rsaKeyInfo = new RSAParameters
{
Modulus = modulus,
Exponent = exponent
};
rsa.ImportParameters(rsaKeyInfo);
return rsa;
}
catch (Exception)
{
return null;
}
}
}
}
//------- Parses binary ans.1 RSA private key; returns RSACryptoServiceProvider ---
static RSACryptoServiceProvider DecodeRSAPrivateKey( byte[] privkey )
{
byte[] MODULUS, E, D, P, Q, DP, DQ, IQ;
// --------- Set up stream to decode the asn.1 encoded RSA private key ------
MemoryStream mem = new MemoryStream( privkey );
BinaryReader binr = new BinaryReader( mem ); //wrap Memory Stream with BinaryReader for easy reading
byte bt = 0;
ushort twobytes = 0;
int elems = 0;
try
{
twobytes = binr.ReadUInt16();
if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
binr.ReadByte(); //advance 1 byte
else if (twobytes == 0x8230)
binr.ReadInt16(); //advance 2 bytes
else
return null;
twobytes = binr.ReadUInt16();
if (twobytes != 0x0102) //version number
return null;
bt = binr.ReadByte();
if (bt != 0x00)
return null;
//------ all private key components are Integer sequences ----
elems = GetIntegerSize( binr );
MODULUS = binr.ReadBytes( elems );
elems = GetIntegerSize( binr );
E = binr.ReadBytes( elems );
elems = GetIntegerSize( binr );
D = binr.ReadBytes( elems );
elems = GetIntegerSize( binr );
P = binr.ReadBytes( elems );
elems = GetIntegerSize( binr );
Q = binr.ReadBytes( elems );
elems = GetIntegerSize( binr );
DP = binr.ReadBytes( elems );
elems = GetIntegerSize( binr );
DQ = binr.ReadBytes( elems );
elems = GetIntegerSize( binr );
IQ = binr.ReadBytes( elems );
Console.WriteLine( "showing components .." );
if (verbose)
{
showBytes( "\nModulus", MODULUS );
showBytes( "\nExponent", E );
showBytes( "\nD", D );
showBytes( "\nP", P );
showBytes( "\nQ", Q );
showBytes( "\nDP", DP );
showBytes( "\nDQ", DQ );
showBytes( "\nIQ", IQ );
}
// ------- create RSACryptoServiceProvider instance and initialize with public key -----
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSAParameters RSAparams = new RSAParameters();
RSAparams.Modulus = MODULUS;
RSAparams.Exponent = E;
RSAparams.D = D;
RSAparams.P = P;
RSAparams.Q = Q;
RSAparams.DP = DP;
RSAparams.DQ = DQ;
RSAparams.InverseQ = IQ;
RSA.ImportParameters( RSAparams );
return RSA;
}
catch (Exception)
{
return null;
}
finally { binr.Close(); }
}
private static int GetIntegerSize( BinaryReader binr )
{
byte bt = 0;
byte lowbyte = 0x00;
byte highbyte = 0x00;
int count = 0;
bt = binr.ReadByte();
if (bt != 0x02) //expect integer
return 0;
bt = binr.ReadByte();
if (bt == 0x81)
count = binr.ReadByte(); // data size in next byte
else
if (bt == 0x82)
{
highbyte = binr.ReadByte(); // data size in next 2 bytes
lowbyte = binr.ReadByte();
byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
count = BitConverter.ToInt32( modint, 0 );
}
else
{
count = bt; // we already have the data size
}
while (binr.ReadByte() == 0x00)
{ //remove high order zeros in data
count -= 1;
}
binr.BaseStream.Seek( -1, SeekOrigin.Current ); //last ReadByte wasn't a removed zero, so back up a byte
return count;
}
//----- Get the binary RSA PRIVATE key, decrypting if necessary ----
static byte[] DecodeOpenSSLPrivateKey( String instr )
{
const String pemprivheader = "-----BEGIN RSA PRIVATE KEY-----";
const String pemprivfooter = "-----END RSA PRIVATE KEY-----";
String pemstr = instr.Trim();
byte[] binkey;
if (!pemstr.StartsWith( pemprivheader ) || !pemstr.EndsWith( pemprivfooter ))
return null;
StringBuilder sb = new StringBuilder( pemstr );
sb.Replace( pemprivheader, "" ); //remove headers/footers, if present
sb.Replace( pemprivfooter, "" );
String pvkstr = sb.ToString().Trim(); //get string after removing leading/trailing whitespace
try
{ // if there are no PEM encryption info lines, this is an UNencrypted PEM private key
binkey = Convert.FromBase64String( pvkstr );
return binkey;
}
catch (System.FormatException)
{ //if can't b64 decode, it must be an encrypted private key
//Console.WriteLine("Not an unencrypted OpenSSL PEM private key");
}
StringReader str = new StringReader( pvkstr );
//-------- read PEM encryption info. lines and extract salt -----
if (!str.ReadLine().StartsWith( "Proc-Type: 4,ENCRYPTED" ))
return null;
String saltline = str.ReadLine();
if (!saltline.StartsWith( "DEK-Info: DES-EDE3-CBC," ))
return null;
String saltstr = saltline.Substring( saltline.IndexOf( "," ) + 1 ).Trim();
byte[] salt = new byte[saltstr.Length / 2];
for (int i = 0; i < salt.Length; i++)
salt[i] = Convert.ToByte( saltstr.Substring( i * 2, 2 ), 16 );
if (!(str.ReadLine() == ""))
return null;
//------ remaining b64 data is encrypted RSA key ----
String encryptedstr = str.ReadToEnd();
try
{ //should have b64 encrypted RSA key now
binkey = Convert.FromBase64String( encryptedstr );
}
catch (System.FormatException)
{ // bad b64 data.
return null;
}
//------ Get the 3DES 24 byte key using PDK used by OpenSSL ----
SecureString despswd = GetSecPswd( "Enter password to derive 3DES key==>" );
//Console.Write("\nEnter password to derive 3DES key: ");
//String pswd = Console.ReadLine();
byte[] deskey = GetOpenSSL3deskey( salt, despswd, 1, 2 ); // count=1 (for OpenSSL implementation); 2 iterations to get at least 24 bytes
if (deskey == null)
return null;
//showBytes("3DES key", deskey) ;
//------ Decrypt the encrypted 3des-encrypted RSA private key ------
byte[] rsakey = DecryptKey( binkey, deskey, salt ); //OpenSSL uses salt value in PEM header also as 3DES IV
if (rsakey != null)
return rsakey; //we have a decrypted RSA private key
else
{
Console.WriteLine( "Failed to decrypt RSA private key; probably wrong password." );
return null;
}
}
// ----- Decrypt the 3DES encrypted RSA private key ----------
static byte[] DecryptKey( byte[] cipherData, byte[] desKey, byte[] IV )
{
MemoryStream memst = new MemoryStream();
TripleDES alg = TripleDES.Create();
alg.Key = desKey;
alg.IV = IV;
try
{
CryptoStream cs = new CryptoStream( memst, alg.CreateDecryptor(), CryptoStreamMode.Write );
cs.Write( cipherData, 0, cipherData.Length );
cs.Close();
}
catch (Exception exc)
{
Console.WriteLine( exc.Message );
return null;
}
byte[] decryptedData = memst.ToArray();
return decryptedData;
}
//----- OpenSSL PBKD uses only one hash cycle (count); miter is number of iterations required to build sufficient bytes ---
static byte[] GetOpenSSL3deskey( byte[] salt, SecureString secpswd, int count, int miter )
{
IntPtr unmanagedPswd = IntPtr.Zero;
int HASHLENGTH = 16; //MD5 bytes
byte[] keymaterial = new byte[HASHLENGTH * miter]; //to store contatenated Mi hashed results
byte[] psbytes = new byte[secpswd.Length];
unmanagedPswd = Marshal.SecureStringToGlobalAllocAnsi( secpswd );
Marshal.Copy( unmanagedPswd, psbytes, 0, psbytes.Length );
Marshal.ZeroFreeGlobalAllocAnsi( unmanagedPswd );
//UTF8Encoding utf8 = new UTF8Encoding();
//byte[] psbytes = utf8.GetBytes(pswd);
// --- contatenate salt and pswd bytes into fixed data array ---
byte[] data00 = new byte[psbytes.Length + salt.Length];
Array.Copy( psbytes, data00, psbytes.Length ); //copy the pswd bytes
Array.Copy( salt, 0, data00, psbytes.Length, salt.Length ); //concatenate the salt bytes
// ---- do multi-hashing and contatenate results D1, D2 ... into keymaterial bytes ----
MD5 md5 = new MD5CryptoServiceProvider();
byte[] result = null;
byte[] hashtarget = new byte[HASHLENGTH + data00.Length]; //fixed length initial hashtarget
for (int j = 0; j < miter; j++)
{
// ---- Now hash consecutively for count times ------
if (j == 0)
result = data00; //initialize
else
{
Array.Copy( result, hashtarget, result.Length );
Array.Copy( data00, 0, hashtarget, result.Length, data00.Length );
result = hashtarget;
//Console.WriteLine("Updated new initial hash target:") ;
//showBytes(result) ;
}
for (int i = 0; i < count; i++)
result = md5.ComputeHash( result );
Array.Copy( result, 0, keymaterial, j * HASHLENGTH, result.Length ); //contatenate to keymaterial
}
//showBytes("Final key material", keymaterial);
byte[] deskey = new byte[24];
Array.Copy( keymaterial, deskey, deskey.Length );
Array.Clear( psbytes, 0, psbytes.Length );
Array.Clear( data00, 0, data00.Length );
Array.Clear( result, 0, result.Length );
Array.Clear( hashtarget, 0, hashtarget.Length );
Array.Clear( keymaterial, 0, keymaterial.Length );
return deskey;
}
static SecureString GetSecPswd( String prompt )
{
SecureString password = new SecureString();
Console.ForegroundColor = ConsoleColor.Gray;
Console.Write( prompt );
Console.ForegroundColor = ConsoleColor.Magenta;
while (true)
{
ConsoleKeyInfo cki = Console.ReadKey( true );
if (cki.Key == ConsoleKey.Enter)
{
Console.ForegroundColor = ConsoleColor.Gray;
Console.WriteLine();
return password;
}
else if (cki.Key == ConsoleKey.Backspace)
{
// remove the last asterisk from the screen...
if (password.Length > 0)
{
Console.SetCursorPosition( Console.CursorLeft - 1, Console.CursorTop );
Console.Write( " " );
Console.SetCursorPosition( Console.CursorLeft - 1, Console.CursorTop );
password.RemoveAt( password.Length - 1 );
}
}
else if (cki.Key == ConsoleKey.Escape)
{
Console.ForegroundColor = ConsoleColor.Gray;
Console.WriteLine();
return password;
}
else if (Char.IsLetterOrDigit( cki.KeyChar ) || Char.IsSymbol( cki.KeyChar ))
{
if (password.Length < 20)
{
password.AppendChar( cki.KeyChar );
Console.Write( "*" );
}
else
{
Console.Beep();
}
}
else
{
Console.Beep();
}
}
}
static bool CompareBytearrays( byte[] a, byte[] b )
{
if (a.Length != b.Length)
return false;
int i = 0;
foreach (byte c in a)
{
if (c != b[i])
return false;
i++;
}
return true;
}
static void showBytes( String info, byte[] data )
{
Console.WriteLine( "{0} [{1} bytes]", info, data.Length );
for (int i = 1; i <= data.Length; i++)
{
Console.Write( "{0:X2} ", data[i - 1] );
if (i % 16 == 0)
Console.WriteLine();
}
Console.WriteLine( "\n\n" );
}
}

