Parse RSA key pair from string in Python - python

I'm trying to generate/read a RSA-keypair from the publicKey and the privateKey as a String.
Something like this:
priK = "-----BEGIN RSA PRIVATE KEY-----MIIBOQIBAAJAVJhUS0gLqXLOmVv2xG23oFPwim9+rVxGhLUXqKShQCvB3iRMOHn7/GNJumpwmnglcsNXuqAhN0OxqKGGJdtYdwIDAQABAkBP0VrXnSbDvvuIX+k59Xvo3sp7FDAmSoaO+H9WM9+ht5H/f/geIrSEXSIkFLnzniMwtOJ422GmkDkL1F67HuDhAiEAlNauDiq3RqoXufbauyPEOG9fMS2pvB+auT2XCHJhhKsCIQCRgIo7WIRZYnNpNRWaoppUQK3g+aM8sdeBYpbs2nwDZQIgZXIxrmxFAUAb7d+oVFdbfc/DRSTHhPbRoaKuF87GUwMCIFmzaATsLjO42TPMETSS+BfnBAtFe5hIf3Z5pFgC3h9tAiEAgYjug92fmVvE+CcRSg6at7meSEbK/Kxg7Ar4mlkXMlI=-----END RSA PRIVATE KEY-----"
pubK = "-----BEGIN PUBLIC KEY-----MFswDQYJKoZIhvcNAQEBBQADSgAwRwJAVJhUS0gLqXLOmVv2xG23oFPwim9+rVxGhLUXqKShQCvB3iRMOHn7/GNJumpwmnglcsNXuqAhN0OxqKGGJdtYdwIDAQAB-----END PUBLIC KEY-----"
keyPair = RSA.importKey(priK + pubK)
My error that I'm getting is:
in importKey
if lines[1].startswith(b('Proc-Type:4,ENCRYPTED')):
I don't even know if it's possible like that. I didn't really find information about that.

RSA.importKey(key) imports one key. It cannot import concatenated keys.
If you import a private key, then you can extract a public key from that, because common PKCS#1 and PKCS#8 format have all the necessary information to create public key. So, you don't even need to concatenate the public key to it.
Use:
privateKey = RSA.importKey(priK)
publicKey = privateKey.publickey()

Related

Same public key differs with the way to get it

I'm writting a little program using SSL sockets. A client sends values to a server and when the server gets the value it checks the client's public key to make sure he's expected to send something. So at first the server is getting all the public keys like this :
cert = f.read()
crtObj = crypto.load_certificate(crypto.FILETYPE_PEM, cert)
pubKeyObject = crtObj.get_pubkey()
pubKeyString = crypto.dump_publickey(crypto.FILETYPE_PEM,pubKeyObject)
with this method, the public key is :
b'-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7GIzek5JgfFzFCGwnx7X\ncE4QULV/9uyoGgd9HbHYyYcItEcSPU39ORXCrNQGxh09k4oFPBYntjD2gIORF8V4\n6EAC10bFaT18OuM1F/37v+K/+BuvCDTqcS9Y0CRalwPFVYB+yttvZ8fnvO2l/TxF\nsLmZh0yY/ajaHxey/ppUQycGy4xA8XD6VlWFM7+I0t/19rrLN9iMFSym/TgYpBbn\nxyZel8rMW/ACS09nSprEu1BuI+myhhej+cuy3wU8byRTwANpqHxsx5cTwp642TVx\nBKbuO8GHAzEKcrFZnrKcsXr9emWV5ouYiVzehOT4Pd3I2W8qSy6x/Ovv/iS3ojT4\ndQIDAQAB\n-----END PUBLIC KEY-----\n'
and when the server wants to get it from the socket connection like this :
test1 = writer.get_extra_info('ssl_object')
der =test1.getpeercert(binary_form=True)
test = test1.getpeercert()
cert = x509.Certificate.load(der)
pubkey= cert.public_key.unwrap()
print(pem.armor("PUBLIC KEY", pubkey.contents).decode("ASCII"))
the public key printed is :
-----BEGIN PUBLIC KEY-----
AoIBAQDsYjN6TkmB8XMUIbCfHtdwThBQtX/27KgaB30dsdjJhwi0RxI9Tf05FcKs
1AbGHT2TigU8Fie2MPaAg5EXxXjoQALXRsVpPXw64zUX/fu/4r/4G68INOpxL1jQ
JFqXA8VVgH7K229nx+e87aX9PEWwuZmHTJj9qNofF7L+mlRDJwbLjEDxcPpWVYUz
v4jS3/X2uss32IwVLKb9OBikFufHJl6Xysxb8AJLT2dKmsS7UG4j6bKGF6P5y7Lf
BTxvJFPAA2mofGzHlxPCnrjZNXEEpu47wYcDMQpysVmespyxev16ZZXmi5iJXN6E
5Pg93cjZbypLLrH86+/+JLeiNPh1AgMBAAE=
-----END PUBLIC KEY-----
So I don't know if it's a matter of format or if it's really not the same public key I'm getting... but it should be.
Sorry for the long post and thank you very much for reading.
Most(if not all) base64 encoding variants use A-Z, a-z, 0-9 characters to represent data the total is 62 character and some base64 encodings varies in the last 2 characters but most of them use + and / which will complete 64. So the above 2 keys seems to be completely different and the second key is also padded because of = sign at end. The standard base64 encoding is RFC 4648 variant which use + and / and make padding as optional not mandatory like RFC 4880 openpgp base64 encoding.
It was in fact the same keys but the way of getting it was wrong : here the right one from the ssl object :
test1 = writer.get_extra_info('ssl_object')
der =test1.getpeercert(binary_form=True)
crtObj = crypto.load_certificate(crypto.FILETYPE_ASN1, der)
pubKeyObject = crtObj.get_pubkey()
pubKeyString = crypto.dump_publickey(crypto.FILETYPE_PEM, pubKeyObject)

