Decrypt cipher text encrypted with PyCrypto using cryptopp - python

My server encrypts files using pycrypto with AES in CTR mode. My counter is a simple counter like this:
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03
I wanna decrypt the cipher text with c++'s cryptopp library in my clients. How should I do so?
Python code:
encryptor = AES.new(
CRYPTOGRAPHY_KEY,
AES.MODE_CTR,
counter=Counter.new(128),
)
cipher = encryptor.encrypt(plain_text)
C++ code so far:
byte ctr[] = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"
mDecryptor = new CryptoPP::CTR_Mode<CryptoPP::AES>::Decryption(key, 32, ctr);
std::string plain;
CryptoPP::StringSource(std::string(data, len), true, new CryptoPP::StreamTransformationFilter(*mDecryptor, new CryptoPP::StringSink(plain)));
but after running this plain is garbage.
Update:
Sample encrypted data you can try to decrypt with crypto++ so that you can help me even if you don't know python and you're just experienced with crypto++:
Try to decrypt this base64 encoded text:
2t0lLuSBY7NkfK5I4kML0qjcZl3xHcEQBPbDo4TbvQaXuUT8W7lNbRCl8hfSGJA00wgUXhAjQApcuTCZckb9e6EVOwsa+eLY78jo2CqYWzhGez9zn0D2LMKNmZQi88WuTFVw9r1GSKIHstoDWvn54zISmr/1JgjC++mv2yRvatcvs8GhcsZVZT8dueaNK6tXLd1fQumhXCjpMyFjOlPWVTBPjlnsC5Uh98V/YiIa898SF4dwfjtDrG/fQZYmWUzJ8k2AslYLKGs=
with this key:
12341234123412341234123412341234
with counter function described in the beginning of this post using crypto++. If you succeed post the decrypted text (which contains only numbers) and your solution please.
Update2:
I'm not providing an IV in python code, the python module ignores IV. I the IV thing is what causing the problem.

As I read their source codes I can say PyCrypto and Crypto++ Both are perfect libraries for cryptography for Python and C++. The problem was that I was prefixing the encrypted data with some meta information about file and I totally forgot about that, after handling these meta data in client Crypto++ decrypted my files.
As I didn't find this documented explicitly anywhere (not even in Wikipedia) I write it here:
Any combination of Nonce, IV and Counter like concatenation, xor, or likes will work for CTR mode, but the standard that most libraries implement is to concatenate these values in order. So the value that is used in block cipher algorithm is usually: Nonce + IV + Counter. And counter usually starts from 1 (not 0).

Related

Encrypt a password using a public key + RSA in Python. What am I doing wrong?

