2010-08-17 13 views
12

C# ile yazılmış bir şifreleme/şifre çözme algoritması var - PHP'de aynı şifrelemeyi üretebilmem gerekiyor, böylece şifrelenmiş metni C# tarafında şifresi çözülecek şekilde HTTP üzerinden gönderebiliyorum . Şifreleme için C# kodu burada. Rewrite Rijndael 256 C# PHP'de Şifreleme Kodu

this.m_plainText = string.Empty; 
this.m_passPhrase = "passpharse"; 
this.m_saltValue = "saltvalue"; 
this.m_hashAlgorithm = "SHA1"; 
this.m_passwordIterations = 2; 
this.m_initVector = "1a2b3c4d5e6f7g8h"; 
this.m_keySize = 256; 

public string Encrypt() 
{ 
    string plainText = this.m_plainText; 
    string passPhrase = this.m_passPhrase; 
    string saltValue = this.m_saltValue; 
    string hashAlgorithm = this.m_hashAlgorithm; 
    int passwordIterations = this.m_passwordIterations; 
    string initVector = this.m_initVector; 
    int keySize = this.m_keySize; 

    // Convert strings into byte arrays. 
    // Let us assume that strings only contain ASCII codes. 
    // If strings include Unicode characters, use Unicode, UTF7, or UTF8 
    // encoding. 
    byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector); 
    byte[] saltValueBytes = Encoding.ASCII.GetBytes(saltValue); 

    // Convert our plaintext into a byte array. 
    // Let us assume that plaintext contains UTF8-encoded characters. 
    byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText); 

    // First, we must create a password, from which the key will be derived. 
    // This password will be generated from the specified passphrase and 
    // salt value. The password will be created using the specified hash 
    // algorithm. Password creation can be done in several iterations. 
    PasswordDeriveBytes password = new PasswordDeriveBytes(
                passPhrase, 
                saltValueBytes, 
                hashAlgorithm, 
                passwordIterations); 

    // Use the password to generate pseudo-random bytes for the encryption 
    // key. Specify the size of the key in bytes (instead of bits). 
    byte[] keyBytes = password.GetBytes(keySize/8); 

    // Create uninitialized Rijndael encryption object. 
    RijndaelManaged symmetricKey = new RijndaelManaged(); 

    // It is reasonable to set encryption mode to Cipher Block Chaining 
    // (CBC). Use default options for other symmetric key parameters. 
    symmetricKey.Mode = CipherMode.CBC; 

    // Generate encryptor from the existing key bytes and initialization 
    // vector. Key size will be defined based on the number of the key 
    // bytes. 
    ICryptoTransform encryptor = symmetricKey.CreateEncryptor(
                keyBytes, 
                initVectorBytes); 

    // Define memory stream which will be used to hold encrypted data. 
    MemoryStream memoryStream = new MemoryStream(); 

    // Define cryptographic stream (always use Write mode for encryption). 
    CryptoStream cryptoStream = new CryptoStream(memoryStream, 
               encryptor, 
               CryptoStreamMode.Write); 
    // Start encrypting. 
    cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length); 

    // Finish encrypting. 
    cryptoStream.FlushFinalBlock(); 

    // Convert our encrypted data from a memory stream into a byte array. 
    byte[] cipherTextBytes = memoryStream.ToArray(); 

    // Close both streams. 
    memoryStream.Close(); 
    cryptoStream.Close(); 

    // Convert encrypted data into a base64-encoded string. 
    string cipherText = Convert.ToBase64String(cipherTextBytes); 

    // Return encrypted string. 
    return cipherText; 
} 

ben yardımcı olabilecek bazı benzer PHP kodu var. Tam olarak gerektiği gibi yapmaz, ama başlamak için iyi bir yer olabilir diye düşünüyorum.

<?php 

/* 
* DEFINE CONSTANTS 
*/ 
$HashPassPhrase = "passpharse"; 
$HashSalt = "saltvalue"; 
$HashAlgorithm = "SHA1"; 
$HashIterations = "2"; 
$InitVector = "1a2b3c4d5e6f7g8h";  // Must be 16 bytes 
$keySize = "256"; 

class Cipher { 
    private $securekey, $iv; 
    function __construct($textkey) { 
     $this->securekey = hash($HashAlgorithm,$textkey,TRUE); 
     $this->iv = $InitVector; 
    } 
    function encrypt($input) { 
     return base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $this->securekey, $input, MCRYPT_MODE_CBC, $this->iv)); 
    } 
    function decrypt($input) { 
     return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $this->securekey, base64_decode($input), MCRYPT_MODE_CBC, $this->iv)); 
    } 
} 

$cipher = new Cipher($HashPassPhrase); 

$encryptedtext = $cipher->encrypt("Text To Encrypt"); 
echo "->encrypt = $encryptedtext<br />"; 

