【PHP8.x】openssl_open関数の使い方

作成日: 更新日:

openssl_open関数は、OpenSSL拡張機能の一部として、暗号化されたメッセージを安全に復号するために使用される関数です。この関数は、主にデジタルエンベロープの概念に基づいた、非対称鍵暗号と対称鍵暗号の組み合わせによるデータの復号化を行います。

具体的には、送信者が秘密のメッセージを一時的な共通鍵(対称鍵)で暗号化し、その共通鍵をさらに受信者の公開鍵で暗号化して、メッセージと共に送信するケースで利用されます。openssl_open関数は、受信者の秘密鍵を使って暗号化された共通鍵を復号し、次にその復号された共通鍵を使って、元の秘密のメッセージを復号するという一連のプロセスを実行します。

この関数は、暗号化されたデータ本体、復号後のデータを格納する変数の参照、受信者の秘密鍵リソース、および送信者の公開鍵(または証明書)リソースなどを引数として受け取ります。復号処理が成功した場合は真(true)を返し、失敗した場合は偽(false)を返します。セキュリティに関わる重要な処理であるため、戻り値を必ず確認し、必要に応じてopenssl_error_string()関数を使用してエラー原因を調査することが非常に重要です。

安全な情報通信やデータの保護を実現するために不可欠な機能であり、PHPアプリケーションで暗号化された情報を扱う際に利用されます。この機能を利用するには、PHP環境でOpenSSL拡張機能が有効になっている必要があります。

基本的な使い方

構文(syntax)

openssl_open(string $encrypted_data, string &$open_data, string $encrypted_key, mixed $private_key, string $cipher_algo = "AES-128-CBC", string $iv = ""): bool

引数(parameters)

string $data, string &$output, string $encrypted_key, mixed $private_key, string $cipher_algo, string $iv = ""

  • string $data: 復号化したい暗号化されたデータを指定する文字列
  • string &$output: 復号化されたデータが格納される参照渡し文字列
  • string $encrypted_key: 暗号化されたセッションキーを指定する文字列
  • mixed $private_key: 復号化に使用する秘密鍵。string(PEM形式)、resource(OpenSSL key resource)、またはarray(password付きPEM)で指定可能
  • string $cipher_algo: 復号化に使用する暗号化アルゴリズムを指定する文字列(例: 'aes-256-cbc')
  • string $iv = "": 初期化ベクトル(IV)を指定する文字列。アルゴリズムによっては必須

戻り値(return)

bool

openssl_open関数は、指定された公開鍵で暗号化されたデータを復号化する処理が成功したかどうかを示す真偽値(bool)を返します。成功した場合は true、失敗した場合は false が返されます。

サンプルコード

openssl_openでデータを復号する

<?php

/**
 * Demonstrates the usage of openssl_open for decrypting data that was sealed (encrypted)
 * using openssl_seal. This process involves hybrid encryption, where a symmetric key
 * is used for data encryption and then that symmetric key is encrypted by an
 * asymmetric (public) key. openssl_open uses the private key to decrypt the
 * symmetric key, and then decrypts the data.
 */
