Как программно вычислить идентификатор расширения Chrome?

Я создаю автоматизированный процесс для создания расширений. Есть ли пример кода вычисления идентификатора расширения напрямую и полностью в обход взаимодействия с браузером?

(Я отвечаю на свой собственный вопрос ниже.)

3 ответов


я смог найти только связанную статью с фрагментом Ruby, и она доступна только в IA: http://web.archive.org/web/20120606044635/http://supercollider.dk/2010/01/calculating-chrome-extension-id-from-your-private-key-233

важно знать:

  1. это зависит от открытого ключа с кодировкой DER (RAW binary), а не от ключа с кодировкой PEM (хороший ASCII, сгенерированный base64-кодировкой ключа DER).
  2. расширения-идентификаторы основание-16, но кодируются с использованием [a-p] (называется "mpdecimal"), а не [0-9a-f].

используя открытый ключ в PEM-кодировке, выполните следующие действия:

  1. если ваш открытый ключ в формате PEM все еще имеет верхний и Нижний колонтитулы и разделен на несколько строк, переформатируйте его вручную, чтобы у вас была одна строка символов, которая исключает верхний и Нижний колонтитулы, и работает вместе, так что каждая строка ключа обертывается к следующему.
  2. Base64-декодировать общественность ключ для отображения открытого ключа в формате DER.
  3. создайте шестнадцатеричный дайджест SHA256 ключа в формате DER.
  4. возьмите первые 32 байта хэша. Остальное вам не понадобится.
  5. для каждого символа преобразуйте его в base-10 и добавьте код ASCII для 'a'.

для этого используется следующая процедура Python:

import hashlib
from base64 import b64decode

def build_id(pub_key_pem):
    pub_key_der = b64decode(pub_key_pem)
    sha = hashlib.sha256(pub_key_der).hexdigest()
    prefix = sha[:32]

    reencoded = ""
    ord_a = ord('a')
    for old_char in prefix:
        code = int(old_char, 16)
        new_char = chr(ord_a + code)

        reencoded += new_char

    return reencoded

def main():
    pub_key = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCjvF5pjuK8gRaw/2LoRYi37QqRd48B/FeO9yFtT6ueY84z/u0NrJ/xbPFc9OCGBi8RKIblVvcbY0ySGqdmp0QsUr/oXN0b06GL4iB8rMhlO082HhMzrClV8OKRJ+eJNhNBl8viwmtJs3MN0x9ljA4HQLaAPBA9a14IUKLjP0pWuwIDAQAB'

    id_ = build_id(pub_key)
    print(id_)

if __name__ == '__main__':
    main()

вы более чем приветствуется, чтобы проверить это против существующего расширения и его ID. Чтобы получить его PEM-формат открытый ключ:

  1. перейдите в список существующих расширений в Chrome. Возьмите идентификатор расширения одного из них.
  2. найдите каталог, в котором размещается расширение. В моем окне Windows 7 это: C:\Users\\AppData\Local\Google\Chrome\User Data\Default\Extensions\
  3. Возьмите ключ из манифеста.файл json в разделе "ключ". Поскольку ключ уже готов к декодированию base64, вы можете пропустить Шаг (1) процесса.

открытый ключ в пример из расширения "Chrome Reader". Его идентификатор расширения - "lojpenhmoajbiciapkjkiekmobleogjc".

Читайте также:

  1. Google Chrome-буквенно-цифровые хэши для идентификации расширений
  2. http://blog.roomanna.com/12-14-2010/getting-an-extensions-id

хороший и простой способ получить открытый ключ из .crx-файл с использованием python, так как chrome генерирует только частный .PEM key для вас. Открытый ключ хранится в .crx файл.

это основано на формат .crx файл найден здесьhttp://developer.chrome.com/extensions/crx.html

import struct
import hashlib
import string

def get_pub_key_from_crx(crx_file):
    with open(crx_file, 'rb') as f:
        data = f.read()
    header = struct.unpack('<4sIII', data[:16])
    pubkey = struct.unpack('<%ds' % header[2], data[16:16+header[2]])[0]
    return pubkey

def get_extension_id(crx_file):
    pubkey = get_pub_key_from_crx(crx_file)
    digest = hashlib.sha256(pubkey).hexdigest()

    trans = string.maketrans('0123456789abcdef', string.ascii_lowercase[:16])
    return string.translate(digest[:32], trans)

if __name__ == '__main__':
    import sys
    if len(sys.argv) != 2:
        print 'usage: %s crx_file' % sys.argv[0]

    print get_extension_id(sys.argv[1])

хотя это невозможно сделать "в обход взаимодействия с браузером", потому что вам все равно нужно сгенерировать .файл CRX с команду

chrome.exe --pack-extension=my_extension --pack-extension-key=my_extension.pem

начиная с Chrome 64, Chrome изменил формат пакета для расширений на CRX format формат файла, который поддерживает несколько подписей и явно объявляет свой идентификатор CRX. Извлекая идентификатор CRX из файла CRX₃ требуется парсинг буфера протокола.

вот небольшой скрипт python для извлечения идентификатора из файла CRX₃. Это решение должно использоваться только с доверенными файлами CRX₃ или в контекстах, где безопасность не является проблемой: в отличие от CRX₂, формат пакета не ограничивает того, что CRX и идентификатор файла CRX₃ заявляет. (На практике потребители файла (например, Chrome) будут накладывать на него ограничения, например, требовать, чтобы файл был подписан хотя бы одним ключом, который хэширует объявленный идентификатор CRX).

import binascii
import string
import struct
import sys

def decode(proto, data):
    index = 0
    length = len(data)
    msg = dict()
    while index < length:
        item = 128
        key = 0
        left = 0
        while item & 128:
            item = data[index]
            index += 1
            value = (item & 127) << left
            key += value
            left += 7
        field = key >> 3
        wire = key & 7
        if wire == 0:
            item = 128
            num = 0
            left = 0
            while item & 128:
                item = data[index]
                index += 1
                value = (item & 127) << left
                num += value
                left += 7
            continue
        elif wire == 1:
            index += 8
            continue
        elif wire == 2:
            item = 128
            _length = 0
            left = 0
            while item & 128:
                item = data[index]
                index += 1
                value = (item & 127) << left
                _length += value
                left += 7
            last = index
            index += _length
            item = data[last:index]
            if field not in proto:
                continue
            msg[proto[field]] = item
            continue
        elif wire == 5:
            index += 4
            continue
        raise ValueError(
            'invalid wire type: {wire}'.format(wire=wire)
        )
    return msg

def get_extension_id(crx_file):
    with open(crx_file, 'rb') as f:
      f.read(8); # 'Cr24'
      data = f.read(struct.unpack('<I', f.read(4))[0])
    crx3 = decode(
        {10000: "signed_header_data"},
        [ord(d) for d in data])
    signed_header = decode(
        {1: "crx_id"},
        crx3['signed_header_data'])
    return string.translate(
        binascii.hexlify(bytearray(signed_header['crx_id'])),
        string.maketrans('0123456789abcdef', string.ascii_lowercase[:16]))

def main():
    if len(sys.argv) != 2:
      print 'usage: %s crx_file' % sys.argv[0]
    else:
      print get_extension_id(sys.argv[1])

if __name__ == "__main__":
    main()

(спасибо https://github.com/thelinuxkid/python-protolite для скелета парсера protobuf.)