$decryptedtext = $cipher->decrypt($encryptedtext); 
echo "->decrypt = $decryptedtext<br />"; 

var_dump($cipher); 

?> Sen C# koduyla aynı yolu PasswordDeriveBytes yapar geçişdeyiminden anahtarı türetmek gerekir

cevap

17

. Bu RFC2898 göre, PBKDF1 anahtar türetme yapmak belgelenmiştir:

Bu sınıf temel malzemesi olarak kullanım için uygun olan bayt türetmek için PKCS # 5 v2.0 standardında tanımlanan PBKDF1 algoritmasının bir uzantısı kullanan şifresinden. Standart IETF RRC belgelenmiştir 2898.

orada orada PBKDF1 uygulamak PHP kütüphaneleri vardır, ama o RFC ont dayalı sıfırdan yazmak için gerçekten basittir:

PBKDF1 (P, S, c, dkLen)

Seçenek: hash
temel hızlı arama fonksiyonu

giriş: P
şifre, bir sekizli dizi S tuzu, bir sekiz sekizli dizi C yineleme sayısı, pozitif bir tamsayıdır dkLen SHA için MD2 veya MD5 ve 20 için elde edilen anahtar, pozitif bir tamsayı arasında sekizlilerde uzunluğu, en fazla 16 amaçlanan -1

Çıkış:

1. If dkLen > 16 for MD2 and MD5, or dkLen > 20 for SHA-1, output 
    "derived key too long" and stop. 

    2. Apply the underlying hash function Hash for c iterations to the 
    concatenation of the password P and the salt S, then extract 
    the first dkLen octets to produce a derived key DK: 

       T_1 = Hash (P || S) , 
       T_2 = Hash (T_1) , 
       ... 
       T_c = Hash (T_{c-1}) , 
       DK = Tc<0..dkLen-1> 

    3. Output the derived key DK. 
: DK anahtar, dkLen sekizli dize

Adım türetilmiş

o her adımda değerleri gösterir implementaiton genellikle bir örnek aramak,

Bu durumda kendinizi bulmak güncellendi. Örneğin http://www.di-mgt.com.au/cryptoKDFs.html#examplespbkdf az bir:

Şimdi
function PBKDF1($pass,$salt,$count,$dklen) { 
    $t = $pass.$salt; 
    //echo 'S||P: '.bin2hex($t).'<br/>'; 
    $t = sha1($t, true); 
    //echo 'T1:' . bin2hex($t) . '<br/>'; 
    for($i=2; $i <= $count; $i++) { 
     $t = sha1($t, true); 
     //echo 'T'.$i.':' . bin2hex($t) . '<br/>'; 
    } 
    $t = substr($t,0,$dklen); 
    return $t;  
} 

Eğer yollardan Hataları görebilirsiniz: Eğer tüm önemli raw=true belirtmedi

Password = "password" 
     = (0x)70617373776F7264 
Salt  = (0x)78578E5A5D63CB06 
Count = 1000 
kLen  = 16 
Key  = PBKDF1(Password, Salt, Count, kLen) 
     = (0x)DC19847E05C64D2FAF10EBFB4A3D2A20 

P || S = 70617373776F726478578E5A5D63CB06 
T_1=  D1F94C4D447039B034494400F2E7DF9DCB67C308 
T_2=  2BB479C1D369EA74BB976BBA2629744E8259C6F5 
... 
T_999= 6663F4611D61571068B5DA168974C6FF2C9775AC 
T_1000= DC19847E05C64D2FAF10EBFB4A3D2A20B4E35EFE 
Key=  DC19847E05C64D2FAF10EBFB4A3D2A20 

Yani şimdi bunu yapar bir PHP fonksiyonu yazmanıza olanak tanır sha1 parametresi.

$HashPassPhrase = pack("H*","70617373776F7264"); 
$HashSalt = pack("H*","78578E5A5D63CB06"); 
$HashIterations = 1000; 
$devkeylength = 16; 
$devkey = PBKDF1($HashPassPhrase,$HashSalt,$HashIterations,$devkeylength); 
echo 'Key:' . bin2hex(substr($devkey, 0, 8)) . '<br/>'; 
echo 'IV:' . bin2hex(substr($devkey, 8, 8)) .'<br/>'; 
echo 'Expected: DC19847E05C64D2FAF10EBFB4A3D2A20<br/>'; 

bu çıkış tam olarak beklenen sonuç:

Key:dc19847e05c64d2f 
IV:af10ebfb4a3d2a20 
Expected: DC19847E05C64D2FAF10EBFB4A3D2A20 