function demonstrateOpensslOpen(): void
{
    // 1. Generate a new private and public key pair (RSA is common for this).
    $config = [
        "digest_alg"       => "sha512",         // Hashing algorithm for key generation.
        "private_key_bits" => 2048,             // Key strength in bits.
        "private_key_type" => OPENSSL_KEYTYPE_RSA, // Type of key.
    ];

    // Create a new private key resource.
    $privateKeyResource = openssl_pkey_new($config);
    if ($privateKeyResource === false) {
        echo "Error: Failed to generate private key.\n";
        return;
    }

    // Export the private key to a string. In a real system, this would be stored securely.
    $privateKeyString = '';
    if (!openssl_pkey_export($privateKeyResource, $privateKeyString)) {
        echo "Error: Failed to export private key.\n";
        openssl_pkey_free($privateKeyResource);
        return;
    }

    // Get the public key details from the private key resource.
    $publicKeyDetails = openssl_pkey_get_details($privateKeyResource);
    if ($publicKeyDetails === false) {
        echo "Error: Failed to get public key details.\n";
        openssl_pkey_free($privateKeyResource);
        return;
    }
    // Extract the public key string. This key is shared for encryption.
    $publicKeyString = $publicKeyDetails['key'];

    // Define the original plaintext data to be encrypted.
    $plainText = "Hello, aspiring System Engineer! This is a secret message to be protected.";
    // Define the symmetric cipher algorithm to be used for data encryption.
    $cipherAlgo = 'aes-256-cbc';

    // Generate a unique Initialization Vector (IV) for the chosen cipher.
    // The IV is essential for security in modes like CBC and must be unique for each encryption.
    $ivLength = openssl_cipher_iv_length($cipherAlgo);
    if ($ivLength === false) {
        echo "Error: Invalid cipher algorithm specified: " . $cipherAlgo . "\n";
        openssl_pkey_free($privateKeyResource);
        return;
    }
    $iv = openssl_random_pseudo_bytes($ivLength);
    if ($iv === false) {
        echo "Error: Failed to generate Initialization Vector (IV).\n";
        openssl_pkey_free($privateKeyResource);
        return;
    }

    echo "Original Text: " . $plainText . "\n";
    echo "----------------------------------------\n";

    // 2. Encrypt (seal) the data using openssl_seal.
    // This function:
    // a) Generates a random symmetric key.
    // b) Encrypts the $plainText with this symmetric key using $cipherAlgo and $iv.
    // c) Encrypts the symmetric key itself using the provided $publicKeysArray.
    $sealedData = '';        // Will store the symmetrically encrypted data.
    $encryptedKeys = [];     // Will store the symmetrically key(s) encrypted by public key(s).
    $publicKeysArray = [$publicKeyString]; // openssl_seal expects an array of public keys.

    if (!openssl_seal($plainText, $sealedData, $encryptedKeys, $publicKeysArray, $cipherAlgo, $iv)) {
        echo "Error: Failed to seal data.\n";
        while ($msg = openssl_error_string()) { // Output any OpenSSL errors for debugging.
            echo "OpenSSL error: " . $msg . "\n";
        }
        openssl_pkey_free($privateKeyResource);
        return;
    }

    // The first element of $encryptedKeys holds the symmetric key, encrypted by our public key.
    $encryptedSymmetricKey = $encryptedKeys[0];

    echo "Data sealed successfully (encrypted with public key).\n";
    // echo "Sealed Data (base64 for display): " . base64_encode($sealedData) . "\n";
    // echo "Encrypted Symmetric Key (base64 for display): " . base64_encode($encryptedSymmetricKey) . "\n";
    echo "----------------------------------------\n";

    // 3. Decrypt (open) the sealed data using openssl_open.
    // This function reverses the process:
    // a) Uses $privateKeyString to decrypt $encryptedSymmetricKey, recovering the symmetric key.
    // b) Uses the recovered symmetric key, $cipherAlgo, and $iv to decrypt $sealedData.
    $decryptedText = ''; // This variable will store the decrypted plaintext.
    if (!openssl_open($sealedData, $decryptedText, $encryptedSymmetricKey, $privateKeyString, $cipherAlgo, $iv)) {
        echo "Error: Failed to open (decrypt) sealed data.\n";
        while ($msg = openssl_error_string()) { // Output any OpenSSL errors for debugging.
            echo "OpenSSL error: " . $msg . "\n";
        }
        openssl_pkey_free($privateKeyResource);
        return;
    }

    echo "Data opened (decrypted with private key) successfully.\n";
    echo "Decrypted Text: " . $decryptedText . "\n";

    // Verify if the decrypted text matches the original plaintext.
    if ($plainText === $decryptedText) {
        echo "Verification: Decrypted text matches original text. Success!\n";
    } else {
        echo "Verification: Decrypted text DOES NOT match original text. Failure!\n";
    }

    // Free the private key resource to release memory.
    openssl_pkey_free($privateKeyResource);
}