Generate a pair of private and public keys in python

I need to generate a pair of private and public keys in Python. I used to generate them on the site mkjwk, but I want to automate this process.
I need to generate keys in a format like the ones highlighted in yellow in the picture.
I tried to use the cryptography library to solve this problem.
I managed to generate a private key in the required format, but I don’t know how to generate a public key.
key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
)
private_key = key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=serialization.NoEncryption(),
)
I would be very grateful for any help with this. This does not have to be done with the cryptography library, if there is a better way.
I guess using pyopenssl should do it for you
https://www.pyopenssl.org/en/stable/api/crypto.html#OpenSSL.crypto.PKey.generate_key
from OpenSSL import crypto
pk = crypto.PKey()
pk.generate_key(crypto.TYPE_RSA, 2048)
print(f"Private key : {crypto.dump_privatekey(crypto.FILETYPE_PEM, pk)}\n")
print(f"Public key : {crypto.dump_publickey(crypto.FILETYPE_PEM, pk)}\n")
Private key : b'-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDngpyqg5dMlaLL\nNv6WruNAmeA9bZWmr62b0GEOqccjaq6aWscPUYAdzV/xoqAQ8JV6v8OS2O54mi7h\naE0ma4MXpajq8GAf8l/EJoHM1/2mo7r/XDVsxyUgBpd0P/8ds4KiN++x4wr4/Kof\nGHd6+aEwVbRb20ha/IjS6500eDSr1Ld1QMoSWHZ9AJSNgRFc2bm+y/7O/Qf/Oel5\nUSuWKHWBvoyqX5d643ltUDwc3a3H/A0bTwHYYM3W7FbeievNjxs9yM2dUMhPHOS+\n4ATlD1rvsVF686z/MiTicIDe0Sd0Svsk6w2y2QZeYql96EPRcwTAHTlBvpIzJpJx\nJlL2554RAgMBAAECggEBAMvP0NzMvIZPteHxqHA/xxE4ZpGtx/HW96AU811VWltz\nsANzp4t01LVn+O9hnElNhEtsR2EgWdES6/LFQCZywBYxYWRz+iwl1Ol6fQs5m7T4\nr8fgBaieKbDoHK3bKV2ci4UEeaDBoQdSaPK3N3isC5vh18aGZkyzxkDp7JwktzoN\nfinMTmuZPuczl2leqnoY6Dlkkj8hElMLqzAK3TAq5eVh+k0mGklF3LwdLBiG9JTa\nPh1IFYv3OqKvBla+HATyY/596otd3mn7I7EJMekWickNsroxO/wCi7SUvtizyB+d\nAY83u7vY5vJM9kNGU3X6/ac+eK/S65Hp7kRyqhhCV4ECgYEA+ekBijL5jTyTh/a2\nlfh0LKnynXYzyhwM4ZRIEjHGHjkkDSUbDiD9K+8DFrz3btc5rBImRVYABh4uBXJC\ntL42r5A4woxwblD4UZ09OJ+nuuE1ahtjWMTxRnIzKCfsfDCxETxBusMcyCxDggm0\nSAvBkLjGgqimN7nR0QqcRFuEU2MCgYEA7SbSqkFy4m8y+7TLPvA8ynrhR6GJ+xCe\nxOWBcbqs7W6e5q1OTPBnV0gXA1gBcu0AfAz3GZAYDeO44fBOFtDOzALjNOkVRM1U\nFEjESyAGbGAp2tfzMc7rN6UOZ7SJ78HNu7WTjTSmFK+wyxdcEIS93GDOjKYbNqwR\nBw056aRrdPsCgYBhpnI7lf98+JaNIhHmN9btPNrYPD+wUZWW72HZ+ij31kwH9t/D\nfZBvgk0qrVvhq6eVXOInZZtMyK4i4qq+BTVJFImZO1cTRABDo7UwUvIvS6CbfWgs\nX4gHhsgGgNMfE8ecfHcSivrMHL+kKDaRkEZqTkkC9PM+AxXBIOw+qaPjEQKBgC+O\nN3R4x2bs0ZZz/MXUOvyHg6FvnVHBVXU6aKi7vG2oXyj229rF4pM4G5VKEpBPQmed\n2fdAU/KFFdAI9j/RA4cZlSJJE0DGw/OFXAeMln0pE3uVDmmQis6PxMG7DGYNwQnB\nMaOBSUSgrp0rRATz+Xa12vWNYaum+YsmddSKahn9AoGBAKKZ1kBir/KQdWv4XcSD\nVni4KTbBj9W6bDHYASPL7NQnBnrwbkEqZu5U4gHTCpJ6KHMLn0pJv9W3PMjBIlDd\n3va9+Ex6KM0+Xuh21I2HAxzqGFW7b60rGnKx8CKqhzggOpyK9FpaxE3u51b+s6sE\nZac7zriIze30/udoDzckVqJM\n-----END PRIVATE KEY-----\n'
Public key : b'-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA54KcqoOXTJWiyzb+lq7j\nQJngPW2Vpq+tm9BhDqnHI2qumlrHD1GAHc1f8aKgEPCVer/DktjueJou4WhNJmuD\nF6Wo6vBgH/JfxCaBzNf9pqO6/1w1bMclIAaXdD//HbOCojfvseMK+PyqHxh3evmh\nMFW0W9tIWvyI0uudNHg0q9S3dUDKElh2fQCUjYERXNm5vsv+zv0H/znpeVErlih1\ngb6Mql+XeuN5bVA8HN2tx/wNG08B2GDN1uxW3onrzY8bPcjNnVDITxzkvuAE5Q9a\n77FRevOs/zIk4nCA3tEndEr7JOsNstkGXmKpfehD0XMEwB05Qb6SMyaScSZS9uee\nEQIDAQAB\n-----END PUBLIC KEY-----\n'