Sonra, C# işlevi aynı işi yapar doğrulayabilmeniz:

  byte[] password = Encoding.ASCII.GetBytes("password"); 
      byte[] salt = new byte[] { 0x78, 0x57, 0x8e, 0x5a, 0x5d, 0x63, 0xcb, 0x06}; 

      PasswordDeriveBytes pdb = new PasswordDeriveBytes(
       password, salt, "SHA1", 1000); 

      byte[] key = pdb.GetBytes(8); 
      byte[] iv = pdb.GetBytes(8); 

      Console.Out.Write("Key: "); 
      foreach (byte b in key) 
      { 
       Console.Out.Write("{0:x} ", b); 
      } 
      Console.Out.WriteLine(); 

      Console.Out.Write("IV: "); 
      foreach (byte b in iv) 
      { 
       Console.Out.Write("{0:x} ", b); 
      } 
      Console.Out.WriteLine(); 

bu bizim fonksiyon çıkışı ne görelim aynı çıktıyı üretir:

QED

ikramiye açıklama

Eğer ne yaptığınızı tam bilmiyorsanız kripto Lütfen bunu yapma. PHP implementaiton'unu doğru bir şekilde aldıktan sonra bile, yayınlanan C# kodunuzun bazı ciddi sorunları vardır. Onaltılık dizileri, hex dumps'ları temsil eden bir karıştırma ile karıştırıyorsunuz, parola ve tuzdan türetmek yerine sabit kodlanmış bir IV kullanıyorsunuz, sadece genel olarak yanlıştır. Lütfen SSL veya S-MIME gibi bir raf şifreleme şeması kullanın ve kendiniz icat etmeyin. yanlış'u alacaksınız.

+0

Cevabınız için teşekkürler, burada ne söylemeye çalıştığınızı anlıyorum, ama aslında bunu kullanmaya koymak için yeterli değil. Tamamen kodlanmış bir örnek var mı? –

+0

Bu, PBKDF1 $ HashPassPhrase = "passpharse" için geldiğim PHP işlevidir; $ HashSalt = "saltvalue"; $ HashIterations = 2; $ devkeylength = 32; $ devkey = PBKDF1 ($ HashPassPhrase, $ Hashsalt, $ HashIterations, $ devkeylength); \t fonksiyonu PBKDF1 ($ geçiren, $ tuzu, $ sayısı, $ dklen) \t { \t \t $ t = SHA-1 ($ geçmektedir.$ Tuzu); için \t \t ($ i = 1; $ ı sayısı $ <; $ i ++) \t \t { \t \t \t $ t = SHA-1 ($ t); \t \t} \t \t $ t = substr ($ t, 0, $ dklen-1); \t \t dönüş $ t; Ancak doğru sonuçları elde ettiğimi sanmıyorum. –

+0

PBKDF1 işleviniz $ dklen-1 kullanıyor ... neden ??? –

4

Ana sorununun, C# tarafındaki PasswordDeriveBytes() adımının yerine hash() PHP kullanmanız olduğu anlaşılıyor. Bu iki yöntem eşdeğer değildir. İkincisi, hash() sadece bir karma iken PBKDF1 şifre türetme algoritmasını uygular. PEAR might have a PBKDF1 implementation gibi görünüyor, ancak aksi takdirde kendiniz yazmanız gerekebilir.

Ayrıca, henüz yazmamışsanız, metin kodlamanızın her iki tarafta da tutarlı olduğundan emin olmanız gerekir.

Son olarak, cryptography is harder than it looks için yaptığınız şeyi yapmamanızı düşünmelisiniz. HTTP kullandığınızdan, kendi yazdığınız yerine SSL protokolünü kullanabilirsiniz. Bu, daha iyi bir güvenlik sağlar ve artan IV'leri eşitleme ve niçin tutma gibi düşük seviyeli ayrıntılarda daha az güçlük çeker.

+0

Bu cevap için teşekkürler. Evet, asıl sorunun PasswordDeriveBytes() ile olduğunu anladım. SSL ile HTTPS üzerinden net bir metin şifresi iletmenin, onu bir tarafa kodlamaktan ve diğerini deşifre etmekten daha güvenli olduğunu mu söylüyorsunuz? –

+0

@Derek Armstrong: evet! SSL, planınız olduğu gibi düz metin şifreleri gibi şeyleri korumak için tasarlanmıştır. Temel fark, SSL'nin son 15 yıldır gezegendeki en iyi şifreleme uzmanları tarafından gözden geçirilmesi ve revize edilmesidir. Özellikle HTTP üzerinden konuştuğunuz için kullanmanın çok daha kolay olacağını düşünüyorum. – ladenedge

+1

Ayrıca, Remus cevabına bir not ekledim, ŞifreDeriveBytes, tutarsız, belgesiz [tescilli bir şema] (http://bouncy-castle.1462172.n4.nabble.com/NET-PasswordDeriveBytes-td1462616.html) ondan istenen maksimum PBKDF1 (20 byte) çıkışı. –

0

PHP'de OpenSSL rutinlerini kontrol edin, yapmanız gerekenleri halledebilmeli.