0

この投稿を見つけました PHP で Google Chrome Crx ファイル を作成して、ZIP アーカイブから .crx ファイルを作成します。しかし、実行しようとすると、長さが $signtrue = 0 になり、理由がわかりませんでした... .pem ファイルが見つかって有効であり、.pub キーファイルも同様ですが、それでも返されます署名の長さがゼロです...

$fp = fopen("/public_html/apps/chrome/nsii.pem", "r");
$priv_key = fread($fp, filesize($fp));
fclose($fp);

$pkeyid = openssl_get_privatekey($priv_key);
openssl_sign(file_get_contents("$name.zip"), $signature, $pkeyid, 'sha1');
openssl_free_key($pk);

# decode the public key

$key = base64_decode(file_get_contents('extension_public_key.pub'));

$fh = fopen("$name.crx", 'wb');
fwrite($fh, 'Cr24');             // extension file magic number
fwrite($fh, pack('V', 2));       // crx format version
fwrite($fh, pack('V', strlen($key)));            // public key length
fwrite($fh, pack('V', strlen($signature)));      // signature length
fwrite($fh, $key);               // public key
fwrite($fh, $signature);         // signature
fwrite($fh, file_get_contents("$name.zip")); // package contents, zipped
fclose($fh);

アップデート:

元のコードは次のとおりです。

<?php
# make a SHA1 signature using our private key
$pk = openssl_pkey_get_private(file_get_contents('/public_html/apps/chrome/nsii.pem'));
openssl_sign(file_get_contents('/public_html/apps/chrome/extGoogle.zip'), $signature, $pk, 'sha1');
openssl_free_key($pk);

# decode the public key
$key = base64_decode(file_get_contents('/public_html/apps/chrome/extension_public_key.pub'));

# .crx package format:
#
#   magic number               char(4)
#   crx format ver             byte(4)
#   pub key lenth              byte(4)
#   signature length           byte(4)
#   public key                 string
#   signature                  string
#   package contents, zipped   string
#
# see http://code.google.com/chrome/extensions/crx.html
#
$fh = fopen('extension.crx', 'wb');
fwrite($fh, 'Cr24');                             // extension file magic number
fwrite($fh, pack('V', 2));                       // crx format version
fwrite($fh, pack('V', strlen($key)));            // public key length
fwrite($fh, pack('V', strlen($signature)));      // signature length
fwrite($fh, $key);                               // public key
fwrite($fh, $signature);                         // signature
fwrite($fh, file_get_contents('/public_html/apps/chrome/extGoogle.zip')); // package contents, zipped
fclose($fh);

うまくいかないようです...質問はなぜですか?... CLIを使用してコンパイルすると、すべて正常に動作します...

4

1 に答える 1

2

あなたの質問はそのままでは、正確に何がうまくいかないのかという兆候はありません。

ただし、エラーチェックがあまり行われていないことがわかります。よく書かれたコードではよくあることなので、最初に戻り値のテストをいくつか追加することをお勧めします。また、エラー レポートを有効にし、エラー ログを追跡して問題を確認する必要がある場合もあります。

コードを少し見直して、コメントを追加しましょう。いくつかのポインタだけで完全ではありません。上にある次の 3 行から始めます。

$fp = fopen("/public_html/apps/chrome/nsii.pem", "r");
$priv_key = fread($fp, filesize($fp));
fclose($fp);

ご存知のとおり (少なくとも後で使用します)、便利な関数がありますfile_get_contents。これらの 3 行を置き換えることができます。コードをよりわかりやすくするために、さらにいくつかの行を追加します。

$privateKeyPemFile = "/public_html/apps/chrome/nsii.pem";
$privateKey = file_get_contents($privateKeyPemFile);
if (false === $privateKey) {
    throw new UnexpectedValueException(
        sprintf("Unable to read private key file %s", $privateKeyPemFile)
    );
}

これを使用する次file_get_contentsは、実際にファイルを開くことが正常に機能することを確認します。そうでない場合は、UnexpectedValueException何が起こったかを示すエラー メッセージとともに がスローされます。例外の名前はそれほど重要ではありませんException

ここで例外をスローすると、これが失敗した場合に実際にメッセージが表示されます。

ご覧のとおり、関数の戻り値を検証し、エラー条件を確認する必要があります。マニュアルで使用している PHP 関数を参照し、文書化されている戻り値とその意味を確認してください。ここの のように、エラーのケースがよくありますfile_get_contents

次に、ゼロ長について特に懸念しています$signature。各関数について、戻り値をチェックしてエラー情報を取得できるかどうかを見てみましょう。

$keyResource = openssl_pkey_get_private($privateKey);

openssl_get_privatekey実際には のエイリアスなopenssl_pkey_get_privateので、ここでは既に変更されています。また、変数名の名前を変更したので、もっと言えば、十分なスペースがあり、複雑な省略形は必要ありません。

ここでも戻り値があり、これはfalseエラーの場合です。エラーのない状態では、これは重要なリソースであるため、それに応じて戻り値に名前を付けました。エラーチェックを再度追加しましょう。

if (false === $keyResource) {
    throw new UnexpectedValueException("Unable to parse and prepare private key");
}

したがって、次の行までは、最初は変更されていません。

openssl_sign(file_get_contents("$name.zip"), $signature, $pkeyid, 'sha1');

file_get_contents明らかに、zip ファイルのエラーチェックはありません。代わりに、これを安全にするためにさらにいくつかのコード行が必要です。

