杰瑞科技汇

AES在Java与Delphi中的实现差异是什么?

Of course. Comparing AES implementation in Java and Delphi is a great topic, as it highlights different approaches to cryptography in modern programming languages. Here’s a detailed breakdown covering the concepts, code examples, and key differences.

AES在Java与Delphi中的实现差异是什么?-图1
(图片来源网络,侵删)

The Core Concept: AES is an Algorithm, Not a Class

First, a crucial point: AES (Advanced Encryption Standard) is a symmetric block cipher algorithm. It's a set of mathematical rules for encrypting and decrypting data.

When you use a library like Java's javax.crypto or Delphi's System.TIdEncryption, you are not using the "AES class" directly. Instead, you are using a class that implements the AES algorithm. This class is usually part of a higher-level construct, like a Cipher (Java) or a Stream (Delphi).

The process always involves:

  1. Key: A secret key used for both encryption and decryption.
  2. Mode of Operation: How the blocks of data are chained together (e.g., CBC, GCM, CTR). This is critical for security.
  3. Padding: How data that isn't a multiple of the block size (128 bits) is handled (e.g., PKCS#7, NoPadding).
  4. Initialization Vector (IV): A random value used in certain modes (like CBC) to ensure encrypting the same plaintext multiple times produces different ciphertexts. The IV is not secret and must be transmitted with the ciphertext.

Java Implementation (javax.crypto)

Java has a robust, built-in cryptography API. The main classes you'll interact with are:

AES在Java与Delphi中的实现差异是什么?-图2
(图片来源网络,侵删)
  • javax.crypto.SecretKey: Represents the secret key.
  • javax.crypto.Cipher: The workhorse class that performs the actual encryption and decryption.
  • javax.crypto.KeyGenerator: Used to generate a new random key.
  • javax.crypto.spec.SecretKeySpec: Allows you to create a key from a raw byte array.
  • javax.crypto.spec.IvParameterSpec: Specifies the Initialization Vector.

Example: AES-256 in CBC Mode with PKCS#5 Padding

This is a common and secure mode for encrypting data that is not a stream.

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Base64;
public class AesExample {
    public static void main(String[] args) throws Exception {
        String plainText = "This is a secret message.";
        String keyString = "ThisIsASecretKey123456"; // Must be 16, 24, or 32 bytes for AES-128, 192, 256
        // 1. Generate a random IV (16 bytes for AES)
        byte[] iv = new byte[16];
        new SecureRandom().nextBytes(iv);
        IvParameterSpec ivSpec = new IvParameterSpec(iv);
        // 2. Create the key from a string
        byte[] keyBytes = keyString.getBytes(StandardCharsets.UTF_8);
        SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES");
        // 3. Setup the Cipher for encryption
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
        // 4. Encrypt the data
        byte[] encryptedBytes = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
        // 5. Combine IV and ciphertext for storage/transmission
        // The IV is not secret, so it's stored with the ciphertext.
        byte[] combined = new byte[iv.length + encryptedBytes.length];
        System.arraycopy(iv, 0, combined, 0, iv.length);
        System.arraycopy(encryptedBytes, 0, combined, iv.length, encryptedBytes.length);
        String encryptedBase64 = Base64.getEncoder().encodeToString(combined);
        System.out.println("Encrypted (Base64): " + encryptedBase64);
        // --- Decryption ---
        // 1. Decode the combined data
        byte[] combinedDecoded = Base64.getDecoder().decode(encryptedBase64);
        // 2. Extract the IV and ciphertext
        byte[] extractedIv = new byte[16];
        byte[] cipherText = new byte[combinedDecoded.length - 16];
        System.arraycopy(combinedDecoded, 0, extractedIv, 0, extractedIv.length);
        System.arraycopy(combinedDecoded, extractedIv.length, cipherText, 0, cipherText.length);
        // 3. Setup the Cipher for decryption
        IvParameterSpec extractedIvSpec = new IvParameterSpec(extractedIv);
        Cipher decipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        decipher.init(Cipher.DECRYPT_MODE, secretKey, extractedIvSpec);
        // 4. Decrypt the data
        byte[] decryptedBytes = decipher.doFinal(cipherText);
        String decryptedText = new String(decryptedBytes, StandardCharsets.UTF_8);
        System.out.println("Decrypted: " + decryptedText);
    }
}

Delphi Implementation

Delphi's standard library does not have a built-in, high-level cryptography API like Java's javax.crypto. The most common and robust solution is to use a third-party library. Indy (TId... components) is very popular, but it's considered legacy. A more modern and recommended library is OpenSSL4Pascal.

Using OpenSSL4Pascal (Recommended)

This library provides a clean, object-oriented interface to the OpenSSL library, which is the industry standard.

  1. Install: Use the GetIt package manager in Delphi to install OpenSSL4Pascal.

    AES在Java与Delphi中的实现差异是什么?-图3
    (图片来源网络,侵删)
  2. Code Example: AES-256 in CBC Mode

unit Unit1;
interface
uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, OpenSSL4Pascal, System.StrUtils;
type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
var
  Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
var
  PlainText: string;
  KeyString: string;
  Cipher: TIdEVP_CIPHER;
  Encryptor: TIdEVP_CIPHER_CTX;
  Key, IV: TBytes;
  EncryptedBytes, FinalBytes: TBytes;
  Base64Str: string;