I'm accessing an API that returns the following public key:
"publicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArZj/8FWa9e2PmHIBzdMwA/Wo5HYyOHBOxORU5bVBOsb8ZJekhgNWplZxskpuMx1GC9m0WTvCHK+lLmlxKyOomu85q7MxocM8n7iF8Cc0Qrgjushut35FM1bT36em46eCCuO4WqG9/GhCsUeLTsQFBTUxF2Zk6++EJcmBgwU1yNvFZNUScfTmNSMpOcnWlGgt0GpOCdsx8GECOgZhwkJFDnUa01k4BeHYDJEufgNkq4lXh8wxep03S6RyZIAye9zDTaGhGvA5+loQq8bBWCbBzNTJWNhn1kpsnPQJHFcugLMYUyglzxk6phy1Et/s1ANH8H8jdRojhoJEVjg7+Y0JwwIDAQAB"
I need to use this public key to encrypt a password and send it to another endpoint in Base64 format.
What I need to do is exactly what this website does: https://www.devglan.com/online-tools/rsa-encryption-decryption
I've followed several tutorials, asked gpt chat for help and tested it in several ways but I can't.
When I encrypt the password using the website above and call the API directly through Postman it works, but when I encrypt it via Python it doesn't work. It says the encrypted password was not recognized.
My code:
#staticmethod
def get_pem_format(publickey: str):
lenkey = int(len(publickey) / 64)
key = ""
for x in range(0, lenkey+1):
startpos = x*64
endpos = startpos + 64
keylen = str(publickey[startpos:endpos])
key += f"{keylen}\n" if len(keylen) == 64 else keylen
key = f"-----BEGIN PUBLIC KEY-----\n{key}\n-----END PUBLIC KEY-----"
return key
def get_rsa_credentials(self, sourceid, newpass):
userkeyinfo = self._query_password_info(sourceid)
# Save public key
with open('/tmp/public_key.pem', 'w') as f:
f.write(self.get_pem_format(userkeyinfo["publicKey"]))
# Get public key
with open('/tmp/public_key.pem', 'rb') as f:
publickey = RSA.importKey(f.read())
cipher = PKCS1_OAEP.new(publickey)
encryptedpass = cipher.encrypt(newpass.encode())
base64encryptedpass = base64.b64encode(encryptedpass).decode()
return base64encryptedpass, userkeyinfo["publicKeyId"]
newpass = "Oliveir4souz#"
sourceid = "2c9180878168627f018192ff06f66ccb"
ecryptedpass, publickeyid = self.get_rsa_credentials(sourceid, newpass)
The _query_password_info method is where I call the api and get the public key.
And I created this method get_pem_format that generates the file in pem format, because all the libraries I found only carry the key of a file.
The code above does not generate any errors. But the encrypted value is not valid. But when I use the website as in the image below, it works perfectly in the API call.
I cannot understand what I am doing wrong.
The problem is caused by different paddings: With RSA (just like with RSA/ECB/PKCS1Padding) in the Cipher Type field, the website applies PKCS#1 v1.5 padding. Since the ciphertext generated with the website can be successfully decrypted by the endpoint, the endpoint obviously uses PKCS#1 v1.5 padding as well.
However, in the Python code, cipher = PKCS1_OAEP.new(publickey) specifies OAEP as padding, which is why the Python code on the one hand and the website (or the endpoint) on the other hand are incompatible. To fix the problem, cipher = PKCS1_v1_5.new(publickey) must be used in the Python code so that PKCS#1 v1.5 padding is applied there as well. With this change, the ciphertext generated with the Python code can be successfully decrypted using the website with RSA in the Cipher Type field (and should also be successfully decrypted by the endpoint).
For completeness: PKCS1_OAEP() applies OAEP as padding using the default SHA-1 for content digest and MGF1 digest, which is equivalent to the RSA/ECB/OAEPWithSHA-1AndMGF1Padding option of the website.
As side note: PyCryptdome supports import and export of keys in different formats and encodings. This way you can convert your Base64 encoded ASN.1/DER encoded key (in X.509/SPKI format) into a PEM encoded key as follows:
key = RSA.import_key(base64.b64decode(publickey)).exportKey(format='PEM').decode('utf8')
where publickey is: MIIBIj....
Regarding the posted screenshot on encryption with the website: Keep in mind that RSA encryptions (both with OAEP and PKCS#1 v1.5) are non-deterministic, i.e. encryptions with the same key and plaintext generate different ciphertexts. Therefore, it is not an indication of an error if tests with identical key and plaintext result in different ciphertexts.

python symmetric encryption to binary without timestamp

I want to encrypt a .zip file using AES256 in Python. I am aware of the Python cryptography module, in particular the example given at:
https://cryptography.io/en/latest/fernet/
However, I have needs that are a bit different:
I want to output binary data (because I want a small encrypted file). How can I output in binary instead of armored ASCII?
I do not want to have the plaintext timestamp. Any way to remove it?
If I cannot fix those points I will use another method. Any suggestions? I was considering issuing gpg commands through subprocess.
Looking at Fernet module, seems it encrypts and authenticates the data. Actually its safer than only encrypting (see here). However, removing the timestamp, in the case of this module, doesn't make sense if you also want to authenticate.
Said that, seems you want to risky and only encrypt instead of encrypt and authenticate. You might follow the examples of the same module found at https://cryptography.io/en/latest/hazmat/primitives/symmetric-encryption/. Just make sure this is what you really want.
As you're worried about size and want to use AES, you could try AES in CTR mode, which does not need padding, avoiding extra bytes at the end.
import os
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
backend = default_backend()
key = os.urandom(32)
nonce = os.urandom(16)
cipher = Cipher(algorithms.AES(key), modes.CTR(nonce), backend=backend)
encryptor = cipher.encryptor()
ct = encryptor.update(b"a secret message") + encryptor.finalize()
print(ct)
decryptor = cipher.decryptor()
print(decryptor.update(ct) + decryptor.finalize())
So, answering your questions:
(1) The update method already returns a byte array.
(2) This way there will be no plaintext data automatically appended to the ciphertext (but be aware of the security implications about not authenticating the data). However, you'll need to pass the IV anyway, what you would have to do in either case.

Pycrypto OpenPGP encryption: Why is there a 16byte limit to the key, what part of the key do I provide to hit 16 bytes?

I'm trying to encrypt a file using OpenPGP in python via the pycrypto application. I've been following the sample provided in their code here: https://github.com/dlitz/pycrypto/blob/master/lib/Crypto/Cipher/CAST.py
So I'm using mode.openPGP, but I can't seem to encrypt anything using a public key. My public key is well over the 16byte limit they specify (and any generation I've seen is over this limit as well). Is there a different value I'm supposed to use here, like the fingerprint ID?
I'm trying to read the contents of a file, encrypt it with a key, then print it into a new file to be sent (both will be deleted later on).
My code is as follows:
iv = CryptoRandom.new().read(CAST.block_size)
cipher = CAST.new(public_key, CAST.MODE_OPENPGP, iv)
file = open(filename)
contents = ''.join(file.readlines())
encrypted_contents = cipher.encrypt(contents)
encrypted_filename = filename.replace('/tmp/', '/tmp/encrypted')
encrypted_filename = encrypted_filename.replace('.csv', '.asc')
encrypted_file = open(encrypted_filename, 'w')
encrypted_file.write(encrypted_contents)
return encrypted_filename
I think you may be misunderstanding the algorithm you're using here. CAST is a symmetric-key algorithm, but whilst this implementation has an "OpenPGP mode", that doesn't mean that you simply pass your public key to it.
You should be generating a unique 16 byte key and passing that to CAST.new(). You would then generally encrypt that randomly-generated key using the public-key, and store/transmit the cipher text, and encrypted random-key together. The decryption process would decrypt the random-key using the private-key, then use the decrypted random-key to decrypt the cipher text.

How to encrypt a string using the key

I have a 'public key' in a variable named varkey, for getting the public key I used the urllib and stored that public key in a variable. Now I want to encrypt a msg/string using the public key.
It's ok if somebody could lead me to some library.
My blog post (the passingcuriosity.com link in John Boker's answer) does AES -- a symmetric encryption algorithm -- using the M2Crypto library. M2Crypto is a Python wrapper around OpenSSL. The API is pretty much a straight translation of OpenSSL's into Python, so the somewhat sketchy documentation shouldn't be too much of a problem. If the public key encryption algorithm you need to use is supported by M2Crypto, then you could very well use it to do your public key cryptography.
I found the M2Crypto test suite to be a useful example of using its API. In particular, the RSA (in test_rsa.py), PGP (in test_pgp.py), and EVP (in test_evp.py) tests will help you figure out how to set up and use the library. Do be aware that they are unit-tests, so it can be a little tricky to figure out exactly what code is necessary and what is an artefact of being a test.
PS: As I'm new, my posts can only contain one link so I had to delete most of them. Sorry.
Example
from M2Crypto import RSA
rsa = RSA.load_pub_key('rsa.pub.pem')
encrypted = rsa.public_encrypt('your message', RSA.pkcs1_oaep_padding)
print encrypted.encode('base64')
Output
X3iTasRwRdW0qPRQBXiKN5zvPa3LBiCDnA3HLH172wXTEr4LNq2Kl32PCcXpIMxh7j9CmysLyWu5
GLQ18rUNqi9ydG4ihwz3v3xeNMG9O3/Oc1XsHqqIRI8MrCWTTEbAWaXFX1YVulVLaVy0elODECKV
4e9gCN+5dx/aG9LtPOE=
Here's the script that demonstrates how to encrypt a message using M2Crypto ($ easy_install m2crypto) given that public key is in varkey variable:
#!/usr/bin/env python
import urllib2
from M2Crypto import BIO, RSA
def readkey(filename):
try:
key = open(filename).read()
except IOError:
key = urllib2.urlopen(
'http://svn.osafoundation.org/m2crypto/trunk/tests/' + filename
).read()
open(filename, 'w').write(key)
return key
def test():
message = 'disregard the -man- (I mean file) behind curtain'
varkey = readkey('rsa.pub.pem')
# demonstrate how to load key from a string
bio = BIO.MemoryBuffer(varkey)
rsa = RSA.load_pub_key_bio(bio)
# encrypt
encrypted = rsa.public_encrypt(message, RSA.pkcs1_oaep_padding)
print encrypted.encode('base64')
del rsa, bio
# decrypt
read_password = lambda *args: 'qwerty'
rsa = RSA.load_key_string(readkey('rsa.priv2.pem'), read_password)
decrypted = rsa.private_decrypt(encrypted, RSA.pkcs1_oaep_padding)
print decrypted
assert message == decrypted
if __name__ == "__main__":
test()
Output
gyLD3B6jXspHu+o7M/TGLAqALihw7183E2effp9ALYfu8azYEPwMpjbw9nVSwJ4VvX3TBa4V0HAU
n6x3xslvOnegv8dv3MestEcTH9b3r2U1rsKJc1buouuc+MR77Powj9JOdizQPT22HQ2VpEAKFMK+
8zHbkJkgh4K5XUejmIk=
disregard the -man- (I mean file) behind curtain
From my recent python experience, python doesn't do encryption natively. You need to use an external (3rd party) package. Each of these, obviously, offers a different experience. Which are you using? This will probably determine how your syntax will vary.
You might want to have a look at:
http://www.example-code.com/python/encryption.asp
or this
http://passingcuriosity.com/2009/aes-encryption-in-python-with-m2crypto/
Have you ever heard about "RSAError: data too large for key size"?
Try your sample with more long message:
encrypted = rsa.public_encrypt('My blog post (the passingcuriosity.com link in John Boker's answer) does AES -- a symmetric encryption algorithm -- using the M2Crypto library', RSA.pkcs1_oaep_padding)
You could use MD5 or SHA1 hashing along with your key...

Python RSA Decryption Using OpenSSL Generated Keys

Does anyone know the simplest way to import an OpenSSL RSA private/public key (using a passphrase) with a Python library and use it to decrypt a message.
I've taken a look at ezPyCrypto, but can't seem to get it to recognise an OpenSSL RSA key, I've tried importing a key with importKey as follows:
key.importKey(myKey, passphrase='PASSPHRASE')
myKey in my case is an OpenSSL RSA public/private keypair represented as a string.
This balks with:
unbound method importKey() must be called with key instance as first
argument (got str instance instead)
The API doc says:
importKey(self, keystring, **kwds)
Can somebody suggest how I read a key in using ezPyCrypto? I've also tried:
key(key, passphrase='PASSPHRASE')
but this balks with:
ezPyCrypto.CryptoKeyError: Attempted
to import invalid key, or passphrase
is bad
Link to docs here:
http://www.freenet.org.nz/ezPyCrypto/detail/index.html
EDIT: Just an update on this. Successfully imported an RSA key, but had real problem decrypting because eqPyCrypto doesn't support the AES block cipher. Just so that people know. I successfully managed to do what I wanted using ncrypt (http://tachyon.in/ncrypt/). I had some compilation issues with M2Crypto because of SWIG and OpenSSL compilation problems, despite having versions installed that exceeded the minimum requirements. It would seem that the Python encryption/decryption frameworks are a bit of a minefield at the moment. Ho hum, thanks for your help.
The first error is telling you that importKey needs to be called on an instance of key.
k = key()
k.importKey(myKey, passphrase='PASSPHRASE')
However, the documentation seems to suggest that this is a better way of doing what you want:
k = key(keyobj=myKey, passphrase='PASSPHRASE')
It is not clear what are you trying to achieve, but you could give M2Crypto a try. From my point of view it is the best OpenSSL wrapper available for Python.
Here is a sample RSA encryption/decription code:
import M2Crypto as m2c
import textwrap
key = m2c.RSA.load_key('key.pem', lambda prompt: 'mypassword')
# encrypt something:
data = 'testing 123'
encrypted = key.public_encrypt(data, m2c.RSA.pkcs1_padding)
print "Encrypted data:"
print "\n".join(textwrap.wrap(' '.join(['%02x' % ord(b) for b in encrypted ])))
# and now decrypt it:
decrypted = key.private_decrypt(encrypted, m2c.RSA.pkcs1_padding)
print "Decrypted data:"
print decrypted
print data == decrypted

Categories