簡體   English   中英

將 PEM 格式的公鑰傳遞給 openssl_pkey_get_public 會給出錯誤:0906D06C:PEM 例程:PEM_read_bio:no start line

[英]Passing public key in PEM format to openssl_pkey_get_public gives error:0906D06C:PEM routines:PEM_read_bio:no start line

以下 PEM 格式的公共 RSA 密鑰已提供給 openssl_pkey_get_public。

-----BEGIN PUBLIC KEY-----
MIIBITANBgkqhkiG9w0BAQEFAAOCAQ4AMIIBCQKCAQCIZouo/rL5IkIIGrke/qkY
Nsb9JDXUw2MfutYdwIVjPiEbAcLiVxK6tOVXy7dq+hU0zyNd68bUi7VJjXWoiepS
+Mm6v76GCGvVvno48m7ofWIq6VLEaMQjIM/pzkF0TW7CmtjKvgg722Hx87AI/KCM
sWuHjhcQZsMgV4ibC8EAY6GYwHYAPWfUq+LI2wfRsQHumFC2IuT4guO/Vs5FJGXw
Arrvv7VPyKwZ8cpcZn9ka1K0N7su7QiGnzOhS3n2THaj25alE6TMXnrKmt6yIiXh
amsKVEKPPzHpw9ldTao1aG7vVNC9QXC8i9uQTWhhokxvSNw5OYFFkDZC5jD7McvB
AgMBAAE=
-----END PUBLIC KEY-----

但是,方法調用失敗,返回 false,錯誤字符串error:0906D06C:PEM routines:PEM_read_bio:no start line

公鑰無效嗎? 作為記錄,我的代碼從公鑰模數和指數開始,並使用此處發布的算法將其轉換為 PEM 格式。

這是完整的腳本:

<?php

function createPemFromModulusAndExponent($n, $e)
{
    $modulus = urlsafeB64Decode($n);
    $publicExponent = urlsafeB64Decode($e);
    $components = array(
        'modulus' => pack('Ca*a*', 2, encodeLength(strlen($modulus)), $modulus),
        'publicExponent' => pack('Ca*a*', 2, encodeLength(strlen($publicExponent)), $publicExponent)
    );

    $RSAPublicKey = pack('Ca*a*a*', 48, encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])), $components['modulus'], $components['publicExponent']);

    $rsaOID = pack('H*', '300d06092a864886f70d0101010500');
    $RSAPublicKey = chr(0) . $RSAPublicKey;
    $RSAPublicKey = chr(3) . encodeLength(strlen($RSAPublicKey)) . $RSAPublicKey;
    $RSAPublicKey = pack('Ca*a*', 48, encodeLength(strlen($rsaOID . $RSAPublicKey)), $rsaOID . $RSAPublicKey);

    $RSAPublicKey = "-----BEGIN PUBLIC KEY-----" . chunk_split(base64_encode($RSAPublicKey), 64) . '-----END PUBLIC KEY-----';
    return $RSAPublicKey;
}

function urlsafeB64Decode($input)
{
    $remainder = strlen($input) % 4;
    if ($remainder)
    {
        $padlen = 4 - $remainder;
        $input .= str_repeat('=', $padlen);
    }
    return base64_decode(strtr($input, '-_', '+/'));
}

function encodeLength($length)
{
    if ($length <= 0x7F)
    {
        return chr($length);
    }

    $temp = ltrim(pack('N', $length), chr(0));
    return pack('Ca*', 0x80 | strlen($temp), $temp);
}

$key = createPemFromModulusAndExponent('iGaLqP6y-SJCCBq5Hv6pGDbG_SQ11MNjH7rWHcCFYz4hGwHC4lcSurTlV8u3avoVNM8jXevG1Iu1SY11qInqUvjJur--hghr1b56OPJu6H1iKulSxGjEIyDP6c5BdE1uwprYyr4IO9th8fOwCPygjLFrh44XEGbDIFeImwvBAGOhmMB2AD1n1KviyNsH0bEB7phQtiLk-ILjv1bORSRl8AK677-1T8isGfHKXGZ_ZGtStDe7Lu0Ihp8zoUt59kx2o9uWpROkzF56ypresiIl4WprClRCjz8x6cPZXU2qNWhu71TQvUFwvIvbkE1oYaJMb0jcOTmBRZA2QuYw-zHLwQ', 'AQAB');

print_r($key);

print_r(openssl_pkey_get_public($key));

print_r(openssl_error_string());