$zipFile     = "$name.zip";
$zipContents = file_get_contents($zipFile);
if (false === $zipContents) { 
    throw new ... 
}

そして、これはデータをロードするためだけのものだったので、openssl_sign呼び出しを採用する必要があります:

$signatureAlgorithm = OPENSSL_ALGO_SHA1;
$result = openssl_sign($zipContents, $signature, $keyResource, $signatureAlgorithm);
if (!$result) {
    throw new ...
}

おそらく最初の違いは'SHA1'、マニュアルでは定数が使用されていると書かれているのに、文字列を使用したことです。多分それは同じ値を持っているかもしれません、私はあなたに言うことができません、私はここで文書化された定数を使用しました(文書化が常に正しいとは限りませんが、何が起こっているのかわからない場合は、まず第一にdocs を参照してください)。

ここでも、関数の戻り値でエラー状態がチェックされています。コードがその関数で失敗した可能性があると仮定すると、失敗したことだけがわかりますが、何が間違っていたのかはわかりません。

PHP のマニュアルにはopenssl_error_string、エラー文字列を返す (peu-a-peu) 関数が呼び出されることが示されています。次々に返されるため、ループしてすべてを取得する必要があります (最新のものを最初に)。これは、共通のforループで実行できます。

for ($buffer = ''; $string = openssl_error_string(); $buffer .= $string, "\n");

したがって、それを例外に追加できます。おそらく、これをある種の関数の中に入れて、必要なことを行うのも便利です。

if (!$result) {
    $buffer = sprintf(
        "Failed to sign zip contents (%d); Openssl error(s):\n", strlen($zipContents)
    );
    for (; $string = openssl_error_string(); $buffer .= $string, "\n");
    throw new UnexpectedValueException($buffer);
}

したがって、あなたのコードは、エラー状態をチェックし、さまざまな関数/ライブラリが失敗を通知するさまざまな方法とそれに関する情報を取得する方法を示す場所の完璧な例です。

安全な方法でコードを作成すると、すべての事前および事後条件チェックが行われます。つまり、戻り値をチェックするだけでなく、使用する前に値もチェックすることになります。1 つの例は、zip ファイルのファイル名です。ファイルを開く前に、ファイルが存在し、読み取り可能であることを確認します。

$zipFile = "$name.zip";
if (!is_readable($zipFile)) {
    throw new Exception(sprintf("Zipfile is not readable %s", $zipFile));
}
$zipContents = file_get_contents($zipFile);
...

これは単なる例であり、失敗を想定した設計です (この場合、file_get_contents場合によってはファイルを読み取ることができなかったことも通知されるため、少しやり過ぎかもしれませんが、これらの事前条件チェックの方が価値がある場合があります。

これまでのコードをまとめて確認し、パラメーター変数を一番上に移動します。

$privateKeyPemFile  = "/public_html/apps/chrome/nsii.pem";
$zipFile            = "$name.zip";
$signatureAlgorithm = OPENSSL_ALGO_SHA1;

$privateKey = file_get_contents($privateKeyPemFile);
if (false === $privateKey) {
    throw new UnexpectedValueException(
        sprintf("Unable to read private key file %s", $privateKeyPemFile)
    );
}

$keyResource = openssl_pkey_get_private($privateKey);
if (false === $keyResource) {
    $buffer = sprintf(
        "Unable to parse and prepare private key (%d) %s; Openssl error(s):\n"
        , strlen($privateKey), $privateKeyPemFile
    );
    for (; $string = openssl_error_string(); $buffer .= $string, "\n") ;
    throw new UnexpectedValueException($buffer);
}

if (!is_readable($zipFile)) {
    throw new Exception(sprintf("Zipfile is not readable %s", $zipFile));
}
$zipContents = file_get_contents($zipFile);
if (false === $zipContents) {
    throw new UnexpectedValueException(
        sprintf("Failed to open zipfile %s", $zipFile)
    );
}

$result = openssl_sign($zipContents, $signature, $keyResource, $signatureAlgorithm);
if (!$result) {
    $buffer = sprintf(
        "Failed to sign zip contents (%d); Openssl error(s):\n"
        , strlen($zipContents)
    );
    for (; $string = openssl_error_string(); $buffer .= $string, "\n") ;
    throw new UnexpectedValueException($buffer);
}

変数を一番上に置くと、次のコードへの入力パラメーターを前もって確認し、より良い概要を得ることができます。

処理中の戻り値を確認することで、問題を早期に発見することができます。

openssll エラーを読み取ると、問題のトラブルシューティングに役立つ情報が得られます。

これに関するほとんどすべての情報は、PHP マニュアル内にあります。この回答は具体的な問題を解決しない可能性がありますが、問題自体とコードで発生する可能性のある将来の問題をより適切にトラブルシューティングするために必要なツールを提供する必要があります。

数行のコードが必要になるだけですが、コードははるかにうまく機能します。スクリプトを新しいサーバーにコピーすると、おそらく状況が異なり、盲目的に飛行したくないと想像してください。

楽しいデバッグ。ご覧のとおり、私はあなたのコードの前半しかカバーしていません。私はあなたのコードを実行していないことを知っておいてください。したがって、この例は最善を尽くして作成されていますが、シナリオですぐに機能するという保証はありません。しかし、私が実証したかったことは目に見えると思います。コードの残りの部分に同様のチェックを適用すると、解決策の半分である何が間違っているのかを正確に知ることができるはずです。

于 2012-09-29T22:01:59.217 に答える