将Python长整型/整型转换为固定大小字节数组

62 投票
10 回答
145141 浏览
提问于 2025-04-17 09:32

我正在尝试在Python中实现RC4加密和DH密钥交换。问题是我不知道如何把密钥交换中得到的Python长整型/整数转换成RC4实现所需的字节数组。有没有简单的方法可以把长整型转换成所需长度的字节数组呢?

更新:我忘了提到,我处理的数字是768位的无符号整数。

10 个回答

32

大家都把这个问题想得太复杂了:

some_int = <256 bit integer>
some_bytes = some_int.to_bytes(32, sys.byteorder)
my_bytearray = bytearray(some_bytes)

你只需要知道你想转换的字节数。在我的使用场景中,通常我只在加密时才用到这么大的数字,这时候我还得考虑模运算等等,所以我觉得知道返回的最大字节数并不是个大问题。

既然你是在做768位的数学运算,那么参数就不是32,而是96。

65

在Python 3.2及以后的版本中,你可以使用 int.to_bytesint.from_bytes 这两个功能。具体的介绍可以查看这个链接:https://docs.python.org/3/library/stdtypes.html#int.to_bytes

21

我没有做过任何性能测试,但这个方法对我来说“有效”。

简单来说:使用 '%x' % val,然后对结果使用 unhexlify。不过,细节很重要,因为 unhexlify 需要偶数个十六进制数字,而 %x 并不能保证这一点。具体情况可以查看文档说明,还有里面的详细注释。

from binascii import unhexlify

def long_to_bytes (val, endianness='big'):
    """
    Use :ref:`string formatting` and :func:`~binascii.unhexlify` to
    convert ``val``, a :func:`long`, to a byte :func:`str`.

    :param long val: The value to pack

    :param str endianness: The endianness of the result. ``'big'`` for
      big-endian, ``'little'`` for little-endian.

    If you want byte- and word-ordering to differ, you're on your own.

    Using :ref:`string formatting` lets us use Python's C innards.
    """

    # one (1) hex digit per four (4) bits
    width = val.bit_length()

    # unhexlify wants an even multiple of eight (8) bits, but we don't
    # want more digits than we need (hence the ternary-ish 'or')
    width += 8 - ((width % 8) or 8)

    # format width specifier: four (4) bits per hex digit
    fmt = '%%0%dx' % (width // 4)

    # prepend zero (0) to the width, to zero-pad the output
    s = unhexlify(fmt % val)

    if endianness == 'little':
        # see http://stackoverflow.com/a/931095/309233
        s = s[::-1]

    return s

...还有我的 nosetest 单元测试 ;-)

class TestHelpers (object):
    def test_long_to_bytes_big_endian_small_even (self):
        s = long_to_bytes(0x42)
        assert s == '\x42'

        s = long_to_bytes(0xFF)
        assert s == '\xff'

    def test_long_to_bytes_big_endian_small_odd (self):
        s = long_to_bytes(0x1FF)
        assert s == '\x01\xff'

        s = long_to_bytes(0x201FF)
        assert s == '\x02\x01\xff'

    def test_long_to_bytes_big_endian_large_even (self):
        s = long_to_bytes(0xab23456c8901234567)
        assert s == '\xab\x23\x45\x6c\x89\x01\x23\x45\x67'

    def test_long_to_bytes_big_endian_large_odd (self):
        s = long_to_bytes(0x12345678901234567)
        assert s == '\x01\x23\x45\x67\x89\x01\x23\x45\x67'

    def test_long_to_bytes_little_endian_small_even (self):
        s = long_to_bytes(0x42, 'little')
        assert s == '\x42'

        s = long_to_bytes(0xFF, 'little')
        assert s == '\xff'

    def test_long_to_bytes_little_endian_small_odd (self):
        s = long_to_bytes(0x1FF, 'little')
        assert s == '\xff\x01'

        s = long_to_bytes(0x201FF, 'little')
        assert s == '\xff\x01\x02'

    def test_long_to_bytes_little_endian_large_even (self):
        s = long_to_bytes(0xab23456c8901234567, 'little')
        assert s == '\x67\x45\x23\x01\x89\x6c\x45\x23\xab'

    def test_long_to_bytes_little_endian_large_odd (self):
        s = long_to_bytes(0x12345678901234567, 'little')
        assert s == '\x67\x45\x23\x01\x89\x67\x45\x23\x01'

撰写回答