2012-07-16 92 views
18

我需要從文件中加載以下RSA公共密鑰以用於RSACryptoServiceProvider類。我怎樣才能做到這一點?如何從C#中的文件加載RSA公共密鑰

-----BEGIN PUBLIC KEY----- 
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/syEKqEkMtQL0+d 
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX+izR 
KbGMRtur2TYklnyVkjeeHfAggo8vWQmWesnOG55vQYHbOOFoJbk0EkwEr5R/PbKm 
byXPPN8zwnS5/XXXXXXXXXXXX 
-----END PUBLIC KEY----- 

該代碼可以使用我的酒館鍵:http://www.jensign.com/opensslkey/

這裏是我使用

 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(); } 

     } 

只需撥打您要的文字「RSA」方法的代碼加密,你就完成了。

+0

通過使用'IO.StreamReader'? – Mr47 2012-07-16 14:57:32

+1

可能的重複? http://stackoverflow.com/questions/243646/how-to-read-a-pem-rsa-private-key-from-net – Phil 2012-07-16 15:01:13

+0

我試圖使用Convert.FromBase64String將字符串轉換爲字節,然後將字節傳遞給RSACryptoServiceProvider構造函數,但它會引發異常。與X509Certificate.CreateFromFile – 2012-07-16 15:03:07

回答

1

如果你談論的是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()); 

在 「2012/7/16 15點04分58秒Z」 做@hkproj的評論後編輯:

環顧這裏我發現「Reading PEM RSA Public Key Only using Bouncy Castle」。 我猜你想要的東西是這樣的:

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); 
} 

然而,隨着你的文件,我得到一個錯誤:System.IO.IOException : base64 data appears to be truncated

因此改變你的文件是這樣的:

-----BEGIN PUBLIC KEY----- 
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/syEKqEkMtQL0+d 
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX+izR 
KbGMRtur2TYklnyVkjeeHfAggo8vWQmWesnOG55vQYHbOOFoJbk0EkwEr5R/PbKm 
byXPPN8zwnS5/XXXXXXXXXXXXZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ 
-----END PUBLIC KEY----- 

結果是:

PemObject, Type: PUBLIC KEY 
PemObject, Length: 192 
+1

已經嘗試過。沒有工作。用此消息拋出'CryptographicException':無法找到請求的對象。 – 2012-07-16 15:04:58

3

你談到存儲在文件證書嗎?

如果你有一個對象,如:

X509Certificate2 certificate; 

你可以用下面的代碼:

RSACryptoServiceProvider rsaprovider = 
        (RSACryptoServiceProvider)certificate.PublicKey.Key; 

,然後使用類的RSACryptoServiceProvider(見http://msdn.microsoft.com/en-us/library/system.security.cryptography.rsacryptoserviceprovider.aspx)。

加載X509Certificate2使用其構造函數(請參閱http://msdn.microsoft.com/en-us/library/system.security.cryptography.x509certificates.x509certificate2.aspx)。

這個節目對我來說工作得很好:

static void Main(string[] args) 
    { 
     try 
     { 
      X509Certificate2 certificate = 
       new X509Certificate2("<PFX Certificate Path", "<Certificate-Password>"); 
      RSACryptoServiceProvider rsaprovider = (RSACryptoServiceProvider)certificate.PublicKey.Key; 
     } 
     catch(Exception e) 
     { 

     } 
    } 
+0

我已經試過,沒有成功。 – 2012-07-16 15:09:17

+0

你有什麼問題? – iSamnium 2012-07-16 15:26:06

+0

用此消息拋出'CryptographicException':無法找到請求的對象 – 2012-07-16 15:47:07

24

您可以使用下面的類(GetRSAProviderFromPemFile法)PEM文件得到的RSACryptoServiceProvider。 版權所有(C)2000 JavaScience諮詢,米歇爾浩

用法:

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"); 
    } 

} 
+1

我很抱歉,OP沒有將您的答案標記爲已接受。這是一個非常好的。 – 2017-04-27 06:06:13

+0

偉大的代碼。完美工作。 – 2017-07-26 16:07:45