2012-02-10 36 views
7

Tôi cố gắng để sử dụng Tháng Mười Hai 3.0 thư viện (Delphi Encryption Compedium Part I) để mã hóa dữ liệu trong Delphi 7 và gửi nó đến một kịch bản PHP qua POST, nơi tôi đang giải mã nó với Mcrypt (RIJNDAEL_256, chế độ ECB).Delphi Tháng Mười Hai thư viện (Rijndael) mã hóa

Delphi phần:

uses Windows, DECUtil, Cipher, Cipher1; 

function EncryptMsgData(MsgData, Key: string): string; 
var RCipher: TCipher_Rijndael; 
begin 
    RCipher:= TCipher_Rijndael.Create(KeyStr, nil); 
    RCipher.Mode:= cmECB; 
    Result:= RCipher.CodeString(MsgData, paEncode, fmtMIME64); 
    RCipher.Free; 
end; 

PHP phần:

function decryptMsgContent($msgContent, $sKey) { 
    return mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $sKey, base64_decode($msgContent), MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND)); 
} 

Vấn đề là giải mã từ PHP không hoạt động và đầu ra là vô nghia, khác nhau từ các dữ liệu thực tế.

Tất nhiên, Delphi Key và PHP $Key có cùng chuỗi 24 ký tự.

Bây giờ tôi biết DEC 3.0 cũ và lỗi thời, và tôi không phải là chuyên gia về mã hóa và không thể biết liệu việc thực hiện có thực sự là Rijndael 256. Có thể ai đó có thể cho tôi biết cách thực hiện này khác với mcrypt của PHP RIJNDAEL_256. Có lẽ các keyize là khác nhau, hoặc kích thước khối, nhưng không thể nói điều này từ mã. Dưới đây là một đoạn trích từ Cipher1.pas:

const 
{ don’t change this } 
    Rijndael_Blocks = 4; 
    Rijndael_Rounds = 14; 

class procedure TCipher_Rijndael.GetContext(var ABufSize, AKeySize, AUserSize: Integer); 
begin 
    ABufSize := Rijndael_Blocks * 4; 
    AKeySize := 32; 
    AUserSize := (Rijndael_Rounds + 1) * Rijndael_Blocks * SizeOf(Integer) * 2; 
end; 

Side câu hỏi:

tôi biết chế độ ECB không được khuyến khích và tôi sẽ sử dụng CBC ngay sau khi tôi được làm việc ECB. Câu hỏi đặt ra là, tôi có phải truyền IV đã tạo trong Delphi sang tập lệnh PHP không? Hoặc biết chìa khóa là đủ, như cho ECB?

+0

Đây có thể là một câu hỏi rất ngu ngốc. Nhưng bằng cách sử dụng delphi là bạn có thể giải mã dữ liệu được mã hóa của bạn? Ồ, và câu trả lời cho câu hỏi này có hữu ích không: http://stackoverflow.com/q/8313992/41338 – RobS

+0

Bạn gọi mcrypt_create_iv(). IV bạn đã sử dụng ở Delphi là gì? –

+0

@ldsandon: tài khoản đang sử dụng chế độ ECB. Không có IV. –

Trả lời

6

Bạn đang gọi TCipher.Create (const Mật khẩu: Chuỗi; Bảo vệ: Bảo vệ TP); constructor, sẽ tính toán một hash của mật khẩu trước khi chuyển nó sang phương thức Init, thực hiện lịch biểu khóa chuẩn của thuật toán được triển khai. Để ghi đè dẫn xuất khóa này, hãy sử dụng:

function EncryptMsgData(MsgData, Key: string): string; 
var RCipher: TCipher_Rijndael; 
begin 
    RCipher:= TCipher_Rijndael.Create('', nil); 
    RCipher.Init(Pointer(Key)^,Length(Key),nil); 
    RCipher.Mode:= cmECB; 
    Result:= RCipher.CodeString(MsgData, paEncode, fmtMIME64); 
    RCipher.Free; 

kết thúc;

+0

Cảm ơn bạn, điều này đã giúp tôi đi đúng hướng.Tôi đang thực sự sử dụng chuyển đổi mật khẩu của riêng tôi (bỏ qua từ mã được đăng) mà chỉ mất 24 ký tự đầu tiên của sha1 của PasswordSecret + Dấu thời gian (đi cùng trong dữ liệu POST). Tôi đã có nghĩa vụ phải làm điều này khi tôi nhận được một lỗi trong PHP rằng mcrypt chấp nhận một khóa tối đa 24 ký tự. – talereader

+0

Một phần khác của vấn đề là việc triển khai DEC thực sự là ** 128 bit ** Rijndael. Tìm thấy rằng bằng cách thay đổi chức năng giải mã PHP để sử dụng RIJNDAEL_128. Bây giờ đầu ra của hàm giải mã PHP có dữ liệu gốc + một số vô nghĩa ở cuối. Từ những gì tôi đã đọc trước đây trong các bài viết khác trên stackoverflow, có một vấn đề padding quanh đây một nơi nào đó mà tôi phải tìm ra. – talereader

+0

Ngoài ra, bằng cách xem nguồn DEC, có thể thực hiện một số sửa đổi để biến đổi nó thành biến thể của kích thước khối 128 bit và kích thước khóa 256 (AES 256), vì vậy nó sẽ kết hợp với mcrypt w/RIJNDAEL_256 ? – talereader

2