Verifying a RS256 signed JWT

I've tried using my public key vs. using the private key, putting a b in front, doing 3 vs. 1 quotes vs. one quote (and many more things I see here on Stackoverflow) and I keep getting the following error:
ValueError: Could not deserialize key data.
Any ideas?
import jwt
secret_key='''MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwE...pTeoOgWZ'''
token = '''eyJraWQiOiI4NkQ4OEtmIiwiNG2Bua1WoKEI8T..._cXnyThWA'''
#public_key = '''iGaLqP6y-SJCCBq5Hv6pGDbG_SQ11MNj...Mb0jcOTmBRZA2QuYw-zHLwQ'''
payload = jwt.decode(token, secret_key, algorithms=['RS256'])
return payload
Try with the complete public key including the begin and end delimiters and use """ or ''' for a multiline string:
import jwt
token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.K71d-kndywmuxJR-SzDqTARcHJBsHezMeRtDwn4S0NMd3ZmW1LKKqoVQv8rMNam4HK6hsSmB0sqIpKfiLFn73HC1j9-fdBafZNy-9eL4cNr1ldwxj5jD6CwPMjT0mOB01liHFPiIfHDabYV2FzFEV8oaX8CxVL7_CLLv2I01NHo"
pubkey = """-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDCcbV4zk01saMn/VzIDN7rOY8D
BTYGnH6fnYB/wTKBHme6MpuOv+4d48ZKDseP8EcTFQmmAmKeKRGJUzSJ9cN2KlND
a4y2gkEsqh8a9U/7492hVWNB6JkSPrdqrkRho9/q9gCcDlTZROY/RDuf3Y1F1riV
tiFvXvfCp75fpLceIQIDAQAB
-----END PUBLIC KEY-----"""
payload = jwt.decode(token, pubkey, algorithms=['RS256'])
print(payload)
Output:
{'sub': '1234567890', 'name': 'John Doe', 'iat': 1516239022}
(no worries about the key, I just created it online just for demonstration purpose)
I figured it out. My public key above wasn't actually a public key. It is a RSA key modulus. I had to use this tutorial to take this RSA key modulus and the exponent to generate a public key. When i did this, then the JWT.decode functionality worked just fine.
Thanks #jps for your help!

Python rsa module troubles

I need to encrypt some data, so I decided to use rsa module. I need to generate public and private keys. I want to send my public keys to some clients. How can I do this?
import rsa
(public_key, private_key) = rsa.newkeys(512)
So I've already generated it, but public_key now is a structure with two fields e and n. how can I transform it into bytes? And back from bytes to such structure?
You can use PyCrypto module
from Crypto.PublicKey import RSA
key = RSA.generate(512)
private_key = key.exportKey(passphrase='', pkcs=8)
public_key = key.publickey().exportKey()
In your case, try using save_pkcs1 method for saving into PEM format.
UPD:
Code sample for encrypt using rsa:
message = b'message'
# Alternative way - message = 'message'.encode('utf-8')
encrypted = rsa.encrypt(message, public_key)
For decrypt:
rsa.decrypt(encrypted, private_key).decode('utf-8')

Create compatible Java "RSA" signature using a PKCS#8 encoded private key

I have pkcs8_rsa_private_key file which generate by openssl from a rsa_private_key.pem file.
I need make a signature by the private key in python, make the same signature with below java code.
public static final String SIGN_ALGORITHMS = "SHA1WithRSA";
public static String sign(String content, String privateKey) {
String charset = "utf-8";
try {
PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(
Base64.decode(privateKey));
KeyFactory keyf = KeyFactory.getInstance("RSA");
PrivateKey priKey = keyf.generatePrivate(priPKCS8);
java.security.Signature signature = java.security.Signature
.getInstance(SIGN_ALGORITHMS);
signature.initSign(priKey);
signature.update(content.getBytes(charset));
byte[] signed = signature.sign();
return Base64.encode(signed);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
PKCS#8 defines a way to encode and transport secret keys and it is not specific to OpenSSL; PKCS#1 defines a way to use an RSA key (no matter how it was loaded into your application, with PKCS#8 or not) to carry out and verify digital signature on data.
The piece of code you have does three things:
It decodes Base64 into PKCS#8
It decodes PKCS#8 into the actual key in memory (mind you may need to provide a passphrase here)
It performs PKCS#1 v1.5 signature using SHA-1 using said key
It encodes the signature in Base64
The example for PKCS#1 v1.5 signing in the API of PyCrypto does exactly steps #2 and #3.
from Crypto.Signature import PKCS1_v1_5 as pk
from Crypto.Hash import SHA
class Crypt(object):
pkcs8_private_key = RSA.importKey(open('pkcs8_rsa_private_key.pem', 'r').read())
def rsa_sign(cls, des_reqdata):
"""
#:param reqdata: request reqData
"""
h = SHA.new(des_reqdata)
signer = pk.new(cls.pkcs8_private_key)
signature = signer.sign(h)
return base64.b64encode(signature)

Categories