首先: openssl_pkey_get_public旨在直接加載公鑰或從證書中提取公鑰,如openssl_pkey_get_publiccertificate參數文檔中所述。

已經為此問題提交了一個錯誤,即 2017 年 12 月(版本 7.1.12)的#75643 ,其狀態為No Feedback ,目前已暫停(請注意,#75643 實際上是指openssl_public_encrypt ,但它使用相同的邏輯關於密鑰為openssl_pkey_get_public在這里):

隊列中的錯誤是預期的。 如果您將字符串作為 PEM 提供(字符串不以“file://”為前綴,這將是一個文件路徑),則首先嘗試證書(使用 PEM_ASN1_read_bio)。 這意味着它失敗並且錯誤被保存到隊列中。 然而,這個隊列只是被清空的 OpenSSL 的副本。 之后,使用 PEM_read_bio_PUBKEY 加載密鑰,這在您的情況下是成功的,因此您可以取回結果。 總而言之,openssl_error_string 並不意味着操作失敗,而只是發出了一些錯誤......

據此,錯誤消息是由於未能從證書中提取密鑰引起的。 但是,繼續處理並直接加載密鑰。 換句話說,錯誤消息在直接加載密鑰時按預期出現,並且在這種情況下可以忽略(至少在直接加載成功的情況下)。

對於記錄:從 7.2(.17) 開始,顯示的錯誤消息略有不同: error:0909006C:PEMroutines:get_name:no start line


更新:

正如@President James Moveon Polk 在他的評論中指出的那樣, createPemFromModulusAndExponent沒有正確生成密鑰。 如果第一個/最高有效字節大於0x7F ,則模數前面必須有一個0x00字節,這目前不會發生。 例如,在發布的代碼中,模數以0x88開頭(Base64url 解碼),這意味着生成的(= 發布的)密鑰無效。 如果手動添加0x00並且如此更正的值(Base64url 編碼)傳遞給createPemFromModulusAndExponent ,則以下現在有效的密鑰結果:

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiGaLqP6y+SJCCBq5Hv6p
GDbG/SQ11MNjH7rWHcCFYz4hGwHC4lcSurTlV8u3avoVNM8jXevG1Iu1SY11qInq
UvjJur++hghr1b56OPJu6H1iKulSxGjEIyDP6c5BdE1uwprYyr4IO9th8fOwCPyg
jLFrh44XEGbDIFeImwvBAGOhmMB2AD1n1KviyNsH0bEB7phQtiLk+ILjv1bORSRl
8AK677+1T8isGfHKXGZ/ZGtStDe7Lu0Ihp8zoUt59kx2o9uWpROkzF56ypresiIl
4WprClRCjz8x6cPZXU2qNWhu71TQvUFwvIvbkE1oYaJMb0jcOTmBRZA2QuYw+zHL
wQIDAQAB
-----END PUBLIC KEY-----

當然,如果createPemFromModulusAndExponent會自動進行此校正會更好。 @President James Moveon Polk 已為此提出問題,請點擊此處

請允許我提出一種更簡單、更簡潔的替代方法。 使用 phpseclib,

require __DIR__ . '/vendor/autoload.php';

use phpseclib\Math\BigInteger;
use phpseclib\Crypt\RSA;

$rsa = new RSA;
$rsa->loadKey([
    'e' => new BigInteger(base64_decode('AQAB'), 256),
    'n' => new BigInteger(base64_decode('iGaLqP6y-SJCCBq5Hv6pGDbG_SQ11MNjH7rWHcCFYz4hGwHC4lcSurTlV8u3avoVNM8jXevG1Iu1SY11qInqUvjJur--hghr1b56OPJu6H1iKulSxGjEIyDP6c5BdE1uwprYyr4IO9th8fOwCPygjLFrh44XEGbDIFeImwvBAGOhmMB2AD1n1KviyNsH0bEB7phQtiLk-ILjv1bORSRl8AK677-1T8isGfHKXGZ_ZGtStDe7Lu0Ihp8zoUt59kx2o9uWpROkzF56ypresiIl4WprClRCjz8x6cPZXU2qNWhu71TQvUFwvIvbkE1oYaJMb0jcOTmBRZA2QuYw-zHLwQ'), 256)
]);

print_r(openssl_pkey_get_public($rsa));

實際上,您使用的代碼是使用從 phpseclib 2.0 中提取的代碼。 有關更多信息,請參閱https://github.com/dragosgaftoneanu/okta-simple-jwt-verifier/issues/1#issuecomment-612503921

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM