Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generate Chrome .crx from PHP

I want to generate a Chrome extension (Chrome theme) from PHP. My PHP script generates a zip file (download.zip). To convert it to a .crx package, it needs to add headers, including a public key and a signature.

I saw this answer, but you need a .pem file that generates a .pub file. I'm on a shared hosting so exec() won't work (to convert a .pem to a .pub). There is no need to have a .pem file, it only has to use it once downloading (no updating needed).

Then I saw this comment that explains you can generate the private and public keys. Combining the two scripts won't work (see code).

How can I generate a keypair and use it to sign a chrome .crx package with PHP?

This code fails (CRX_SIGNATURE_VERIFICATION_INITALIZATION_FAILED):

// Create the keypair
$res=openssl_pkey_new();

// Get private key
openssl_pkey_export($res, $pk);

// Get public key
$key=openssl_pkey_get_details($res);
$key=$key["key"];

# make a SHA1 signature using our private key
openssl_sign(file_get_contents('download.zip'), $signature, $pk, 'sha1');

# decode the public key
$key = base64_decode($key);

# .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('download.zip')); // package contents, zipped
fclose($fh);
like image 380
Bart Langelaan Avatar asked Dec 16 '12 14:12

Bart Langelaan


1 Answers

You were using openssl_pkey_export wrong and you haven't removed

-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----

from public key string before decoding it. I figured this out by looking at length of public key and signature. First one should be 161 and second one should be 128 bytes long (source):

A2 00 00 00   # 162 -- length of public key in bytes
80 00 00 00   # 128 -- length of signature in bytes

Here is the fixed code (PHP 5.4):

$pk=file_get_contents('pk.pem');

$priv = openssl_pkey_get_private($pk);
$pub = openssl_pkey_get_details($priv)['key'];

# make a SHA1 signature using our private key
openssl_sign(file_get_contents('download.zip'), $signature, $priv, OPENSSL_ALGO_SHA1);

# geting rid of -----BEGIN/END PUBLIC KEY-----
# you can probably do it better using preg_match_all / explode(PHP_EOL, $pub) etc.
$pub = trim(explode('-----',$pub)[2]);

# decode the public key
$pub = base64_decode($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($pub)));            // public key length
fwrite($fh, pack('V', strlen($signature)));      // signature length
fwrite($fh, $pub);                               // public key
fwrite($fh, $signature);                         // signature
fwrite($fh, file_get_contents('download.zip')); // package contents, zipped
fclose($fh);
like image 178
Konrad Dzwinel Avatar answered Oct 23 '22 12:10

Konrad Dzwinel