获取 Python 中整数的大端字节序列
Get the big-endian byte sequence of integer in Python
基于此post:
我需要base64url-encode这两个数字的八位字节值:
n = 124692971944797177402996703053303877641609106436730124136075828918287037758927191447826707233876916396730936365584704201525802806009892366608834910101419219957891196104538322266555160652329444921468362525907130134965311064068870381940624996449410632960760491317833379253431879193412822078872504618021680609253
e = 65537
The "n" (modulus) parameter contains the modulus value for the RSA public key. It is represented as a Base64urlUInt-encoded value.
Note that implementers have found that some cryptographic libraries
prefix an extra zero-valued octet to the modulus representations they
return, for instance, returning 257 octets for a 2048-bit key, rather
than 256. Implementations using such libraries will need to take
care to omit the extra octet from the base64url-encoded
representation.
The "e" (exponent) parameter contains the exponent value for the RSA
public key. It is represented as a Base64urlUInt-encoded value.
For instance, when representing the value 65537, the octet sequence
to be base64url-encoded MUST consist of the three octets [1, 0, 1];
the resulting representation for this value is "AQAB".
例如,有效的编码应如下所示:https://www.googleapis.com/oauth2/v3/certs
¿如何在 Python 中执行此操作?
不幸的是 pack() 不支持那么大的数字,并且 int.to_bytes() 只在 Python 3 中支持,所以我们必须在编码之前自己打包它们。受到 this post 的启发,我首先通过转换为十六进制字符串找到了解决方案:
import math
import base64
def Base64urlUInt(n):
# fromhex() needs an even number of hex characters,
# so when converting our number to hex we need to give it an even
# length. (2 characters per byte, 8 bits per byte)
length = int(math.ceil(n.bit_length() / 8.0)) * 2
fmt = '%%0%dx' % length
packed = bytearray.fromhex(fmt % n)
return base64.urlsafe_b64encode(packed).rstrip('=')
导致:
n = 124692971944797177402996703053303877641609106436730124136075828918287037758927191447826707233876916396730936365584704201525802806009892366608834910101419219957891196104538322266555160652329444921468362525907130134965311064068870381940624996449410632960760491317833379253431879193412822078872504618021680609253
e = 65537
Base64urlUInt(n) == 'sZGVa39dSmJ5c7mbOsJZaq62MVjPD3xNPb-Aw3VJznk6piF5GGgdMoQmAjNmANVBBpPUyQU2SEHgXQvp6j52E662umdV2xU-1ETzn2dW23jtdTFPHRG4BFZz7m14MXX9i0QqgWVnTRy-DD5VITkFZvBqCEzWjT_y47DYD2Dod-U'
Base64urlUInt(e) == 'AQAB'
在搜索了解决此问题的最佳方法后,使用 pyjwkest 似乎比创建我自己的函数更好。
pip install pyjwkest
那么我们就用long_to_base64
这个函数
>>> from jwkest import long_to_base64
>>> long_to_base64(65537)
'AQAB'
这是任务的 Python 代码的不同部分,摘自 rsalette
def bytes_to_int(data):
"""Convert bytes to an integer"""
hexy = binascii.hexlify(data)
hexy = b'0'*(len(hexy)%2) + hexy
return int(hexy, 16)
def b64_to_int(data):
"""Convert urlsafe_b64encode(data) to an integer"""
return bytes_to_int(urlsafe_b64decode(data))
def int_to_bytes(integer):
hexy = as_binary('%x' % integer)
hexy = b'0'*(len(hexy)%2) + hexy
data = binascii.unhexlify(hexy)
return data
def int_to_b64(integer):
"""Convert an integer to urlsafe_b64encode() data"""
return urlsafe_b64encode(int_to_bytes(integer))
def as_binary(text):
return text.encode('latin1')
基于此post:
我需要base64url-encode这两个数字的八位字节值:
n = 124692971944797177402996703053303877641609106436730124136075828918287037758927191447826707233876916396730936365584704201525802806009892366608834910101419219957891196104538322266555160652329444921468362525907130134965311064068870381940624996449410632960760491317833379253431879193412822078872504618021680609253
e = 65537
The "n" (modulus) parameter contains the modulus value for the RSA public key. It is represented as a Base64urlUInt-encoded value. Note that implementers have found that some cryptographic libraries prefix an extra zero-valued octet to the modulus representations they return, for instance, returning 257 octets for a 2048-bit key, rather than 256. Implementations using such libraries will need to take care to omit the extra octet from the base64url-encoded representation.
The "e" (exponent) parameter contains the exponent value for the RSA public key. It is represented as a Base64urlUInt-encoded value. For instance, when representing the value 65537, the octet sequence to be base64url-encoded MUST consist of the three octets [1, 0, 1]; the resulting representation for this value is "AQAB".
例如,有效的编码应如下所示:https://www.googleapis.com/oauth2/v3/certs
¿如何在 Python 中执行此操作?
不幸的是 pack() 不支持那么大的数字,并且 int.to_bytes() 只在 Python 3 中支持,所以我们必须在编码之前自己打包它们。受到 this post 的启发,我首先通过转换为十六进制字符串找到了解决方案:
import math
import base64
def Base64urlUInt(n):
# fromhex() needs an even number of hex characters,
# so when converting our number to hex we need to give it an even
# length. (2 characters per byte, 8 bits per byte)
length = int(math.ceil(n.bit_length() / 8.0)) * 2
fmt = '%%0%dx' % length
packed = bytearray.fromhex(fmt % n)
return base64.urlsafe_b64encode(packed).rstrip('=')
导致:
n = 124692971944797177402996703053303877641609106436730124136075828918287037758927191447826707233876916396730936365584704201525802806009892366608834910101419219957891196104538322266555160652329444921468362525907130134965311064068870381940624996449410632960760491317833379253431879193412822078872504618021680609253
e = 65537
Base64urlUInt(n) == 'sZGVa39dSmJ5c7mbOsJZaq62MVjPD3xNPb-Aw3VJznk6piF5GGgdMoQmAjNmANVBBpPUyQU2SEHgXQvp6j52E662umdV2xU-1ETzn2dW23jtdTFPHRG4BFZz7m14MXX9i0QqgWVnTRy-DD5VITkFZvBqCEzWjT_y47DYD2Dod-U'
Base64urlUInt(e) == 'AQAB'
在搜索了解决此问题的最佳方法后,使用 pyjwkest 似乎比创建我自己的函数更好。
pip install pyjwkest
那么我们就用long_to_base64
这个函数
>>> from jwkest import long_to_base64
>>> long_to_base64(65537)
'AQAB'
这是任务的 Python 代码的不同部分,摘自 rsalette
def bytes_to_int(data):
"""Convert bytes to an integer"""
hexy = binascii.hexlify(data)
hexy = b'0'*(len(hexy)%2) + hexy
return int(hexy, 16)
def b64_to_int(data):
"""Convert urlsafe_b64encode(data) to an integer"""
return bytes_to_int(urlsafe_b64decode(data))
def int_to_bytes(integer):
hexy = as_binary('%x' % integer)
hexy = b'0'*(len(hexy)%2) + hexy
data = binascii.unhexlify(hexy)
return data
def int_to_b64(integer):
"""Convert an integer to urlsafe_b64encode() data"""
return urlsafe_b64encode(int_to_bytes(integer))
def as_binary(text):
return text.encode('latin1')