// Execute the demonstration function to see openssl_open in action.
demonstrateOpensslOpen();

openssl_open関数は、openssl_seal関数でハイブリッド暗号化されたデータを復号するために利用されます。ハイブリッド暗号化とは、実際のデータは高速な共通鍵暗号方式で暗号化し、その共通鍵だけを公開鍵暗号方式で暗号化して、安全に受け渡しする手法です。openssl_openはこの逆の処理を行い、暗号化された共通鍵とデータを復号します。

具体的には、引数$encrypted_keyで渡された、公開鍵で暗号化された共通鍵を、引数$private_keyで指定された秘密鍵を使って復号します。次に、復号された共通鍵と引数$cipher_algoで指定された暗号化アルゴリズム、および引数$ivで渡された初期化ベクトル(IV)を用いて、引数$dataで渡された暗号化済みのデータを復号します。最終的に復号された平文データは、参照渡しされる引数&$outputに格納されます。

引数$dataには暗号化されたデータ本体、&$outputには復号結果が格納されます。$encrypted_keyには公開鍵で暗号化された共通鍵、$private_keyには復号に使う秘密鍵(リソースまたは文字列)、$cipher_algoにはデータの暗号化に使用したアルゴリズム、$ivには同じくデータ暗号化で使用したIVを指定します。

この関数は、復号処理が成功すればtrueを、失敗すればfalseをブール値で返します。サンプルコードでは、秘密鍵と公開鍵の生成から始まり、openssl_sealによるデータの暗号化、そしてopenssl_openによる復号までの一連の流れが示されており、安全なデータ通信の仕組みを学ぶ上で非常に参考になります。

openssl_openは、openssl_sealで暗号化されたデータを復号する際に使用します。この関数は、公開鍵で暗号化された共通鍵を秘密鍵で復号し、その共通鍵を用いて暗号データを平文に戻すハイブリッド暗号方式を採用しています。引数には、暗号化データ、公開鍵で暗号化された共通鍵、秘密鍵、共通鍵暗号化アルゴリズム、初期化ベクトル(IV)を正確に渡す必要があります。特に、秘密鍵は厳重に管理し、使用後は速やかにメモリから解放することがセキュリティ上不可欠です。また、IVは暗号化のたびにユニークなものを生成し、暗号データと共に渡してください。復号に失敗した場合は、openssl_error_string()でOpenSSLのエラーを確認し、原因を特定しましょう。引数の不一致は復号失敗の主な原因です。

openssl_open でエンベロープ暗号化を複合化する

<?php

/**
 * openssl_open 関数を使用して、エンベロープ暗号化されたデータを複合化する例を示します。
 *
 * この関数は以下のステップを実行します:
 * 1. RSA鍵ペア (公開鍵と秘密鍵) を生成します。
 * 2. 任意の元データを準備します。
 * 3. データを暗号化するためのセッションキー (対称鍵) と初期化ベクトル (IV) を生成します。
 * 4. セッションキーを使用して元データを暗号化します (openssl_encrypt)。この際、生データ形式を使用します。
 * 5. 受信者の公開鍵を使用してセッションキーを暗号化します (openssl_public_encrypt)。
 * 6. 暗号化されたデータと暗号化されたセッションキーを、openssl_open 関数と受信者の秘密鍵を使用して複合化します。
 * 7. 複合化されたデータとセッションキーが元のものと一致するか検証します。
 */
