将Python长整型/整型转换为固定大小字节数组
我正在尝试在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_bytes
和 int.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'