OK, vì vậy để tổng hợp này lên, có 3 vấn đề với mã của tôi:

  1. Do thiếu hiểu biết của tôi về Mcrypt và thuật toán mã hóa nói chung, MCRYPT_RIJNDAEL_256 đề cập đến 128 bit khối và doesn' t tham khảo các phím. Lựa chọn đúng của tôi phải là MCRYPT_RIJNDAEL_128, là tiêu chuẩn AES và cũng được hỗ trợ bởi DEC 3.0.

  2. DEC có khóa dẫn xuất mặc định của riêng nó, vì vậy tôi cần phải bỏ qua nó vì vậy tôi cũng sẽ không phải triển khai nó trong PHP nữa. Trong thực tế, tôi đang sử dụng thuật toán dẫn xuất khóa của chính mình mà dễ dàng tái tạo trong PHP (32 ký tự đầu tiên của sha1 (khóa)).

  3. Tháng mười hai không đệm văn bản gốc vào nhiều kích thước khối của mật mã, như mcrypt mong đợi, vì vậy tôi phải thực hiện thủ công.

Cung cấp mã làm việc dưới đây:

Delphi:

uses Windows, DECUtil, Cipher, Cipher1, CryptoAPI; 

function EncryptMsgData(MsgData, Key: string): string; 
var RCipher: TCipher_Rijndael; 
    KeyStr: string; 
begin 
    Result:= ''; 
    try 
    // key derivation; just making sure to feed the cipher a 24 chars key 
    HashStr(HASH_SHA1, Key, KeyStr); 
    KeyStr:= Copy(KeyStr, 1, 24); 
    RCipher:= TCipher_Rijndael.Create('', nil); 
    RCipher.Init(Pointer(KeyStr)^, Length(KeyStr), nil); 
    RCipher.Mode:= cmECB; 
    Result:= RCipher.CodeString(MsgData + StringOfChar(#0,16-(Length(MsgData) mod 16)), paEncode, fmtMIME64); 
    RCipher.Free; 
    except 
    end; 
end; 

PHP:

function decryptMsgContent($msgContent, $sKey) { 
    $sKey = substr(sha1(sKey), 0, 24); 
    return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $sKey, base64_decode($msgContent), MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB), MCRYPT_RAND))); 
} 
+0

Uhm. SHA-1 sẽ chỉ xuất ra 20 byte. Giải pháp dễ nhất có lẽ là giảm kích thước khóa xuống 128 bit/16 byte. Một cách khác là sử dụng SHA-256 và chuyển từ DEC 3.0 sang thư viện khác hỗ trợ SHA-256, chẳng hạn như OpenStrSecII hoặc StreamSec Tools hoặc DCP Crypt. Cách thứ ba là sử dụng SHA-1 với khóa kéo dài, nhưng điều đó có thể dễ dàng phức tạp khi thực hiện trong PHP và DEC 3.0 không có bất kỳ chức năng PKCS # 5 v2.0 nào được triển khai. –

+0

Tôi có đúng không khi nói rằng mã ở trên thực sự sử dụng AES-128 và không 192, mặc dù khóa có độ dài 24 ký tự? Tôi có nên giảm độ dài khóa xuống 16? Bởi vì tôi sẽ gắn bó với AES-128 cho thời điểm này, Đó là một dự án nhỏ. Ngoài ra, tôi chuyển sang CBC, cho phép DEC tạo IV trong nội bộ và tôi chỉ chuyển IV base64encoded cùng với dữ liệu. – talereader

+0

Nó sử dụng AES-192, nhưng có tối đa 160 bit entropy trong khóa (được giới hạn bởi độ dài đầu ra của SHA-1) và 4 byte cuối cùng của khóa (# 21 đến # 24) là không xác định (mặc dù có lẽ là 0 kể từ khi bạn làm việc đó). –

0

Một khóa 256 bit tôi tìm thấy là 32 charachters, hoặc 32 byte. Không 24. Đây có thể là vấn đề.

[EDIT]

Tôi đã kết hợp ý tưởng của mọi người (ansistring, v.v) thành một ý tưởng duy nhất với bản sửa lỗi.

Ngoài ra, bạn đang sử dụng codestring (- nó phải được Encodestring (

tôi dán làm việc Encrypt và Decrypt nguồn dưới đây:


function EncryptMsgData(MsgData, Key: AnsiString): AnsiString; 
var RCipher: TCipher_Rijndael; 
begin 
    RCipher:= TCipher_Rijndael.Create('', nil); 
    RCipher.Init(Pointer(Key)^,Length(Key),nil); 
    RCipher.Mode:= cmCBC; 
    Result:= RCipher.EncodeString(MsgData); 
    RCipher.Free; 
end; 

function DecryptMsgData(MsgData, Key: AnsiString): AnsiString; 
var RCipher: TCipher_Rijndael; 
begin 
    RCipher:= TCipher_Rijndael.Create('',nil); 
    RCipher.Init(Pointer(Key)^,Length(Key),nil); 
    RCipher.Mode:= cmCBC; 
    Result:= RCipher.DecodeString(MsgData); 
    RCipher.Free; 
end; 

Sử dụng điều đó với một charachter 32 và bạn nhận được mã hóa và giải mã thích hợp.

Để lưu trữ và sử dụng mã hóa dữ liệu ted dưới dạng chuỗi bạn có thể muốn sử dụng Base64Encode (

Nhưng đừng quên Base64Decode trước khi giải mã.

Đây là kỹ thuật tương tự cần thiết cho Blowfish. Đôi khi các charachters thực sự giống như một backspace, và thực hiện các chức năng hơn là hiển thị trên màn hình. Base64Encode về cơ bản chuyển đổi các charachters thành một cái gì đó bạn có thể hiển thị trong văn bản.

Trước khi chuyển dữ liệu được mã hóa qua internet hoặc sang một ứng dụng khác bằng cùng một ngôn ngữ khác, bạn PHẢI mã hóa và giải mã base64 để không bị mất dữ liệu. Đừng quên nó trong PHP quá!