function demonstrateOpensslOpenUsage(): void
{
    echo "--- openssl_open 関数使用例の開始 ---" . PHP_EOL . PHP_EOL;

    // 1. RSA鍵ペアの生成
    // 実際のシステムでは、よりセキュアな方法で鍵を生成し、ファイルなどに保存して管理します。
    $config = [
        "digest_alg"       => "sha512",
        "private_key_bits" => 2048,
        "private_key_type" => OPENSSL_KEYTYPE_RSA,
    ];

    $res = openssl_pkey_new($config);
    if ($res === false) {
        echo "エラー: 鍵ペアの生成に失敗しました。" . PHP_EOL . openssl_error_string() . PHP_EOL;
        return;
    }

    // 秘密鍵をPEM形式の文字列としてエクスポート
    if (!openssl_pkey_export($res, $privateKeyPem)) {
        echo "エラー: 秘密鍵のエクスポートに失敗しました。" . PHP_EOL . openssl_error_string() . PHP_EOL;
        return;
    }

    // 公開鍵をPEM形式の文字列として取得
    $publicKeyDetails = openssl_pkey_get_details($res);
    if ($publicKeyDetails === false || !isset($publicKeyDetails['key'])) {
        echo "エラー: 公開鍵の詳細取得に失敗しました。" . PHP_EOL . openssl_error_string() . PHP_EOL;
        return;
    }
    $publicKeyPem = $publicKeyDetails['key'];

    echo "INFO: RSA鍵ペアが正常に生成されました。" . PHP_EOL . PHP_EOL;

    // 2. 元データの準備
    $originalData = "これはOpenssl_open関数を使った機密メッセージです。";
    $cipherAlgo = 'aes-256-cbc'; // 対称暗号化アルゴリズム (OpenSSLでサポートされているもの)
    $ivLength = openssl_cipher_iv_length($cipherAlgo);
    // 初期化ベクトル (IV) を生成。暗号化と複合化で同じIVを使用する必要があります。
    $iv = openssl_random_pseudo_bytes($ivLength);

    echo "INFO: 元データ: '" . $originalData . "'" . PHP_EOL;
    echo "INFO: 使用する暗号アルゴリズム: " . $cipherAlgo . PHP_EOL;
    echo "INFO: IV (Base64エンコード): " . base64_encode($iv) . PHP_EOL . PHP_EOL;

    // 3. セッションキー (対称鍵) の生成
    // openssl_open はこのセッションキーの暗号化されたバージョンを受け取り、複合化します。
    // AES-256 の場合、32バイト (256ビット) のキーを使用します。
    $sessionKey = openssl_random_pseudo_bytes(32);
    echo "INFO: セッションキーが生成されました。" . PHP_EOL . PHP_EOL;

    // 4. セッションキーを使用して元データを暗号化
    // OPENSSL_RAW_DATA フラグを使用すると、結果はbase64エンコードされずに生のバイナリ形式で返されます。
    // キーワード 'openssl_encrypt' と 'openssl_raw_data' に関連。
    $encryptedData = openssl_encrypt(
        $originalData,
        $cipherAlgo,
        $sessionKey,
        OPENSSL_RAW_DATA, // 生のバイナリデータを出力
        $iv
    );

    if ($encryptedData === false) {
        echo "エラー: データの暗号化に失敗しました。" . PHP_EOL . openssl_error_string() . PHP_EOL;
        return;
    }
    echo "INFO: データがセッションキーで暗号化されました。" . PHP_EOL;
    echo "INFO: 暗号化データ (Base64エンコード): " . base64_encode($encryptedData) . PHP_EOL . PHP_EOL;

    // 5. 受信者の公開鍵を使用してセッションキーを暗号化 (エンベロープ暗号化)
    // この暗号化されたセッションキーが openssl_open の引数 $encrypted_key になります。
    $encryptedSessionKey = '';
    if (!openssl_public_encrypt($sessionKey, $encryptedSessionKey, $publicKeyPem)) {
        echo "エラー: セッションキーの公開鍵暗号化に失敗しました。" . PHP_EOL . openssl_error_string() . PHP_EOL;
        return;
    }
    echo "INFO: セッションキーが公開鍵で暗号化されました。" . PHP_EOL;
    echo "INFO: 暗号化セッションキー (Base64エンコード): " . base64_encode($encryptedSessionKey) . PHP_EOL . PHP_EOL;

    // --- ここまでが送信者側で準備されるデータ ---
    // --- ここからが受信者側で行われる複合化処理 ---

    // 6. openssl_open 関数を使用してデータとセッションキーを複合化
    // $decryptedDataOutput は参照渡しで、複合化されたデータが格納されます。
    $decryptedDataOutput = ''; // 複合化されたデータがここに格納されます

    // openssl_open の戻り値は、複合化されたセッションキー (string) または失敗時に false です。
    $decryptedSessionKey = openssl_open(
        $encryptedData,        // 暗号化されたデータ
        $decryptedDataOutput,  // 複合化されたデータが格納される変数 (参照渡し)
        $encryptedSessionKey,  // 公開鍵で暗号化されたセッションキー
        $privateKeyPem,        // 受信者の秘密鍵
        $cipherAlgo,           // 暗号化に使用されたアルゴリズム
        $iv                    // 初期化ベクトル
    );

    if ($decryptedSessionKey === false) {
        echo "エラー: openssl_open によるデータとセッションキーの複合化に失敗しました。" . PHP_EOL . openssl_error_string() . PHP_EOL;
        return;
    }

    echo "--- openssl_open による複合化が完了しました ---" . PHP_EOL;
    echo "結果: 複合化されたデータ: '" . $decryptedDataOutput . "'" . PHP_EOL;
    echo "結果: 複合化されたセッションキー (Base64エンコード): " . base64_encode($decryptedSessionKey) . PHP_EOL . PHP_EOL;

    // 7. 検証: 複合化されたデータとセッションキーが元のものと一致するか確認
    if ($decryptedDataOutput === $originalData) {
        echo "検証結果: 成功!複合化されたデータは元のデータと一致します。" . PHP_EOL;
    } else {
        echo "検証結果: 失敗!複合化されたデータが元のデータと一致しません。" . PHP_EOL;
    }

    if ($decryptedSessionKey === $sessionKey) {
        echo "検証結果: 成功!複合化されたセッションキーは元のセッションキーと一致します。" . PHP_EOL;
    } else {
        echo "検証結果: 失敗!複合化されたセッションキーが元のセッションキーと一致しません。" . PHP_EOL;
    }

    echo PHP_EOL . "--- openssl_open 関数使用例の終了 ---" . PHP_EOL;
}

