Assistance encoding/decoding a message with DES - Python - python

I am trying to create some code in Python to encrypt/decrypt a message using DES.
I seem to be getting the following error, and can't work out why.
'utf-8' codec can't decode byte 0xc1 in position 1: invalid start byte'
Code is as follows:
from Crypto.Cipher import DES
key = 'abcdefgh'
def encrypt(msg):
cipher = DES.new(key.encode('utf-8'), DES.MODE_EAX)
ciphertext = cipher.encrypt(msg.encode('utf-8'))
return ciphertext
def decrypt(ciphertext):
cipher = DES.new(key.encode('utf-8'), DES.MODE_EAX)
plaintext = cipher.decrypt(ciphertext)
return plaintext.decode('utf-8')
ciphertext = encrypt('Python')
plaintext = decrypt(ciphertext)
print(f'Cipher text: {ciphertext}')
print(f'Plain text: {plaintext}')

I cannot seem to find out the problem with your code, but may I suggest another way to do it, using the cryptography module?
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.backends import default_backend
from base64 import urlsafe_b64encode
from socket import getfqdn
from os.path import expanduser
print(expanduser("~")) # Get current users home directory, works for linux too, example: C:\Users\username
print(getfqdn()) # Get fully qualified domain name, works for linux too, example: user.company.com
KDF = PBKDF2HMAC(algorithm=hashes.SHA512(),length=32,salt=urlsafe_b64encode(expanduser("~").encode()),iterations=400000,backend=default_backend())
FERNET_KEY = urlsafe_b64encode(KDF.derive(getfqdn().encode()))
def encryptString(string: str) -> str:
cipher: Fernet = Fernet(FERNET_KEY)
return(cipher.encrypt(string.encode()).decode())
def decryptString(string: str) -> str:
cipher: Fernet = Fernet(FERNET_KEY)
return(cipher.decrypt(string.encode()).decode())
ciphertext = encryptString("Python")
plaintext = decryptString(ciphertext)
print(f'Cipher text: {ciphertext}')
print(f'Plain text: {plaintext}')
Result:
Cipher text: gAAAAABjK_6v7Wg...truncated...Q8k8HoVA==
Plain text: Python
I'm using the expanduser as the salt to generate a key with 400.000 iterations. Then the actual key is being made by using the derive function and getfqdn as the password.
FYI: I do not take any responsibility for any lost data or wrong use. This is purely a demonstration of how to use the selected modules for encrypting strings.
PBKDF2HMAC documentation
Cryptography Fernet
Derive
If you are using Windows, you could take advantage of the build-in Data Protection API (DPAPI). You must install pywin32 by using pip install pywin32.
The code:
from win32crypt import CryptProtectData, CryptUnprotectData
def encryptString(string: str) -> str:
return(CryptProtectData(string.encode()))
def decryptString(string: str) -> str:
return(CryptUnprotectData(string)[1].decode())
ciphertext = encryptString("Python")
plaintext = decryptString(ciphertext)
print(f'Cipher text: {ciphertext}')
print(f'Plain text: {plaintext}')
Result:
Cipher text: b'\x01\x00\x00\x00\xd0...truncated ...\xeei\x1bI\xd9\x14\x00\x00\x00*\xe0\xc61P\xd0 \x1f3\n\xfb\xdb\x1d\x7fC7\nwL_'
Plain text: Python

Related

Decrypt RSA with a private key that exists as a string in Python

