I need help using RSA encryption and decryption in Python.
I tried to generate a public key and a private key by using RSA 2048, then send the public key as hex to a destination. However, I faced a problem, the generated public key size is greater than 2048 bit. I used the following script. Could I know why the key size is greater than 2048 bit?
import Crypto
from Crypto.PublicKey import RSA
key = RSA.generate(2048)
binPrivKey = key.exportKey('DER')
binPubKey = key.publickey().exportKey('DER')
print(binPubKey.encode('hex'))
If you want to export the key for use somewhere else, you will probably find it easier to export the key in PEM format as follows:
>>> print(key.publickey().exportKey('PEM'))
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtd2o9NY7P9CvXzECu4Ky
tieqYGAkOFrvuRnZpf3VP4VK0XMFSXM8/i5c0Q3Ml44If/zqVo/cXAO85YqV8ZtJ
YRtK9wcEr5epTX6iahxqgObgjFAbIRd6+we6znxBy+OG0JfEGn/GSBoq9g8mcr2e
HhKflp3B57X3+Qn1EbIYDFThWxy4HUZXh64LJiA5s0yeYzlGyjcC6R/Q59/CsyKP
K5LdcRp+CUrHfqwimiFUEZ+KNOob1klAyY4UKX9CI1AnWwZuSrmtbH+11Cfwgnnk
5RpqDvFNB30qsuD0elE+9zLOrq0jGicEoPtYAC3Z7phoODoR5vHbJ0R38qGItLMl
/wIDAQAB
-----END PUBLIC KEY-----
The DER format contains the same data, but in binary form, which is perhaps a little less portable.
The key length isn't 2048 bits because a public key contains not only the modulus n, but also the encryption exponent e.
If you just want the 2048-bit modulus n, then you can extract it as follows:
>>> print(key.n)
22958437811749378126735904957386766172644032831528249830706401935201456098524775
93935742531467773637499977046456570312080938678104306767641814358663672099815985
37166257748568890906635464134344070390567919827141645499361303936386291407244786
88192939984906393278409502460458733268776367836168349094440408475953441252058796
28391483565417017898863634275114447933217938009351306832376849011143622553495660
63424041991601059614183085270921567421339154500925080655811214971889251644612159
17495238196068931081086234165571014450595993262432428425126883651547622718096951
337250550253777137307186332200705951701212904813212411391
And yes, this is a 2048-bit value:
>>> print(key.n.bit_length())
2048
An RSA public key consists of two components: the modulus and the public exponent. The size of the modulus determines the key size. It is therefore 2048 bits if that's the size given to the key pair generator. The public exponent can be any value and could be up to 2048 bits as well. However, it is usually small. Nowadays it is commonly set to the value 65537, which is 010001 in hexadecimals. It is a special number called the fifth prime of Fermat, usually indicated by "F4" (zero based indexation).
The public key structure should contain both components. The encoded key size is generally larger than the key size for any asymmetric primitive such as RSA. Besides that is may contain overhead (to identify the location of the modulus and exponent) and information about the key itself (for instance an OID that indicates that it is indeed an RSA public key).
To know more about this you could take a look at the PKCS#1 and X.509 certificate specifications. The latter specifies a structure called SubjectPublicKeyInfo which is PEM encoded in the answer of squeamish ossifrage. You can parse it online here.
SEQUENCE (2 elem)
SEQUENCE (2 elem)
OBJECT IDENTIFIER1.2.840.113549.1.1.1 rsaEncryption (PKCS #1)
NULL
BIT STRING (1 elem)
SEQUENCE (2 elem)
INTEGER (2048 bit) 229584378117493781267359049573867661726440328315282498307064019352014…
INTEGER 65537
Here the first number is the modulus and the second is the public exponent.
So in short there is a difference between key size, encoded key size and key strength.
Notes:
An RSA key pair of 2048 bits only provides a key strength of 112 bits (i.e. 112 bits of security), while AES-128 provides about 127 bits of security. In general you should try to use a 3072 bit key instead.
The private key often contains the parameters for the Chinese Remainder Theorem and the public exponent on top of the 2048 bit modulus and 2048 bit private exponent, so it will be even larger.
Related
I'm having trouble loading an RSA512 public key from a .bin file into python. The issue mainly stems from the fact that I don't know what format the key is stored in. This is the only description of the file I was given.
"key.bin - Raw binary bytes of RSA 512 bit public key and exponent. Used to verify signature of incoming
packets."
I don't know if this is helpful but here are bytes printed in python of the .bin file.
9902c4a66b1ff76392919e7bbc35d51a5128b9da03e131b489d5ed01c1d075fc4c139a9952e9a3b040d984219a4aef0d421f6b8f9c79e1c3c35a218ecba54dc9010001
The goal of the actual challenge is to build a udp server that verifies the digital signature and integrity of an incoming packet. Currently i'm using python 2.7 with the cryptography library. Documentation can be found below.
https://cryptography.io/en/latest/hazmat/primitives/asymmetric/rsa/?highlight=rsa%20512
I have already tried the code below but I get the same error for the first two formats and a slightly different one for the third.
with open("key.bin", "rb") as key_file:
private_key = serialization.load_der_public_key(key_file.read(), backend=default_backend())
ValueError: Could not deserialize key data.
with open("key.bin", "rb") as key_file:
private_key = serialization.load_pem_public_key(key_file.read(), backend=default_backend())
ValueError: Could not deserialize key data.
with open("key.bin", "rb") as key_file:
private_key = serialization.load_ssh_public_key(key_file.read(), backend=default_backend())
ValueError: Key is not in the proper format or contains extra data.
Also the hashing algorithm used for verification is SHA256 but that is probably irrelevant.
key.bin - Raw binary bytes of RSA 512 bit public key and exponent. Used to verify signature of incoming packets.
In a RSA-512 key, the modulus is a 512-bit number, which fits in 64 bytes or 128 hexadecimal digits. Your file is represented by 134 hex digits, so it's likely that 128 of these digits are the modulus and the rest is the public exponent and possibly metadata.
The public exponent is almost always 3 or 65537=0x010001. Given that key.bin ends with 010001 in hex, a reasonable guess is that those last 3 bytes are the public exponent, and the first 64 bytes are the modulus.
with open("key.bin", "rb") as key_file:
n_bytes = key_file.read(64)
e_bytes = key_file.read(3)
You now need to figure out whether the encoding is little-endian or big-endian. You can't tell from the public exponent because it's palindromic. So try both possibilities:
n = int(n_bytes.encode('hex'), 16)
or
n = int(reversed(n_bytes).encode('hex'), 16)
Since you have the key in an ad hoc format, rather than a standard format that is used in real life, you're probably meant to use arithmetic primitives rather than a cryptography library to work with the key.
Your key is not encoded in a known standard. You need to extract the modulus and exponent, and then build the public key out of them.
The modulus defines the RSA key size and is therefore 512 bits or 64 bytes as unsigned big endian value. The public exponent may have any size, but is usually small. The most used exponent value is 010001 in hexadecimals, which is the fifth prime of Fermat (also called F4, zero based index). Better however to simply get the first 64 bytes and assume the rest encodes is the public exponent.
So you can use RSAPublicNumbers to create the values from the modulus n and exponent e. Trick is to make sure that you create the modulus as positive value instead of a negative value.
Let's say that data is the binary data read from the file. Then you can get the public key in the following way.
You may want to use 'little' instead of 'big' if the following doesn't work (big endian is the RSA default, but you never know). However, in your case the little endian value is dividable by e.g. 11, so that's not a likely modulus value (the prime values should be close to half the key size in bits to be secure).
modsize = 512 // 8
modBytes = data[slice(0, modsize)]
mod = int.from_bytes(modBytes, byteorder='big')
expBytes = data[slice(modsize, None)]
exp = int.from_bytes(expBytes, byteorder='big')
pubkey = RSAPublicNumbers(exp, mod).public_key(default_backend())
Note that from_bytes has only been added in Python 3.2. RSAPublicNumbers is a bit weird in the sense that it takes the exponent parameter before the modulus. Every other API that I've seen takes the modulus before the exponent.
I'm currently using Fernet encryption which uses AES 128 keys. However my client requires using AES 256. I'm not very familiar with cryptography but here is what I understood so far.
Fernet needs a 256 bits key that is splitted in half. First half is the signing key, second one is the encryption key. As they are 128 bits long it is AES 128.
Would it be enough to double the input key and modify the implementation like below to get AES 256 ?
class Fernet(object):
def __init__(self, key, backend=None):
if backend is None:
backend = default_backend()
key = base64.urlsafe_b64decode(key) # Here 512 bits long instead of 256
self._signing_key = key[:16] # double this
self._encryption_key = key[16:] # double this
self._backend = backend
Yes, you could double the binary input, the input before the key was base 64 encoded. If the result is 256 bit secure depends on how the key is generated. So yes, it is possible to double the size check on the key, but that doesn't say much. If the input key material is 512 bits with a security level of 512 bits then yes, then splitting the key is fine.
Personally I would recommend (and I have recommended it in the past to Fernet) to use HKDF to derive the two keys instead of just splitting the key in two. I cannot see how the key is generated, but if it is generated by PBKDF2 - which Fernet does use to create keys from passwords - then PBKDF2 may require double the amount of work to generate 512 bits, while the attacker will only have to generate 256 bits to perform an attack (and therefore perform half of the work).
Note that using base64 encoding is not great for keys as strings are hard to delete from memory in most runtimes; it's much better if the keys are stored in a key store.
The Fernet Specification is to use AES-128. If you modify the algorithm to use AES-256, then you would no longer be using Fernet. If it's a requirement that you use both Fernet and AES-256, I would recommend encrypting your payload independently using AES-256 and then applying the Fernet algorithm to the result. This essentially encrypts it again using AES-128.
I know how to obtain RSA modulus and exponent from public key using openssl, but now i tried it to do with Python. I've seen this and followed steps.
Suppose this is the public key:
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCdZGziIrJOlRomzh7M9qzo4ibw
QmwORcVDI0dsfUICLUVRdUN+MJ8ELd55NKsfYy4dZodWX7AmdN02zm1Gk5V5i2Vw
GVWE205u7DhtRe85W1oR9WTsMact5wuqU6okJd2GKrEGotgd9iuAJm90N6TDeDZ4
KHEvVEE1yTyvrxQgkwIDAQAB
-----END PUBLIC KEY-----
First, public key is decoded from base64:
import base64
bytearray = base64.b64decode("""MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCdZGziIrJOlRomzh7M9qzo4ibw
QmwORcVDI0dsfUICLUVRdUN+MJ8ELd55NKsfYy4dZodWX7AmdN02zm1Gk5V5i2Vw
GVWE205u7DhtRe85W1oR9WTsMact5wuqU6okJd2GKrEGotgd9iuAJm90N6TDeDZ4
KHEvVEE1yTyvrxQgkwIDAQAB""")
print(bytearray)
output:
0\x81\x9f0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x81\x8d\x000\x81\x89\x02\x81\x81\x00\x9ddl\xe2"\xb2N\x95\x1a&\xce\x1e\xcc\xf6\xac\xe8\xe2&\xf0Bl\x0eE\xc5C#Gl}B\x02-EQuC~0\x9f\x04-\xdey4\xab\x1fc.\x1df\x87V_\xb0&t\xdd6\xcemF\x93\x95y\x8bep\x19U\x84\xdbNn\xec8mE\xef9[Z\x11\xf5d\xec1\xa7-\xe7\x0b\xaaS\xaa$%\xdd\x86*\xb1\x06\xa2\xd8\x1d\xf6+\x80&ot7\xa4\xc3x6x(q/TA5\xc9<\xaf\xaf\x14 \x93\x02\x03\x01\x00\x01
Then, byte array is converted to base 16 (hexadecimal) string:
bytearray.encode('hex')
output:
30819f300d06092a864886f70d010101050003818d00308189028181009d646ce222b24e951a26ce1eccf6ace8e226f0426c0e45c54323476c7d42022d455175437e309f042dde7934ab1f632e1d6687565fb02674dd36ce6d469395798b6570195584db4e6eec386d45ef395b5a11f564ec31a72de70baa53aa2425dd862ab106a2d81df62b80266f7437a4c378367828712f544135c93cafaf1420930203010001
and for the design, let's add ":" between every two character in the string:
':'.join([ i+j for i,j in zip(bytearray[::2],bytearray[1::2])])
output:
30:81:9f:30:0d:06:09:2a:86:48:86:f7:0d:01:01:01:05:00:03:81:8d:00:30:81:89:02:81:81:00:9d:64:6c:e2:22:b2:4e:95:1a:26:ce:1e:cc:f6:ac:e8:e2:26:f0:42:6c:0e:45:c5:43:23:47:6c:7d:42:02:2d:45:51:75:43:7e:30:9f:04:2d:de:79:34:ab:1f:63:2e:1d:66:87:56:5f:b0:26:74:dd:36:ce:6d:46:93:95:79:8b:65:70:19:55:84:db:4e:6e:ec:38:6d:45:ef:39:5b:5a:11:f5:64:ec:31:a7:2d:e7:0b:aa:53:aa:24:25:dd:86:2a:b1:06:a2:d8:1d:f6:2b:80:26:6f:74:37:a4:c3:78:36:78:28:71:2f:54:41:35:c9:3c:af:af:14:20:93:02:03:01:00:01
But, unfortunately, i couldn't understand the last step.
I know that output includes both exponent and modulus, but there are other characters to filter out (since they can't be converted back to base 10).
I also known that hex representation above is based on ASN.1 syntax, so there should be some specific offset in hex representation where these two values will appear.
What needs to be done to obtain modulus and exponent from this result?
Thanks!
The public key is in the ASN.1 SubjectPublicKeyInfo format, also known in the Java world as an X509EncodedKeySpec. Many crypt packages can import such an object directly.
For example, using PyCrypto and Cryptography.io, the following snippet
from Crypto.PublicKey import RSA
key_encoded='''-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCdZGziIrJOlRomzh7M9qzo4ibw
QmwORcVDI0dsfUICLUVRdUN+MJ8ELd55NKsfYy4dZodWX7AmdN02zm1Gk5V5i2Vw
GVWE205u7DhtRe85W1oR9WTsMact5wuqU6okJd2GKrEGotgd9iuAJm90N6TDeDZ4
KHEvVEE1yTyvrxQgkwIDAQAB
-----END PUBLIC KEY-----'''
pubkey = RSA.importKey(key_encoded)
print(pubkey.n)
print(pubkey.e)
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
pubkey2 = serialization.load_pem_public_key(
key_encoded.encode('ascii'),
backend=default_backend()
)
print(pubkey2.public_numbers().n)
print(pubkey2.public_numbers().e)
produces
110524622184298189406696366981362867320131527048683492811128204661745388510505145389459518039217549444918405620726988722254633562452576638635488354260221598432448974859895979017211032905988949400704082939941050902513120244660937339078367607684436944094809985731012813959774525636937965082155868293686780764307
65537
110524622184298189406696366981362867320131527048683492811128204661745388510505145389459518039217549444918405620726988722254633562452576638635488354260221598432448974859895979017211032905988949400704082939941050902513120244660937339078367607684436944094809985731012813959774525636937965082155868293686780764307
65537
I am not a expert in this but i think that...
Last 6 bytes in the hex representation are used to calculate the exponent.
'02'->this byte used to declare the exponent type i.e INTEGER.
'03'->this byte tells that it is 3 byte long number.
'01:00:01'-> these 3 bytes represents the exponent.
When I test RSA encryption on my laptop using PyCrypto (Archlinux, package: python-crypto/python2-crypto), I used a 1024 key generated by RSA module to encrypt a random data, and it produced a 127 bytes length cipher.
A simple code following: (I got the values when debugging)
from Crypto.PublicKey import RSA
pubkey = b'-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDq+qbaMTZtPH3LuXLrAn37YGzc\ngrL7ieTILtkXTl5PIozJUQZ06bQXr/uS+FtvYNSvaT53ZpSyKmVmWtoX7lFzA6FW\nsILFTgFUDNRnPIQv1rQb16wi694rKPRe1uIr8/hthXtTec8b2aJovizQOlkXY0Pq\nZohNGofi02xlUD8KsQIDAQAB\n-----END PUBLIC KEY-----'
prikey = b'-----BEGIN RSA PRIVATE KEY-----\nMIICXgIBAAKBgQDq+qbaMTZtPH3LuXLrAn37YGzcgrL7ieTILtkXTl5PIozJUQZ0\n6bQXr/uS+FtvYNSvaT53ZpSyKmVmWtoX7lFzA6FWsILFTgFUDNRnPIQv1rQb16wi\n694rKPRe1uIr8/hthXtTec8b2aJovizQOlkXY0PqZohNGofi02xlUD8KsQIDAQAB\nAoGBAMkKEI0ng8Br+9i8XqTQ6gaTVjBHpmhtbw8SfexhwXCFR9zJ9PM8LDgD+gKh\neGFPgEhfi/FOE7Rnb3/mBShqXsWbqz7STJ05GOxtKo+L1z5K7X4E9WmVjIEVU46I\nhF43LJQvoDjQRbZh2cUMSYUR8+LqJJd6MFdhLJhEIf+LhCbBAkEA71lRBiSwZH/8\nsaUE4qZ/vxkS65czBcWLSCgn+7D/kvunX1hxqi3zTxMn4gyluw3IICzvLFgdDG6f\nUZk23aDcyQJBAPtTgvi4lYAIoeh6Xx8NZxroVNVBlP9BzJTBCcnX1Ym0aC/p+6n8\n7Lu9bkKk/hb0r7Oy76wzxObWv9uvRQNp+qkCQQCoOy8oEkGpYgxLEKIObNj9iLIz\nxWKne+IaJZ902UPKG/fYnGHIK+QIgH5X9GvIvjcb5nl1wbkpM9fnkrltrdOBAkBe\n7LbuHEGTHy+P8BBXWSeVOSU5etC87GxJzvNUginMHhCv8C82kCoV6sFneIvjvb1T\nIQV3RAJdscS7Q+LMHE4pAkEAzp2o8+2+9QJwzkpxGyNjJ7ZECQsZIb7MOH7LYhX0\ncnwffXFt4ttcwbyX2SdhCVPBDkczkJkOzcnEqtjoWt+dBw==\n-----END RSA PRIVATE KEY-----'
pub = RSA.importKey(pubkey)
data = b'\xc9\xc5\xa9\x1b\xc2\x0f\x05\xf0\xe3\xe1W\x9d\x94b\xc6 '
cipher = pub.encrypt(data, 0)[0]
print(len(cipher))
This will print 127 (normally it would be 128 for 1024 bits key), and I don't know why.
You are not using a correct encryption scheme. From the documentation:
Even though you may choose to directly use the methods of an RSA key object to perform the primitive cryptographic operations (e.g. _RSAobj.encrypt), it is recommended to use one of the standardized schemes instead (like Crypto.Cipher.PKCS1_v1_5 or Crypto.Signature.PKCS1_v1_5).
although nowadays the more modern/safe Crypto.Cipher.PKCS1_OAEP should be preferred over Crypto.Cipher.PKCS1_v1_5.
If you use one of these schemes then the output will always be 128 bytes. The reason for that is that PKCS#1 specifies a function called I2OSP, which converts the result of the modular exponentiation (which is a number bounded by the modulus/the key size) to a static number of octets, the key size to be exact.
The output of the direct encrypt function is what is called raw or textbook RSA: just modular exponentiation. This will just return the number, which may have leading zero bits. How many depends on chance, (somewhat) on the value of the modulus and if signed or unsigned encoding is used.
Here is an example:
p = 11, q = 5, N = p*q = 55, choose encryption exponent e = 3, so d = e^-1 mod (p-1)(q-1) = 27.
If I want encrypt x=13, x^e=13^3=52mod55.
I understand how to encrypt a number which is less than N, but how to encrypt a number which is larger than N?
I know if X is larger than N, we should decompose X into several parts and encrypt them respectively, but I don't know how RSA decompose it?
Optional question:
How to encrypt a file with RSA on IOS or python?
You don't use RSA to encrypt long messages.
The correct approach is using hybrid encryption instead:
Generate a random AES key, encrypt the actual data with AES. Preferably using an authenticated mode like AES-GCM.
Encrypt the AES key with RSA. This key (126 to 256 bits) is small enough to fit within one RSA block. For example using small and thus weak 1024 bit RSA keys you have 500-700 bits for the actual data (the rest is consumed by the padding).
The ciphertext consists of both the RSA encrypted AES key and the AES encrypted file.
It's essential for security to apply padding here, namely OAEP. Most other paddings, including the popular PKCS#1v1.5 padding are not secure.
Don't try to split the file into blocks which you encrypt with RSA. There are no standard ways for doing this, because it's a bad idea.
The RSA algorithm does not handle decomposition of the message at all. It just encrypts fixed-size integers. This kind of encryption algorithms is called a block cipher, because it encrypts messages in fixed-size "blocks".
How the blocks are obtained is generally not specified by the block-cipher itself. So, you have to decide how to split the message. One of the possible ways to decompose an integer into fixed-size blocks is to convert it to base N, and encrypt each digit separately.
Note that you should not encrypt each digit independently from the others, because that wouldn't be safe. In fact doing so is equivalent to using a monoalphabetic cipher. . There are different mode of operations for block ciphers that you can use to safely encrypt multiple blocks. You should read the wikipedia page to learn about them.