// 関数を実行してサンプルコードの動作を確認します
demonstrateOpensslOpenUsage();

PHPのopenssl_open関数は、エンベロープ暗号化されたデータを複合化するために使用されます。エンベロープ暗号化とは、データ本体を高速な対称鍵で暗号化し、その対称鍵を非対称鍵(公開鍵)でさらに暗号化して安全に送受信する技術です。

この関数は、$dataで指定された対称暗号化済みのデータと、$encrypted_keyで指定された公開鍵で暗号化済みのセッションキー(対称鍵)を複合化します。処理では、まず$private_key(受信者の秘密鍵)を用いて$encrypted_keyを複合化し、その結果得られたセッションキーと、元の暗号化に使用された$cipher_algo(暗号アルゴリズム)および$iv(初期化ベクトル)を使用して$dataを複合化します。複合化されたデータ本体は、参照渡しである&$outputに格納されます。

関数が成功した場合は、複合化されたセッションキーが文字列として返され、失敗した場合はfalseが返されます。データ本体の暗号化は、通常openssl_encrypt関数とOPENSSL_RAW_DATAフラグを用いて行われます。

openssl_open関数は、エンベロープ暗号化されたデータを複合化し、その際に使用された対称鍵(セッションキー)も同時に複合化する関数です。引数$dataには対称鍵で暗号化されたデータを渡し、&$outputに複合化された平文データが格納されます。$encrypted_keyには公開鍵で暗号化されたセッションキーを、$private_keyにはそのセッションキーを複合化するための秘密鍵を指定してください。また、$cipher_algo$ivは、データを暗号化した際に使用したものと完全に一致させる必要があります。特に$ivは暗号化時と複合化時で同じ値を再利用する点が重要です。関数が成功すると複合化されたセッションキーを文字列で返し、失敗するとfalseを返しますので、戻り値での成否判定と、複合化されたセッションキーの取得方法を理解してください。実運用では、鍵の生成や安全な管理が非常に重要です。

【PHP8.x】openssl_open関数の使い方 | いっしー@Webエンジニア