I tried to decrypt rsa using a private key that exists as a string, but it failed.
-----BEGIN RSA PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCTiP1x58DboFjS
4WgzMm8tuY9VoHwANbOHNuuiElsZ4xIVFS+ZG7lu7Iz9gzmsno/YiqQXms8hXmUH
ouGgVJLJGPTw/NXAnLq6XlwB3C+zQMWpxvcMub4D6/IltP/PUpKNj9QzKKGhZF+6
s5B/QBzmSeNYlYYyGnO9GxyuHyR7P5xqF9AcpSskn9gjYy0koTLmvg/bwFx6jnci
np2qVHo5CFvSTAgrZKBCmKD2c2AGD9O+TSOrT0RDzvJosW4P1QJ9qsCjKFSPW/8K
IvB+AsSA+9xiVMDm/3YABBBM/R7wDcciTmKucZoEuDeWJg+cwLur8kKSVNwwkwCT
MqTIbHn5AgMBAAECggEABl5aemlDpPdl/ixmwBcEP5gL/OlBkQzAZCzVvRhHwHVR
2YEhnd1ZgtVJPMRGYBI0KWfKr44qNMmLWOoKDU65S179a82uOHNLiDH8jMQBdx5r
qemBzpXSAv9TY5dNl5h75Qp5YX/2gi4AB+IqcaPF25cC7lb+BPmpV1dtRILpozxJ
78mE0o0ddAJhg+0lIX6lZqjXbKJh4uiIqJIwezOM/B9U59qWEiJoHurkJIQoc00n
lp+XKUJfUWE5OHdy2DZNbAWEDqvFiD3DBd5baks/CPHy6IZLj7e5y+h/b65veDw6
MWpwXK30vroRmIzfNFcbONuQ3EjHBvCXzQYppUPnAQKBgQDD/k9NVQ8r2EnhjX0g
dQKobT7aCpY/y0QvDZ6aeuOVkfqnFB+rUUSf5WsXbANghqsHtzJa+cDUpP0C1PuE
hexGS+SsLywKI/33S014tvt5iF/MgEu08sLHtoxR/dH3H4k2LDGMNBrKILFnfDYP
40e3QKdogXMsEw4C0tb85YGrIQKBgQDAtJZp+onH0NSDXL0kbWZa8GFuVMGjTz2m
6E+j3NHK5GDGSAme755lDS61TTz2xNYaJFEwUdvYKQq1x/Q7jffKMPcHUJ5uy7bH
QjFho377rvN9bNSk+UdP8AkhvjaLNC0c5K5eNEUgumrd+yDLw2YNvjOe/RjE3QiW
zpGHLigL2QKBgQC2LUufMSJB+fBqlG6rXbgTwD/8wnx4XcNKDVnQc8vZenBHH0B0
qLyl98S8Z60X6vVM3a8TuzTPM3DuUfRccXN2wBKVLd+8qUnmtHsVatiDVgzd9J7q
WgBNTNRPXiPtlcWfsiJ4FPKV0R+1dlJ2ICfIIXO6gyD/5dJPM5WcSuRloQKBgQCq
Tu+gOgwKzEUE07FVwFn1hPyWxdFcw2t5V1cLOt0usyfUFVZrYNpb9IqTMO/wJ4YR
FIesbKwfHiaZAV6YQ5/60cuAa3+Z6/BdqeJ6qERRqw0GjGhiZOzheQKZD8KkxDga
kQCJwShXBGnuRUN20fofqzl0CbsaQT6WCXoUPnamAQKBgEY/ttlyLCrdHhamWlwN
ZkcgfsvQtLc//DGfEOSIiybs0eD0ORutxVQ2pwEgPI79xJTm0bdTHAFZVYziI9Lm
75k5WKxbBsYTgZm3i+LnnDtcHKPqkpXkq1v6WduTfaWpwMDJt0cJMOiTFzom76s1
mdFdmq2IXR+vXwl9f8fboc61
-----END RSA PRIVATE KEY-----
The private key is a string and exists as shown above.
from Crypto.PublicKey import RSA
import base64
from Crypto.Cipher import PKCS1_OAEP
Pkey ="MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCTiP1x58DboFjS4WgzMm8tuY9VoHwANbOHNuuiElsZ4xIVFS+ZG7lu7Iz9gzmsno/YiqQXms8hXmUHouGgVJLJGPTw/NXAnLq6XlwB3C+zQMWpxvcMub4D6/IltP/PUpKNj9QzKKGhZF+6s5B/QBzmSeNYlYYyGnO9GxyuHyR7P5xqF9AcpSskn9gjYy0koTLmvg/bwFx6jncinp2qVHo5CFvSTAgrZKBCmKD2c2AGD9O+TSOrT0RDzvJosW4P1QJ9qsCjKFSPW/8KIvB+AsSA+9xiVMDm/3YABBBM/R7wDcciTmKucZoEuDeWJg+cwLur8kKSVNwwkwCTMqTIbHn5AgMBAAECggEABl5aemlDpPdl/ixmwBcEP5gL/OlBkQzAZCzVvRhHwHVR2YEhnd1ZgtVJPMRGYBI0KWfKr44qNMmLWOoKDU65S179a82uOHNLiDH8jMQBdx5rqemBzpXSAv9TY5dNl5h75Qp5YX/2gi4AB+IqcaPF25cC7lb+BPmpV1dtRILpozxJ78mE0o0ddAJhg+0lIX6lZqjXbKJh4uiIqJIwezOM/B9U59qWEiJoHurkJIQoc00nlp+XKUJfUWE5OHdy2DZNbAWEDqvFiD3DBd5baks/CPHy6IZLj7e5y+h/b65veDw6MWpwXK30vroRmIzfNFcbONuQ3EjHBvCXzQYppUPnAQKBgQDD/k9NVQ8r2EnhjX0gdQKobT7aCpY/y0QvDZ6aeuOVkfqnFB+rUUSf5WsXbANghqsHtzJa+cDUpP0C1PuEhexGS+SsLywKI/33S014tvt5iF/MgEu08sLHtoxR/dH3H4k2LDGMNBrKILFnfDYP40e3QKdogXMsEw4C0tb85YGrIQKBgQDAtJZp+onH0NSDXL0kbWZa8GFuVMGjTz2m6E+j3NHK5GDGSAme755lDS61TTz2xNYaJFEwUdvYKQq1x/Q7jffKMPcHUJ5uy7bHQjFho377rvN9bNSk+UdP8AkhvjaLNC0c5K5eNEUgumrd+yDLw2YNvjOe/RjE3QiWzpGHLigL2QKBgQC2LUufMSJB+fBqlG6rXbgTwD/8wnx4XcNKDVnQc8vZenBHH0B0qLyl98S8Z60X6vVM3a8TuzTPM3DuUfRccXN2wBKVLd+8qUnmtHsVatiDVgzd9J7qWgBNTNRPXiPtlcWfsiJ4FPKV0R+1dlJ2ICfIIXO6gyD/5dJPM5WcSuRloQKBgQCqTu+gOgwKzEUE07FVwFn1hPyWxdFcw2t5V1cLOt0usyfUFVZrYNpb9IqTMO/wJ4YRFIesbKwfHiaZAV6YQ5/60cuAa3+Z6/BdqeJ6qERRqw0GjGhiZOzheQKZD8KkxDgakQCJwShXBGnuRUN20fofqzl0CbsaQT6WCXoUPnamAQKBgEY/ttlyLCrdHhamWlwNZkcgfsvQtLc//DGfEOSIiybs0eD0ORutxVQ2pwEgPI79xJTm0bdTHAFZVYziI9Lm75k5WKxbBsYTgZm3i+LnnDtcHKPqkpXkq1v6WduTfaWpwMDJt0cJMOiTFzom76s1mdFdmq2IXR+vXwl9f8fboc6"
s="AQEAN2+hgPwYzFATKPJfkXyTQKe9kvUTmT6LhASj2YI0T2KhBO7gSpKbNx/EXF4JYaWcwJVhJwiudgCFvoJMK9qJtnpLFmG12f8drygke+MPo5n2flHFPiKRmJCcCRM/VR8gL+xlbFIZNBL/o4onbqC1XfeQygiHe6tKXGiAZGXcJejbnob+/V+sL46x076KjurqLjcFMH+SCXomhuQZOiSqRqmeAsE6kL8wlj2yhTUfqAL/GuTTRziT6Syp0zJ7dprgCYOXBWbkLD9X6Vw39Db75kLd6Vx5zKT5jUQeU8eTN6pYfIiymlXwModf3TFBG1CObxzxzrevTXxFfIahFZGMAhDltmmcy7GUWDB7Qav24psONYaH+P69VfTimRzbrLMCfPb3zqp0cS8glMZ5YQuWqpigQRlQBhGq4rN9TGxBE3F1YRByBg+CHelBCHZj+2swHauVRmgy0CKU2/nmKpMrypKguFjjE6+bBur8b2AE28LSfjqxPZJx2BM="
code_bytes = s.encode('UTF-8')
by=base64.b64decode(code_bytes)
key = PKCS1_OAEP.new(Pkey)
rsadecrypt=key.decrypt(str(by))
Attempted to decode string 's' after base64 decoding.
However, an error was issued as follows:
Traceback (most recent call last):
File "/Users/isin-yeong/Desktop/element/code/rsadecrypt.py", line 14, in <module>
rsadecrypt=key.decrypt(str(by))
^^^^^^^^^^^^^^^^^^^^
File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/Crypto/Cipher/PKCS1_OAEP.py", line 161, in decrypt
modBits = Crypto.Util.number.size(self._key.n)
^^^^^^^^^^^
AttributeError: 'str' object has no attribute 'n'
How do I convert 'Pkey' that exists as a string into a private key and decode 's' decoded with base64?
The following methods are used for decryption:
"RSA/ECB/PKCS1Padding"
Try saving the private key you posted first in a file with the name 'pkey.pem', and instead of writing the string directly into your code, use the following to upload the key:
Pkey = RSA.importKey(open('pkey.pem').read())
For more information you can look here, the documentation of pycryptodome.
edited according to Topaco's comments
Your code should looks as follows:
from Crypto.PublicKey import RSA
import base64
from Crypto.Cipher import PKCS1_v1_5
from Crypto.Random import get_random_bytes
s="AQEAN2+hgPwYzFATKPJfkXyTQKe9kvUTmT6LhASj2YI0T2KhBO7gSpKbNx/EXF4JYaWcwJVhJwiudgCFvoJMK9qJtnpLFmG12f8drygke+MPo5n2flHFPiKRmJCcCRM/VR8gL+xlbFIZNBL/o4onbqC1XfeQygiHe6tKXGiAZGXcJejbnob+/V+sL46x076KjurqLjcFMH+SCXomhuQZOiSqRqmeAsE6kL8wlj2yhTUfqAL/GuTTRziT6Syp0zJ7dprgCYOXBWbkLD9X6Vw39Db75kLd6Vx5zKT5jUQeU8eTN6pYfIiymlXwModf3TFBG1CObxzxzrevTXxFfIahFZGMAhDltmmcy7GUWDB7Qav24psONYaH+P69VfTimRzbrLMCfPb3zqp0cS8glMZ5YQuWqpigQRlQBhGq4rN9TGxBE3F1YRByBg+CHelBCHZj+2swHauVRmgy0CKU2/nmKpMrypKguFjjE6+bBur8b2AE28LSfjqxPZJx2BM="
code_bytes = s.encode('UTF-8')
by=base64.b64decode(code_bytes)
Pkey = RSA.importKey(open('pkey.pem').read())
key = PKCS1_v1_5.new(Pkey)
sentinel = get_random_bytes(16)
rsadecrypt=key.decrypt(by,sentinel)
Pay attention that you still going to get the following error:
ValueError: Ciphertext with incorrect length.
as Topaco explained in the comments
There are a few problems already noted in the comments by #Topaco:
Apart from the incorrect key import, the wrong padding is used. The
PyCryptodome counterpart to RSA/ECB/PKCS1Padding is PKCS1_v1_5 (and
not PKCS1_OAEP). Second, the ciphertext is apparently corrupted: The
posted (and thus compromised) private key has a length of 2048 bits =
256 bytes, i.e. the ciphertext must be of the same length. But the
posted (Base64 decoded) ciphertext is 380 bytes long (len(by)).
Furthermore, in decrypt() not str(by) but by must be passed.
You also have a typo in Pkey, the Base64 encoded body is ...boc61 and
not ...boc6 (i.e. the last character is missing). If this is fixed,
the key can be imported with RSA.importKey(base64.b64decode(Pkey))
When those are addressed we see that s, after base64 decoding, is too long to be the result of RSA encryption with a 2048 bit modulus. However by trying all offsets into the base64-decoded s and taking the next 256 bytes we get a successful decrypt at offset 3.
# https://stackoverflow.com/q/74840474/238704
import base64
from Cryptodome.Cipher import PKCS1_v1_5
from Cryptodome.PublicKey import RSA
from Cryptodome.Random import get_random_bytes
private_key_pem = '''-----BEGIN RSA PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCTiP1x58DboFjS
4WgzMm8tuY9VoHwANbOHNuuiElsZ4xIVFS+ZG7lu7Iz9gzmsno/YiqQXms8hXmUH
ouGgVJLJGPTw/NXAnLq6XlwB3C+zQMWpxvcMub4D6/IltP/PUpKNj9QzKKGhZF+6
s5B/QBzmSeNYlYYyGnO9GxyuHyR7P5xqF9AcpSskn9gjYy0koTLmvg/bwFx6jnci
np2qVHo5CFvSTAgrZKBCmKD2c2AGD9O+TSOrT0RDzvJosW4P1QJ9qsCjKFSPW/8K
IvB+AsSA+9xiVMDm/3YABBBM/R7wDcciTmKucZoEuDeWJg+cwLur8kKSVNwwkwCT
MqTIbHn5AgMBAAECggEABl5aemlDpPdl/ixmwBcEP5gL/OlBkQzAZCzVvRhHwHVR
2YEhnd1ZgtVJPMRGYBI0KWfKr44qNMmLWOoKDU65S179a82uOHNLiDH8jMQBdx5r
qemBzpXSAv9TY5dNl5h75Qp5YX/2gi4AB+IqcaPF25cC7lb+BPmpV1dtRILpozxJ
78mE0o0ddAJhg+0lIX6lZqjXbKJh4uiIqJIwezOM/B9U59qWEiJoHurkJIQoc00n
lp+XKUJfUWE5OHdy2DZNbAWEDqvFiD3DBd5baks/CPHy6IZLj7e5y+h/b65veDw6
MWpwXK30vroRmIzfNFcbONuQ3EjHBvCXzQYppUPnAQKBgQDD/k9NVQ8r2EnhjX0g
dQKobT7aCpY/y0QvDZ6aeuOVkfqnFB+rUUSf5WsXbANghqsHtzJa+cDUpP0C1PuE
hexGS+SsLywKI/33S014tvt5iF/MgEu08sLHtoxR/dH3H4k2LDGMNBrKILFnfDYP
40e3QKdogXMsEw4C0tb85YGrIQKBgQDAtJZp+onH0NSDXL0kbWZa8GFuVMGjTz2m
6E+j3NHK5GDGSAme755lDS61TTz2xNYaJFEwUdvYKQq1x/Q7jffKMPcHUJ5uy7bH
QjFho377rvN9bNSk+UdP8AkhvjaLNC0c5K5eNEUgumrd+yDLw2YNvjOe/RjE3QiW
zpGHLigL2QKBgQC2LUufMSJB+fBqlG6rXbgTwD/8wnx4XcNKDVnQc8vZenBHH0B0
qLyl98S8Z60X6vVM3a8TuzTPM3DuUfRccXN2wBKVLd+8qUnmtHsVatiDVgzd9J7q
WgBNTNRPXiPtlcWfsiJ4FPKV0R+1dlJ2ICfIIXO6gyD/5dJPM5WcSuRloQKBgQCq
Tu+gOgwKzEUE07FVwFn1hPyWxdFcw2t5V1cLOt0usyfUFVZrYNpb9IqTMO/wJ4YR
FIesbKwfHiaZAV6YQ5/60cuAa3+Z6/BdqeJ6qERRqw0GjGhiZOzheQKZD8KkxDga
kQCJwShXBGnuRUN20fofqzl0CbsaQT6WCXoUPnamAQKBgEY/ttlyLCrdHhamWlwN
ZkcgfsvQtLc//DGfEOSIiybs0eD0ORutxVQ2pwEgPI79xJTm0bdTHAFZVYziI9Lm
75k5WKxbBsYTgZm3i+LnnDtcHKPqkpXkq1v6WduTfaWpwMDJt0cJMOiTFzom76s1
mdFdmq2IXR+vXwl9f8fboc61
-----END RSA PRIVATE KEY-----'''
s = "AQEAN2+hgPwYzFATKPJfkXyTQKe9kvUTmT6LhASj2YI0T2KhBO7gSpKbNx/EXF4JYaWcwJVhJwiudgCFvoJMK9qJtnpLFmG12f8drygke+MPo5n2flHFPiKRmJCcCRM/VR8gL+xlbFIZNBL/o4onbqC1XfeQygiHe6tKXGiAZGXcJejbnob+/V+sL46x076KjurqLjcFMH+SCXomhuQZOiSqRqmeAsE6kL8wlj2yhTUfqAL/GuTTRziT6Syp0zJ7dprgCYOXBWbkLD9X6Vw39Db75kLd6Vx5zKT5jUQeU8eTN6pYfIiymlXwModf3TFBG1CObxzxzrevTXxFfIahFZGMAhDltmmcy7GUWDB7Qav24psONYaH+P69VfTimRzbrLMCfPb3zqp0cS8glMZ5YQuWqpigQRlQBhGq4rN9TGxBE3F1YRByBg+CHelBCHZj+2swHauVRmgy0CKU2/nmKpMrypKguFjjE6+bBur8b2AE28LSfjqxPZJx2BM="
code_bytes = s.encode('UTF-8')
by = base64.b64decode(code_bytes)
private_key = RSA.import_key(private_key_pem)
cipher = PKCS1_v1_5.new(private_key)
sentinel = get_random_bytes(16)
rsadecrypt = cipher.decrypt(by[3: 3 + 256], sentinel)
if rsadecrypt == sentinel:
print('failure')
else:
print(f'success: {rsadecrypt.hex(" ")}')
output is:
success: 48 90 c1 c5 ed fd 67 84 ad 82 df d1 5b 22 40 6f
I don't know what the rest of the bytes of s are all about.
PKCS1 version 1.5 encryption padding is not all that great and is basically deprecated in favor of OAEP padding. One of the weaknesses is the unacceptably high probability that a decryption with the wrong key and/or corrupted ciphertext will succeed. It's unlikely in this case, but not unlikely enough to completely discard the possibility. Although you've provided no additional details about what the payload is supposed to be, the 16 random-looking bytes suggests a key of some sort, perhaps an AES-128 key.

