I have private key with pem format.
-----BEGIN EC PRIVATE KEY-----
MHQCAQEEIBAFWFAlCWPb8IvM4yHLLKBIN/mEJU9cZnM5JD2U2EmAoAcGBSuBBAAK
oUQDQgAErMGUjbHcEf7Gk9gVOOlWdqSaGc0YhE5HBPqhoniBUG8MTx5AT7mxtuyn
QkydMeOciHyvvyU0gf81UW9udef2nA==
-----END EC PRIVATE KEY-----
I want to sign with ruby code like following.
require 'ecdsa'
def sign(str)
digest = Digest::SHA256.digest(str)
temp_key = str.size
signature = ECDSA.sign($group, $private_key, digest, temp_key)
end
I want to know how to code for reading PEM private key file and using to sign.
To parse your key stored in PEM format, you can use the openssl module: sudo gem install openssl
Using this module, you will extract the private key this way: OpenSSL::PKey::EC.new(pemcontent).private_key
Note that your private key is based on the secp256k1 elliptic curve:
% openssl ec -text 2>&1 << EOF | grep OID
-----BEGIN EC PRIVATE KEY-----
MHQCAQEEIBAFWFAlCWPb8IvM4yHLLKBIN/mEJU9cZnM5JD2U2EmAoAcGBSuBBAAK
oUQDQgAErMGUjbHcEf7Gk9gVOOlWdqSaGc0YhE5HBPqhoniBUG8MTx5AT7mxtuyn
QkydMeOciHyvvyU0gf81UW9udef2nA==
-----END EC PRIVATE KEY-----
EOF
ASN1 OID: secp256k1
Therefore, the first parameter to give to EDSA.sign() must be ECDSA::Group::Secp256k1. This is an object that contains the parameters that define this curve.
Finally, here is your code, on which I've made the changes needed to make it work:
require 'ecdsa'
require 'openssl'
def sign(str)
pemcontent = "-----BEGIN EC PRIVATE KEY-----
MHQCAQEEIBAFWFAlCWPb8IvM4yHLLKBIN/mEJU9cZnM5JD2U2EmAoAcGBSuBBAAK
oUQDQgAErMGUjbHcEf7Gk9gVOOlWdqSaGc0YhE5HBPqhoniBUG8MTx5AT7mxtuyn
QkydMeOciHyvvyU0gf81UW9udef2nA==
-----END EC PRIVATE KEY-----"
digest = Digest::SHA256.digest(str)
temp_key = str.size
signature = ECDSA.sign(ECDSA::Group::Secp256k1, OpenSSL::PKey::EC.new(pemcontent).private_key, digest, temp_key)
return signature
end
Note: the value of temp_key should be generated with a more random way than using the length of the message to sign, like you did in your example code (it is a very very bad idea since soon or later, you will sign two messages with the same length, and this could let your private key been discovered).
Related
I'm new to cryptography, sorry if I'm just trying to do something stupid.
So, don't hesitate to say if I tried to do something wrong or not in the right way or whatever it is.
I want to use RSA and I have two people: Alice and Bob.
At first, I wanted to encrypt the message with Alice's private key and later encrypt the encrypted message with Bob's public key, to safeguard the integrity/authenticity and confidentiality of the message.
I have learned that it is not possible to encrypt with the private key, the message needs to be signed and then verified.
I have seen that I need the signed message and the message non-signed to verify the signature.
According to my research at this point, I have two options:
Encrypt two messages one signed and one not and check the signature after the decryption,
Encrypt the concatenation of the message and the signed message with a separator, decrypt the text, get both with the separator, and after that check the signature.
I have decided the second option.
But with this method I have an error with the length that can be encrypted with the RSA key, maybe the right choice is to do as #Topaco said :
Encrypt the message
Sign the encrypted message
Give both to Bob
Verify the signature with the messages
Finally, decrypt the encrypted message?
But with this method, we have to send 2 different messages to Bob (?)
I feel like it's weird
Here is my code :
from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives.asymmetric import rsa, utils
# Generate private key for Alice
alice_private_key = rsa.generate_private_key(public_exponent=65537, key_size=4096)
# Get the public key for Alice
alice_public_key = alice_private_key.public_key()
# Generate private key for Bob
bob_private_key = rsa.generate_private_key(public_exponent=65537, key_size=4096)
# Get the public key for Bob
bob_public_key = bob_private_key.public_key()
# Sign the message using Alice's private key
message = b"Hello, world!"
signature = alice_private_key.sign(
message,
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA256()
)
# Concatenate the message and the signature using a separator
separator = b'|'
signed_message = message + separator + signature
# Encrypt the signed message using Bob's public key
ciphertext = bob_public_key.encrypt(
signed_message,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
# Print the ciphertext
print(ciphertext)
# Decrypt the package using Bob's private key
plaintext = bob_private_key.decrypt(
ciphertext,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
# Get the signature and message
# signature_length = 256 # assuming the signature is 256 bytes long
# signature = plaintext[-signature_length:]
# message = plaintext[:-signature_length]
# Split the plaintext to get the signature and message using the separator
message, signature = plaintext.split(separator)
# Verify the signature using Alice's public key
try:
alice_public_key.verify(
signature,
message,
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA256()
)
print("Message send by Alice !")
except InvalidSignature as e:
print("Message not send by Alice !")
Thank you in advance for your help !
With help from #Topaco the result is the working code below.
But it still feels weird to me to have to send 2 messages in order to ensure the authenticity / integrity and the confidentiality of the message.
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives.asymmetric import rsa
# Generate private key for Alice
alice_private_key = rsa.generate_private_key(public_exponent=65537, key_size=4096)
# Get the public key for Alice
alice_public_key = alice_private_key.public_key()
# Generate private key for Bob
bob_private_key = rsa.generate_private_key(public_exponent=65537, key_size=4096)
# Get the public key for Bob
bob_public_key = bob_private_key.public_key()
message = b"Hello, world!"
print(message)
# Encrypt the message using Bob's public key
encoded_message = bob_public_key.encrypt(
message,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
# Sign the encoded message using the alice's private key
signed_encoded_message = alice_private_key.sign(
encoded_message,
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA256()
)
# Print the encoded_message (To virtually send these to Bob)
print(encoded_message)
print(signed_encoded_message)
# Verify the signature using Alice's public key
alice_public_key.verify(
signed_encoded_message,
encoded_message,
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA256()
)
# If the previous block doesn't raise an InvalidSignature exception
# we can decrypt the encoded_message
# Decrypt the package using Bob's private key
decoded_message = bob_private_key.decrypt(
encoded_message,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
# Bob have the Alice's message
print(decoded_message)
I'm working with pycrpytodomex lib in python3.
Here I'm using a passphrase while generating an RSA key:
from Cryptodome.PublicKey import RSA
def encrypt(pass1):
key = RSA.generate(2048)
encrypted_key = key.exportKey(passphrase=pass1, pkcs=8, protection="scryptAndAES128-CBC").decode('utf')
return encrypted_key
I've put in a 24 char phassphrase, and this is the output:
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIFJTBPBgkqhkiG9w0BBQ0wQjAhBgkrBgEEAdpHBAswFAQIpUW82j76E18CAkAA
AgEIAgEBMB0GCWCGSAFlAwQBAgQQKDB2dU0KvLw9KvDq/pkRRgSCBNAru/ZpRI7o
pHIVVXb8iEuQUc+suZXwH9IBTJzzjMAq1iEETWSCzMB1VWz+PBJ8RmUB0qEie0Cf
Kg6/j0tw3dcZ7P48GXjzFvV4e5gaFtGy4khjGSa5/T97n/pi5oZZ7qsnJRktlxdS
1E2nceChpQwLUPdkVkeG5F3iWc2U9Pgh165m5GSg+ai3Dl7YpJEvhA7LQv55a4kZ
HuTjTnoMxakaA4ncYJoQhiZ0cGKZrnBoAWB7tAwVwnNwobTUpIuReeIJYd+B09gl
ZaiTTJQQAyJ60hRz+bQQioT2HBg1nXi+9KzwPw1AMUY0nU9Y8kUMNtjUzbv7Y2Av
2KiVtlFZ4ottihCWX9W85UQ4gQ4o4/ZyT5B4b1VMiUTO7pCaheHfqPokGU9/MzPO
aZCqDL43efebKqdJ6ZJr0aTkJlAGWxHLJrvd87Fh5yvPdkTvZh++0xHcmHigtpDB
Ufe+jJJE9Xr6VpOtecW8KkVhmC3I6MwnwyhNn3vtSRfGWZi/V23DpdwFUXb8gZSe
Qou7GdjMZ5VXalhrqbVmz1RaS4yTPkXhd71hHZHkWF6jQmNfIZaBAGum7FRF8zgz
PmHvb3nbfYAwHz/I/fHOqsFM8N9UvTnHK5JLSfhKosdUtrq3Hotu6iU7EzLgl7qt
k1yfVbKiyMUFOFbU4n8hmOKy7eVpv1UW7Dh4guq/9xd7vBRZC03gSUBhJY1lj45p
vMqeASkwVSENHoHWS+jkuPgq6IrgYsF3VPfUw3hy0xhj9PSZoArikMaifIRjVwwq
Ayltn5yAU0wbAygwrnHG64t9MdFnoy3RvWe43sG7D+6wzrz53tUKqujV1Ve7rSmG
G6HoXNvwKpVCdGaURbgpac+UaDFwSKkcPEReMMEw46HvH6/s1Ufxv/ecQb1ac0dh
DgkiSNUPSAPDOhRxiI/NzGxny50IkIcI8HwigYFZkTZn081kaps9NEV8L/KfzmNn
KnJO9H3018BMR3TAPGsntUunjfTS9bVoUlYCcbSX/Xpz+sGjWJnnF9KdbpUPLTS2
B/YwvPaU6EtoehfavWX5qIA670hiPSwSGlbdT7KVx5cTzDuk5gOQCFGNWoosoPCV
SyeNpS/IUbPpk6s2mvQRoX09XsfzTqYkApByBdErEHWinkSmyrPNRdi/NXBerWW2
EKPQQw6dwO6RkD9sAv9ncoFkW6b3kn7FiUoFEhUUeVi4tfP3YcSiKzcSvNxgdc9x
KuRzfq+HD8DgHxgY4wciJxcErK52/snrV0vvkf6l2IczQV2tw5tDWF476E0iI2cV
7+ycp9crAjfS9nuMUr2RTZV0x9J1jc8pIZyocTfLMPHaqR5P9LmKmDfoy1U29PKu
TiiFagL2Ukon1r1KAIDlfHZP6MfqTWfxyfhfAMzRCDgR4uJAZi+JEeiZg21Y7caI
tBN5A92Ymiw7jIalAdUT7t1JOwF635c/fc8m33JNYQF+v8GCDBRFoPN2YpsT5ZvJ
P2EbcCNJxcqrSrf4SJl+tPkSuJPCQfeyX3MmyI9TEN5BPlCSpsDsO6VzRuDuGkMq
/JwN4VTltFD02Zhl+KotUkEjKsWLzIcIvaMMIh9jEcT+8TvRnWgBbwTpgIw/LDtv
Gv6LSf9906/YcHVI1xJRF2yTTWVFdLF04Q==
-----END ENCRYPTED PRIVATE KEY-----
I am able to validate the passphrase with the encrycpted key string by using this function:
def decrypt(encoded_key,pass1):
try:
key = RSA.import_key(encoded_key, passphrase=pass1)
return True
except ValueError:
return False
Supposing one only has the private key and not the passphrase (ie, you). Would it still be possible to derive the passphrase using this private key?
Alternative: Would it be possible to construct any passphrase that will return True on the decrypt() function above?
Can you find out what the (or a valid) passphrase is for the above key? What computing power and time did it take?
scryptAndAES128-CBC
It is not possible to derive the password from the ciphertext (to our current knowledge)
Can you find out what the (or a valid) passphrase is for the above key
The problem with passwords are people. Reusing passwords, using simple passwords,..
Usually passwords are looked up using dictionaries and combination tools. So the question is how "guessable" or random password is used.
I'm trying to make a program that fetches someone's MAC Address from their machine, encrypts it, and then copies it to their clipboard. However, all of the encryption methods I see generate a fresh key and thus can't be deciphered without knowing the specific key that was used to encrypt the address. Is there a way to use one key to encrypt everything so all addresses can be decrypted with a single key, and a fresh key is not generated every single time?
you can try it, using Fernet Lib:
from cryptography.fernet import Fernet
# IMPORTANT: The encryption key must be binary, so the prefix 'b' before the string
# To create a random binary key, use 'generate_key' method as below:
# new_key = Fernet.generate_key()
crypto_key = b'dTlQeWw2u5oMoFPHXQ7vQHPaQUEiD71SYzWeJJAQQUk='
mac = '00:33:A4:D9:F1:E1'
fernet = Fernet(crypto_key)
enc_mac = fernet.encrypt(mac.encode())
dec_mac = fernet.decrypt(enc_mac).decode()
print(f'Fixed encryption key: {crypto_key}')
print('Original MAC string: ', mac)
print('Encrypted MAC string: ', enc_mac)
print('Decrypted MAC string: ', dec_mac)
You are describing asymmetric encryption here.
That exists and is a thing, yes. It works by by having a public key for encryption, and a private key for decryption.
There are multiple algorithms that implement that, like RSA.
RSA is supported by the python library cryptography.
A tutorial on how to use it can be found for example here:
https://nitratine.net/blog/post/asymmetric-encryption-and-decryption-in-python/
Working example
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import serialization, hashes
# Generate keys. This only has to be done once.
# Store the keys somewhere and distribute them with the program.
def generate_keys():
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend()
)
public_key = private_key.public_key()
private_key_string = private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=serialization.NoEncryption()
)
public_key_string = public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
)
return (public_key_string, private_key_string)
# This is just for demonstration.
# In practice, don't generate them every time.
# Only generate them once and store them in a string or a file.
(public_key_string, private_key_string) = generate_keys()
# REMOTE COMPUTER
# Only use the public key here, the private key has to stay private.
public_key = serialization.load_pem_public_key(public_key_string, backend=default_backend())
mac_address = "01:23:45:67:89:AB"
mac_address_encrypted = public_key.encrypt(
mac_address.encode(),
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
# LOCAL SERVER
# Use private keys here to decrypt the MAC address
private_key = serialization.load_pem_private_key(private_key_string, password=None, backend=default_backend())
mac_address_decrypted = private_key.decrypt(
mac_address_encrypted,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
).decode()
print(mac_address_decrypted)
https://ideone.com/0eEyU6
You can use Import RSA library rsa
installing :
pip install rsa
Then encrypt the byte string with the public key.
Then the encrypted string can be decrypted with the private key.
The public key can only be used for encryption and the private can only be used for decryption
for examle:
import rsa
publicKey, privateKey = rsa.newkeys(512)
message = "Salio" #this is MAC Address
encMessage = rsa.encrypt(message.encode(), publicKey)
print("encrypted: ", encMessage)
decMessage = rsa.decrypt(encMessage, privateKey).decode()
print("decrypted : ", decMessage)
I'm having some trouble creating Asn1 object with bouncy castle. I've generated an unused key with try to illustrate what I'm doing.
-----BEGIN OPENSSH PRIVATE KEY-----
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
-----END OPENSSH PRIVATE KEY-----
My program snippet has access to the bits between BEGIN and END (basically, above snippet without first and last line).
My code snippet looks something like this (copied from online tutorials) with the input privateKey being a multiline string (snippet above without first and last line, includes /r/n but I've tried removing the newlines).
byte[] keyBytes = Convert.FromBase64String(privateKey);
var privKeyObj = Asn1Object.FromByteArray(keyBytes);
var privStruct = RsaPrivateKeyStructure.GetInstance((Asn1Sequence)privKeyObj);
I'm getting [ERROR] FATAL UNHANDLED EXCEPTION: System.IO.IOException: extra data found after object. Does my input need to be further sanitized? I'm hoping to be able to create a private key to use to sign a JWT.
I'm trying to move my code from Python 2.7 to Python 3.5
Below is the current implementation in Python 2.7 which uses M2Crypto
import M2Crypto
import hashlib
from binascii import hexlify
# Generates the signature of payload
def getSign(payload_xml):
# SHA-1 digest of the payload
dig = myDigest(payload_xml)
# Loading the privateKey PEM file
private_key = M2Crypto.RSA.load_key('privatekey')
# Generating base 16 and encoding
signature = hexlify(private_key.private_encrypt(dig, M2Crypto.RSA.pkcs1_padding))
return signature
# To generate sha-1 digest of payload
def myDigest(payload):
# This will give base 16 of SHA-1 digest
digest_1 = hashlib.sha1(payload).hexdigest()
return digest_1
sign = getSign(<mypayload_xml>)
And this is the new implementation in Python 3.5 using pycryptodome
from Crypto.PublicKey import RSA
import hashlib
from Crypto.Cipher import PKCS1_v1_5
from binascii import hexlify
def myDigest(payload):
# This will give base 16 of SHA-1 digest
digest_1 = hashlib.sha1(payload.encode('utf-8')).hexdigest()
return digest_1
def getSign(payload_xml):
# SHA-1 digest of the payload
dig = myDigest(payload_xml)
with open('privatekey', 'r') as pvt_key:
miPvt = pvt_key.read()
rsa_key_obj = RSA.importKey(miPvt)
cipher = PKCS1_v1_5.new(rsa_key_obj)
cipher_text = cipher.encrypt(dig.encode())
base_16_new = hexlify(cipher_text)
return base_16_new
new_sign = getSign(<mypayload_xml>)
However, for same payload, signatures are different. Can someone help
with the proper solution?
As already mentioned in my comment, encrypt and decrypt of PyCryptodome can only be used to encrypt with the public key and decrypt with the private key. PyCryptodome has no 1:1-counterpart to private_encrypt or public_decrypt of M2Crypto, which allows the encryption with the private key and the decryption with the public key. Instead PyCryptodome uses sign and verify, which however work differently in detail, so that private_encrypt and sign don't generate the same signature (for the same key and message):
sign implements RSASSA-PKCS1-V1_5 padding described in RFC 8017, chapter 8.2. The hash value H of the message is padded as follows:
0x00 || 0x01 || PS || 0x00 || ID || H
ID identifies the digest and is for SHA1 (see here for other digests):
(0x)30 21 30 09 06 05 2b 0e 03 02 1a 05 00 04 14
PS are 0xFF fill bytes, so that the padded message has the length of the modulus.
The padding of private_encrypt differs from RSASSA-PKCS1-V1_5 padding in such a way that ID is not added automatically. So that sign and private_encrypt generate the same signature, ID must be added manually in the context of private_encrypt, e.g:
import M2Crypto
import hashlib
from binascii import hexlify, unhexlify
key = """-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----"""
def getSign(payload_xml):
dig = myDigest(payload_xml)
private_key = M2Crypto.RSA.load_key_string(key)
signature = hexlify(private_key.private_encrypt(unhexlify('3021300906052b0e03021a05000414' + dig.hexdigest()), M2Crypto.RSA.pkcs1_padding))
return signature
def myDigest(payload_xml):
digest_1 = hashlib.sha1(payload_xml)
return digest_1
sign = getSign(b"Hello world")
print("M2Crypto: " + sign)
As a site note, there is a bug in the original code: private_encrypt expects the data in binary format and not as hexadecimal string.
The corresponding PyCryptodome code could be e.g.:
from Crypto.Signature import pkcs1_15
from Crypto.Hash import SHA1
from Crypto.PublicKey import RSA
key = """-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----"""
def getSign(payload_xml):
dig = myDigest(payload_xml)
private_key = RSA.import_key(key)
signature = pkcs1_15.new(private_key).sign(dig)
return signature
def myDigest(payload_xml):
digest_1 = SHA1.new(payload_xml)
return digest_1
sign = getSign(b'Hello world')
print("PyCryptodome: " + sign.hex())
With the following test key (for simplicity a 512 bit key):
key = """-----BEGIN PRIVATE KEY-----
MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEA2gdsVIRmg5IH0rG3
u3w+gHCZq5o4OMQIeomC1NTeHgxbkrfznv7TgWVzrHpr3HHK8IpLlG04/aBo6U5W
2umHQQIDAQABAkEAu7wulGvZFat1Xv+19BMcgl3yhCdsB70Mi+7CH98XTwjACk4T
+IYv4N53j16gce7U5fJxmGkdq83+xAyeyw8U0QIhAPIMhbtXlRS7XpkB66l5DvN1
XrKRWeB3RtvcUSf30RyFAiEA5ph7eWXbXWpIhdWMoe50yffF7pW+C5z07tzAIH6D
Ko0CIQCyveSTr917bdIxk2V/xNHxnx7LJuMEC5DcExorNanKMQIgUxHRQU1hNgjI
sXXZoKgfaHaa1jUZbmOPlNDvYYVRyS0CIB9ZZee2zubyRla4qN8PQxCJb7DiICmH
7nWP7CIvcQwB
-----END PRIVATE KEY-----"""
both codes provide for the following message:
payload_xml = b'The quick brown fox jumps over the lazy dog'
the following signature:
8324a560e6934fa1d1421b9ae37641c3b50a5c3872beecea808fbfed94151747aad69d5e083a23aa0b134d9e8c65e3a9201bb22ec28f459e605692e53965ad3b
Conclusion: It is possible to modify the M2Crypto code so that the result corresponds to the PyCryptodome code by simply adding ID. The other way around, however, it doesn't seem to be possible, because the PyCryptodome implementation adds ID automatically and this apparently can't be prevented.
In the second snippet, you are encrypting the SHA-1 digest using the PKCS#1 1.5 algorithm (Crypto.Cipher.PKCS1_v1_5 module). That is not a signature.
Instead, you should use the Crypto.Signature.pkcs1_15 module of pycryptodome. For instance, see the example taken from here:
from Crypto.Signature import pkcs1_15
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
message = 'To be signed'
key = RSA.import_key(open('private_key.der').read())
h = SHA256.new(message)
signature = pkcs1_15.new(key).sign(h)