Google Chrome Extensions

CRX Package Format

CRX files are ZIP files with a special header and the .crx file extension.

Package header

The header contains the author's public key and the extension's signature. The signature is generated from the ZIP file using SHA-1 with the author's private key. The header requires a little-endian byte ordering with 4-byte alignment. The following table describes the fields of the .crx header in order:

FieldTypeLengthValueDescription
magic numberchar[]32 bitsCr24 Chrome requires this constant at the beginning of every .crx package.
versionunsigned int32 bits2 The version of the *.crx file format used (currently 2).
public key lengthunsigned int32 bits pubkey.length The length of the RSA public key in bytes.
signature lengthunsigned int32 bits sig.length The length of the signature in bytes.
public keybyte[]pubkey.length pubkey.contents The contents of the author's RSA public key, formatted as an X509 SubjectPublicKeyInfo block.
signaturebyte[]sig.length sig.contents The signature of the ZIP content using the author's private key. The signature is created using the RSA algorithm with the SHA-1 hash function.

Extension contents

The extension's ZIP file is appended to the *.crx package after the header. This should be the same ZIP file that the signature in the header was generated from.

Example

The following is an example hex dump from the beginning of a .crx file.

43 72 32 34   # "Cr24" -- the magic number
02 00 00 00   # 2 -- the crx format version number
A2 00 00 00   # 162 -- length of public key in bytes
80 00 00 00   # 128 -- length of signature in bytes
...........   # the contents of the public key
...........   # the contents of the signature
...........   # the contents of the zip file

Packaging scripts

Members of the community have written the following scripts to package .crx files.

Ruby

github: crxmake

Bash

#!/bin/bash -e
#
# Purpose: Pack a Chromium extension directory into crx format

if test $# -ne 2; then
  echo "Usage: crxmake.sh <extension dir> <pem path>"
  exit 1
fi

dir=$1
key=$2
name=$(basename "$dir")
crx="$name.crx"
pub="$name.pub"
sig="$name.sig"
zip="$name.zip"
trap 'rm -f "$pub" "$sig" "$zip"' EXIT

# zip up the crx dir
cwd=$(pwd -P)
(cd "$dir" && zip -qr -9 -X "$cwd/$zip" .)

# signature
openssl sha1 -sha1 -binary -sign "$key" < "$zip" > "$sig"

# public key
openssl rsa -pubout -outform DER < "$key" > "$pub" 2>/dev/null

byte_swap () {
  # Take "abcdefgh" and return it as "ghefcdab"
  echo "${1:6:2}${1:4:2}${1:2:2}${1:0:2}"
}

crmagic_hex="4372 3234" # Cr24
version_hex="0200 0000" # 2
pub_len_hex=$(byte_swap $(printf '%08x\n' $(ls -l "$pub" | awk '{print $5}')))
sig_len_hex=$(byte_swap $(printf '%08x\n' $(ls -l "$sig" | awk '{print $5}')))
(
  echo "$crmagic_hex $version_hex $pub_len_hex $sig_len_hex" | xxd -r -p
  cat "$pub" "$sig" "$zip"
) > "$crx"
echo "Wrote $crx"