Convert Python PBKDF2HMAC to Ruby

I have the following in python and want to transfer this to ruby. but i don't get it working. The original source is from here: https://github.com/HurricaneLabs/splunksecrets/blob/177d350a9164791c66139d11b4c63f5db83710b6/splunksecrets.py#L112
My "secret" is : aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Python
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=b"disk-encryption",
iterations=1,
backend=default_backend()
)
key = kdf.derive(secret[:254])
print("KDF: %s" % key)
Ruby
require 'openssl'
kdf = OpenSSL::KDF.pbkdf2_hmac(
secret,
salt: 'disk-encryption',
iterations: 1,
length: 32,
hash: 'SHA256'
)
puts "KDF: #{kdf.unpack1('H*')}"
Start and end are somehow the same, but the middle is not matching at all. I removed the \x from the python string to better read it.
org py KDF: b'\x0e\x04\xc2^Z\x04t\xddCh\x97E\xf7\xc9%o\xff\xb8o\x96\x0f\x8aA\xablg\x06\x85\\\xc0\xa3\xde'
Python KDF: b'0e04c2 ^Z04tddCh97Ef7c9%offb8o960f8aAablg0685\\ c0a3de'
Ruby KDF: 0e04c2 5e5a0474dd43689745f7c9256fffb86f960f8a41ab6c6706855c c0a3de
as Topaco said in the comments: everything is okay, its only the printout format which was misleading.
The interpretation of Ruby unpack1('H*') in Python is .hex().
So it should be:
print("KDF: %s" % key.hex())
So the KDF implementation in Ruby was ok and working all the time. Only got confused by printing out the values in the wrong format.

