How to programmatically calculate Chrome extension ID?

后端 未结 3 655
醉话见心
醉话见心 2020-12-29 14:08

I\'m building an automated process to produce extensions. Is there a code example of calculating the extension-ID directly and entirely bypassing interaction with the browse

相关标签:
3条回答
  • 2020-12-29 14:36

    Starting with Chrome 64, Chrome changed the package format for extensions to the CRX₃ file format, which supports multiple signatures and explicitly declares its CRX ID. Extracting the CRX ID from a CRX₃ file requires parsing a protocol buffer.

    Here is a small python script for extracting the ID from a CRX₃ file. This solution should only be used with trusted CRX₃ files or in contexts where security is not a concern: unlike CRX₂, the package format does not restrict what CRX ID a CRX₃ file declares. (In practice, consumers of the file (i.e. Chrome) will place restrictions upon it, such as requiring the file to be signed with at least one key that hashes to the declared CRX ID).

    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\3\0\0\0'
          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()
    

    (Thanks to https://github.com/thelinuxkid/python-protolite for the protobuf parser skeleton.)

    0 讨论(0)
  • 2020-12-29 14:46

    I was only able to find a related article with a Ruby fragment, and it's only available in the IA: http://web.archive.org/web/20120606044635/http://supercollider.dk/2010/01/calculating-chrome-extension-id-from-your-private-key-233

    Important to know:

    1. This depends on a DER-encoded public key (raw binary), not a PEM-encoded key (nice ASCII generated by base64-encoding the DER key).
    2. The extension-IDs are base-16, but are encoded using [a-p] (called "mpdecimal"), rather than [0-9a-f].

    Using a PEM-encoded public key, follow the following steps:

    1. If your PEM-formatted public-key still has the header and footer and is split into multiple lines, reformat it by hand so that you have a single string of characters that excludes the header and footer, and runs together such that every line of the key wraps to the next.
    2. Base64-decode the public key to render a DER-formatted public-key.
    3. Generate a SHA256 hex-digest of the DER-formatted key.
    4. Take the first 32-bytes of the hash. You will not need the rest.
    5. For each character, convert it to base-10, and add the ASCII code for 'a'.

    The following is a Python routine to do this:

    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()
    

    You're more than welcome to test this against an existing extension and its ID. To retrieve its PEM-formatted public-key:

    1. Go into the list of your existing extensions in Chrome. Grab the extension-ID of one.
    2. Find the directory where the extension is hosted. On my Windows 7 box, it is: C:\Users<username>\AppData\Local\Google\Chrome\User Data\Default\Extensions<extension ID>
    3. Grab the public-key from the manifest.json file under "key". Since the key is already ready to be base64-decoded, you can skip step (1) of the process.

    The public-key in the example is from the "Chrome Reader" extension. Its extension ID is "lojpenhmoajbiciapkjkiekmobleogjc".

    See also:

    1. Google Chrome - Alphanumeric hashes to identify extensions
    2. http://blog.roomanna.com/12-14-2010/getting-an-extensions-id
    0 讨论(0)
  • 2020-12-29 14:58

    A nice and simple way to get the public key from the .crx file using python, since chrome only generates the private .pem key for you. The public key is actually stored in the .crx file.

    This is based on the format of the .crx file found here 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])
    

    Although this isn't possible to do "bypassing interaction with the browser", because you still need to generate the .crx file with a command like

    chrome.exe --pack-extension=my_extension --pack-extension-key=my_extension.pem
    
    0 讨论(0)
提交回复
热议问题