begin
  PlainText := 'This is a secret message.';
  KeyString := 'ThisIsASecretKey123456'; // 32 bytes for AES-256
  // 1. Setup the Cipher (AES-256 in CBC mode with PKCS#7 padding)
  Cipher := TIdEVP_CIPHER.Create(EVP_aes_256_cbc);
  try
    // 2. Create the key and IV
    // Key must be 256 bits (32 bytes)
    Key := TEncoding.UTF8.GetBytes(KeyString);
    // IV must be 128 bits (16 bytes)
    SetLength(IV, 16);
    RandomBytes(IV); // Helper function to generate random bytes
    // 3. Initialize the encryption context
    Encryptor := TIdEVP_CIPHER_CTX.Create;
    try
      Encryptor.InitEncrypt(Key, IV);
      // 4. Encrypt the data
      SetLength(EncryptedBytes, Encryptor.BlockSize);
      FinalBytes := nil;
      Encryptor.Update(TEncoding.UTF8.GetBytes(PlainText), EncryptedBytes, FinalBytes);
      Encryptor.Final(EncryptedBytes, FinalBytes);
      // Combine IV and ciphertext for storage/transmission
      FinalBytes := IV + FinalBytes;
      // 5. Encode to Base64 for easy transport
      Base64Str := TNetEncoding.Base64.EncodeBytes(FinalBytes);
      ShowMessage('Encrypted (Base64): ' + Base64Str);
      // --- Decryption ---
      // 1. Decode from Base64
      FinalBytes := TNetEncoding.Base64.DecodeString(Base64Str);
      // 2. Extract IV and ciphertext
      SetLength(IV, 16);
      Move(FinalBytes[0], IV[0], 16);
      SetLength(FinalBytes, Length(FinalBytes) - 16);
      // 3. Initialize the decryption context
      Encryptor.InitDecrypt(Key, IV);
      // 4. Decrypt the data
      SetLength(EncryptedBytes, Encryptor.BlockSize);
      var DecryptedBytes: TBytes;
      Encryptor.Update(FinalBytes, EncryptedBytes, DecryptedBytes);
      Encryptor.Final(EncryptedBytes, DecryptedBytes);
      // 5. Convert back to string
      var DecryptedText := TEncoding.UTF8.GetString(DecryptedBytes);
      ShowMessage('Decrypted: ' + DecryptedText);
    finally
      Encryptor.Free;
    end;
  finally
    Cipher.Free;
  end;
end;
// Helper function to generate random bytes
procedure RandomBytes(var Bytes: TBytes);
var
  HCryptProv: Cardinal;
  Buffer: PByte;
  I: Integer;
begin
  if CryptAcquireContext(@HCryptProv, nil, nil, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT or CRYPT_SILENT) then
  try
    SetLength(Bytes, Length(Bytes));
    Buffer := PByte(Bytes);
    CryptGenRandom(HCryptProv, Length(Bytes), Buffer);
  finally
    CryptReleaseContext(HCryptProv, 0);
  end;
end;
end.

Key Differences and Summary

Feature Java (javax.crypto) Delphi (OpenSSL4Pascal)
Availability Built-in. No external libraries needed. Requires a third-party library. OpenSSL4Pascal is the modern standard.
API Style Procedural/OOP. Cipher.getInstance("AES/CBC/PKCS5Padding") is a factory method. Object-Oriented. TIdEVP.Create(EVP_aes_256_cbc) is a constructor.
Key/IV Handling Uses specific classes: SecretKeySpec, IvParameterSpec. Passes raw TBytes for Key and IV to the cipher context.
Mode/Padding Specified as a single string in Cipher.getInstance(). Specified by passing a constant (e.g., EVP_aes_256_cbc) which implies the mode.
Data Handling doFinal() on a byte array. Returns the final encrypted bytes. Update() and Final() methods. You manage byte arrays yourself.
Modern Modes (AEAD) Excellent support. AES/GCM/NoPadding is straightforward. Excellent support. TIdEVP_AES_GCM is available for authenticated encryption.
Ease of Use Generally considered easier and more consistent for standard operations. More manual memory management of byte arrays, but very powerful once you learn the pattern.

Critical Best Practices (For Both Languages)

  1. NEVER Roll Your Own Crypto: Always use a well-vetted, standard library like the ones shown above.
  2. Use Strong Modes: Avoid ECB mode. Use modes like GCM (Galois/Counter Mode) or CBC with a strong padding scheme. GCM is preferred for new applications as it provides both confidentiality and authentication.
  3. IV Must Be Unique: For a given key, the IV must never be reused for two different messages. A random IV is the standard and safest approach.
  4. IV is Not Secret: Store the IV alongside the ciphertext. It does not need to be protected.
  5. Key Management is Hard: Storing keys securely is one of the hardest parts of cryptography. For simple applications, a key derived from a password using a Key Derivation Function (KDF) like PBKDF2 or Argon2 is better than a raw string.
  6. Handle Exceptions: Cryptographic operations can fail (e.g., bad padding, wrong key length). Always wrap your code in try...catch (Java) or try...finally (Delphi).
分享:
扫描分享到社交APP
上一篇
下一篇