Python HMAC hashed value encoding to base64

I am trying to make a twitter auth with the help of django middleware, where I calculate the signature of a request like this (https://dev.twitter.com/oauth/overview/creating-signatures):
key = b"MY_KEY&"
raw_init = "POST" + "&" + quote("https://api.twitter.com/1.1/oauth/request_token", safe='')
raw_params = <some_params>
raw_params = quote(raw_params, safe='')
#byte encoding for HMAC, otherwise it returns "expected bytes or bytearray, but got 'str'"
raw_final = bytes(raw_init + "&" + raw_params, encoding='utf-8')
hashed = hmac.new(key, raw_final, sha1)
request.raw_final = hashed
# here are my problems: I need a base64 encoded string, but get the error "'bytes' object has no attribute 'encode'"
request.auth_header = hashed.digest().encode("base64").rstrip('\n')
As you can see, there is no way to base64 encode a 'bytes' object.
The proposed solution was here: Implementaion HMAC-SHA1 in python
The trick is to use base64 module directly instead of str/byte encoding, which supports binary.
You can fit it like this (untested in your context, should work):
import base64
#byte encoding for HMAC, otherwise it returns "expected bytes or bytearray, but got 'str'"
raw_final = bytes(raw_init + "&" + raw_params, encoding='utf-8')
hashed = hmac.new(key, raw_final, sha1)
request.raw_final = hashed
# here directly use base64 module, and since it returns bytes, just decode it
request.auth_header = base64.b64encode(hashed.digest()).decode()
For test purposes, find below a standalone, working example (python 3 compatible, Python 2.x users have to remove the "ascii" parameter when creating the bytes string.):
from hashlib import sha1
import hmac
import base64
# key = CONSUMER_SECRET& #If you dont have a token yet
key = bytes("CONSUMER_SECRET&TOKEN_SECRET","ascii")
# The Base String as specified here:
raw = bytes("BASE_STRING","ascii") # as specified by oauth
hashed = hmac.new(key, raw, sha1)
print(base64.b64encode(hashed.digest()).decode())
result:
Rh3xUffks487KzXXTc3n7+Hna6o=
PS: the answer you linked to does not work anymore with Python 3. It's python 2 only.
Just thought I'd adjust the answer for Python3.
from hashlib import sha512
import hmac
import base64
key = b"KEY"
path = b"WHAT YOU WANT TO BE SIGNED"
hashed = hmac.new(key, path, sha512).digest()
print(base64.b64encode(hashed))

Encrypt string in Python

I need to encrypt a small string in Python. Is it possible to use a secret key to encrypt the string? Is there a good way to do this and achieve a reasonable encryption level using only Python libraries? Could you show me how to do this?
My knowledge about cryptography is quite basic.
Take a look at py-bcrypt. Perhaps it will meet your needs. From the web site:
py-bcrypt is a Python wrapper of OpenBSD's Blowfish password hashing code, as described in "A Future-Adaptable Password Scheme" by Niels Provos and David Mazières
KeyCzar has a nice interface and should meet your requirements. From the home page:
Keyczar is an open source cryptographic toolkit designed to make it easier and safer for devlopers to use cryptography in their applications. Keyczar supports authentication and encryption with both symmetric and asymmetric keys
crypter = Crypter.Read("/path/to/your/keys")
ciphertext = crypter.Encrypt("Secret message")
I solved this by using a lightweight XTEA library that i found on ASPN. It doesn't require any additional Python libraries and is quite simple to implement whilst acheiving a resonable encryption level.
I recently created a piece of code that does pretty much what your saying. It takes a codeword such as 'abc'gets the values (1, 2, 3) and then adds them to each letter in the word to encrypt. So if 'abc' was the codeword and 'bcd' the text to encrypt. (1+2 =3 2+3 =5 and 3+4 = 7) so the output would then be 'ceg'
codeword = input('Enter codeword : ')
codeword = codeword.replace(" ", "")
encrypt = input('Enter text to encrypt : ')
encrypt = encrypt.replace(" ", "")
j = 0
for i in codeword:
print(chr(ord(encrypt[j])+ ord(codeword[j])-96))
j+=1
Here's my code:
from cryptography.fernet import Fernet
import pyperclip
print("For this program to work, please send the file named 'pwkey' and the encrypted code to the other user.")
key = Fernet.generate_key()
file = open('pwkey', 'wb')
file.write(key)
file.close()
print('File Generated')
original = input('Enter message>>>')
message = original.encode()
f = Fernet(key)
encrypted = f.encrypt(message)
encrypted = encrypted.decode("ascii")
print('Encrypted:', encrypted)
pyperclip.copy(encrypted)
print('Please tell the other user to input the encrypted code in the Decrypt program')
print('(Code copied to Clipboard)')
print("Note: Please delete the 'pwkey' file after sending it to the other user. It
is for one-time use only.")
And decrypting
# Decrypt
from cryptography.fernet import Fernet
print("For this program to work, make sure you have the file named 'pwkey' in your Python folder and the encrypted "
"code.")
file = open('pwkey', 'rb')
key = file.read()
file.close()
print('Key Retrieved')
encrypted = input('Please input your encrypted code>>>')
encrypted = bytes(encrypted, 'utf-8')
f = Fernet(key)
decrypted = f.decrypt(encrypted)
decrypted = decrypted.decode()
print('Decrypted Message:')
print(decrypted)
print("Note: Please delete the 'pwkey' file after getting the decrypted message. It is for one-time use only.")
I would do this with the easy to use cryptocode library (https://github.com/gdavid7/cryptocode).
Encrypting and decrypting is relatively simple compared to other libraries:
## Encrypting:
>>> import cryptocode
>>> myEncryptedMessage = cryptocode.encrypt("I like trains", "password123")
>>> print(myEncryptedMessage)
M+Wykmlub0z7FhEdmA==*PvAbXRNx0SiSDHHxLsKZ5w==*ihQM/fdkgrX3G+yOItyAUQ==*QFNDmuUP1ysgo01/P2MNpg==
## Decrypting:
>>> import cryptocode
>>> myDecryptedMessage = cryptocode.decrypt("M+Wykmlub0z7FhEdmA==*PvAbXRNx0SiSDHHxLsKZ5w==*ihQM/fdkgrX3G+yOItyAUQ==*QFNDmuUP1ysgo01/P2MNpg==", "password123")
>>> print(myDecryptedMessage)
I like trains
The only large disadvantage to this library over others is that it does not have many options to change around your string compared to other cryptography libraries. But it is perfect for a lot of people who just want an abstraction, and don't need to configure custom settings.

How to generate SSH key pairs with Python

I'm attempting to write a script to generate SSH Identity key pairs for me.
from M2Crypto import RSA
key = RSA.gen_key(1024, 65337)
key.save_key("/tmp/my.key", cipher=None)
The file /tmp/my.key looks great now.
By running ssh-keygen -y -f /tmp/my.key > /tmp/my.key.pub I can extract the public key.
My question is how can I extract the public key from python? Using key.save_pub_key("/tmp/my.key.pub") saves something like:
-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADASDASDASDASDBarYRsmMazM1hd7a+u3QeMP
...
FZQ7Ic+BmmeWHvvVP4Yjyu1t6vAut7mKkaDeKbT3yiGVUgAEUaWMXqECAwEAAQ==
-----END PUBLIC KEY-----
When I'm looking for something like:
ssh-rsa AAAABCASDDBM$%3WEAv/3%$F ..... OSDFKJSL43$%^DFg==
Use cryptography! pycrypto is not in active development anymore and if possible you should be using cryptography. Since June it's possible to generate SSH public keys as well:
from cryptography.hazmat.primitives import serialization as crypto_serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.backends import default_backend as crypto_default_backend
key = rsa.generate_private_key(
    backend=crypto_default_backend(),
    public_exponent=65537,
    key_size=2048
)
private_key = key.private_bytes(
    crypto_serialization.Encoding.PEM,
    crypto_serialization.PrivateFormat.PKCS8,
    crypto_serialization.NoEncryption()
)
public_key = key.public_key().public_bytes(
    crypto_serialization.Encoding.OpenSSH,
    crypto_serialization.PublicFormat.OpenSSH
)
Note: You need at least version 1.4.0.
Note: If your SSH client does not understand this private key format, replace PKCS8 with TraditionalOpenSSL.
Just in case there are any future travellers looking to do this. The RSA module support writing out the public key in OpenSSH format now (possibly didn't at the time of earlier posts). So I think you can do what you need with:
from os import chmod
from Crypto.PublicKey import RSA
key = RSA.generate(2048)
with open("/tmp/private.key", 'wb') as content_file:
chmod("/tmp/private.key", 0600)
content_file.write(key.exportKey('PEM'))
pubkey = key.publickey()
with open("/tmp/public.key", 'wb') as content_file:
content_file.write(pubkey.exportKey('OpenSSH'))
The files are opened with a 'wb' as the keys must be written in binary mode.
Obviously don't store you're private key in /tmp...
Edit 05/09/2012:
I just realized that pycrypto already has this:
import os
from Crypto.PublicKey import RSA
key = RSA.generate(2048, os.urandom)
print key.exportKey('OpenSSH')
This code works for me:
import os
from Crypto.PublicKey import RSA
key = RSA.generate(2048, os.urandom)
# Create public key.
ssh_rsa = '00000007' + base64.b16encode('ssh-rsa')
# Exponent.
exponent = '%x' % (key.e, )
if len(exponent) % 2:
exponent = '0' + exponent
ssh_rsa += '%08x' % (len(exponent) / 2, )
ssh_rsa += exponent
modulus = '%x' % (key.n, )
if len(modulus) % 2:
modulus = '0' + modulus
if modulus[0] in '89abcdef':
modulus = '00' + modulus
ssh_rsa += '%08x' % (len(modulus) / 2, )
ssh_rsa += modulus
public_key = 'ssh-rsa %s' % (
base64.b64encode(base64.b16decode(ssh_rsa.upper())), )
The key used by ssh is just base64 encoded, i don't know M2Crypto very much, but after a quick overview it seems you could do what you want this way:
import os
from base64 import b64encode
from M2Crypto import RSA
key = RSA.gen_key(1024, 65537)
raw_key = key.pub()[1]
b64key = b64encode(raw_key)
username = os.getlogin()
hostname = os.uname()[1]
keystring = 'ssh-rsa %s %s#%s' % (b64key, username, hostname)
with open(os.getenv('HOME')+'/.ssh/id_rsa.pub') as keyfile:
keyfile.write(keystring)
I didn't test the generated key with SSH, so please let me know if it works (it should i think)
The base64 decoded version of ssh-keygen output to the contents of key.pub() the format of the keyfile is
b64encode('\x00\x00\x00\x07ssh-rsa%s%s' % (key.pub()[0], key.pub()[1]))
If you want, you could just also use ssh-keygen itself.
You can extend this to also create your file, and just use open to read the content later, but i focused on creating a .pub key from an already existing key here.
from subprocess import Popen, PIPE
import os
home = f'{os.path.expanduser("~")}'
cert_pos = f'{home}/.ssh/my_key'
your_key_pw = ''
cmd = ['ssh-keygen', '-y', '-f', cert_pos]
if your_key_pw:
cmd.append('-P')
cmd.append(your_key_pw)
p = Popen(cmd, stdout=PIPE)
p.wait()
res, err = p.communicate()
cert_content = res.decode('utf-8')
Here is an example using the Twisted Conch library which leverages PyCrypto under the covers. You can find the API documentation at http://twistedmatrix.com/documents/current/api/twisted.conch.ssh.keys.html:
from twisted.conch.ssh import keys
# one-time use key
k="""-----BEGIN RSA PRIVATE KEY-----
PRIVATE KEY STUFF
-----END RSA PRIVATE KEY-----"""
# create pycrypto RSA object
rsa = keys.RSA.importKey(k)
# create `twisted.conch.ssh.keys.Key` instance which has some nice helpers
key = keys.Key(rsa)
# pull the public part of the key and export an openssh version
ssh_public = key.public().toString("openssh")
print ssh_public
You can use pycryptodome as described in documentation:
from Crypto.PublicKey import RSA
key = RSA.generate(2048)
private_key = key.export_key()
file_out = open("private.pem", "wb")
file_out.write(private_key)
public_key = key.publickey().export_key()
file_out = open("receiver.pem", "wb")
file_out.write(public_key)
Just guessing... but have you tried something like this?:
print "ssh-rsa " + "".join([ l.strip() for l in open('/tmp/my.key.pub') if not l.startswith('-----')])
Can you get the AAAA...Dfg== string out of it while it's an object? If so, you could simply open a file yourself and save that instead of using the built in save_pub_key function.
I don't know of such a library that comes standard with Python.
If you want to look to third-party libraries, you might find the paramiko library useful (also available from PyPI). It implements the SSH protocol, and has functionality for handling existing keys, but not generating them.
Generation of keys might be a useful addition to that library (you could work with the developers to incorporate it into the Paramiko library), and an easier start than doing it from scratch.

Categories

Resources