PyCrypto export/import of signature - python

created a client-server application with sockets and I am trying to transfer the signature from client to server. I convert it from tuple to string and then back to tuple. But signing stops working. How to resolve this?
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
public_key_file = open('public.pem','r')
public_key = RSA.importKey(public_key_file.read())
signature = "(90392831408741910958006452852395405116864328891950288888434929210668328849466319419951775157374761930395371626801844365799774616689823184955256615103504859356914334395152128600862146719619859327119380994333493461955529620578485576675021993313219918726432622856542420570716350341841652548574072964446809201965L,)"
signature_tuple = signature.split(",")
message = "Block_Height:1 From:c52030257a864a67ae4ef8a726282ed2b6b273fbccb474885027a857 To:2 Amount:3"
if public_key.verify(message, signature_tuple) == True:
print "Signature valid"
.
Traceback (most recent call last):
File "C:\Users\kucerjan\Desktop\test\sco\public_test.py", line 12, in <module>
if public_key.verify(message, signature_tuple) == True:
File "build\bdist.win32\egg\Crypto\PublicKey\RSA.py", line 221, in verify
return pubkey.pubkey.verify(self, M, signature)
File "build\bdist.win32\egg\Crypto\PublicKey\pubkey.py", line 126, in verify
return self._verify(M, signature)
File "build\bdist.win32\egg\Crypto\PublicKey\RSA.py", line 257, in _verify
return self.key._verify(m, s)
File "build\bdist.win32\egg\Crypto\PublicKey\_slowmath.py", line 73, in _verify
return self._encrypt(sig) == m
File "build\bdist.win32\egg\Crypto\PublicKey\_slowmath.py", line 65, in _encrypt
return pow(m, self.e, self.n)
TypeError: unsupported operand type(s) for pow(): 'str', 'long', 'long'
This signature is already converted to string using str(signature). I basically need to convert it to string and back.
Function reference: https://www.dlitz.net/software/pycrypto/api/current/Crypto.PublicKey.RSA._RSAobj-class.html#verify
Public key:
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDFiMH7Lbd4JPFug8TaxX1DT8ad
lzzGm7CG1js0IQn2pCPPWBS+io1i0iUPmj78IOtUuoBqtEYGPgwqguYHozBuvdJy
Lcz4C2bYcjb2l8mQ4PM7iaCN4eHB+4xa+iJduogTjq8gx5m3j5mttEGUbZc2Q/AO
yde592P2iuRIrXcLuwIDAQAB
-----END PUBLIC KEY-----

The problem is in deserializing the signature tuple.
PyCrypto is expecting a tuple with an integer as the first value, you are passing it a string with a beginning paren "(" and then a string version of a number.
Instead of doing this:
signature_tuple = signature.split(",")
do this
signature_tuple = eval(signature)
That will properly parse the signature.
Now, there are security risks with using eval. So, if I were you, I'd come up with a better serialization/deserialization process.

The best way is to use PKCS1_v1_5 for real applications in combination with base64 for encoding and decoding the signature between client and server. No eval is needed.
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA
from Crypto.PublicKey import RSA
import base64
message = 'To be signed'
key = RSA.importKey(open('privkey.der').read())
h = SHA.new(message)
signer = PKCS1_v1_5.new(key)
signature = signer.sign(h)
signature_enc = str(base64.b64encode(signature))
#print signature_enc
signature_dec = str(base64.b64decode (signature_enc))
#print sugnature_dec
key = RSA.importKey(open('pubkey.der').read())
h = SHA.new(message)
verifier = PKCS1_v1_5.new(key)
if verifier.verify(h, signature_dec):
print "The signature is authentic."
else:
print "The signature is not authentic."

Related

'bytes' object has no attribute 'oid'

my code has an error in line 14 saying bytes object has no attribute oid. I am not sure why is it giving me this error.
from Crypto.PublicKey import DSA
from Crypto.Signature import DSS
import random
import os
def generate_dsa_key_pair(bits=1024):
"""Generates a DSA key pair with the given number of bits."""
key = DSA.generate(bits)
return key
def sign_text(text, key):
"""Signs the given text using the given DSA private key."""
signer = DSS.new(key, 'fips-186-3')
**signature = signer.sign(text.encode())**
return signature
def create_p2ms_script(n, m):
"""Creates a P2MS script using the given number of public keys and signatures."""
# Generate N DSA key pairs
keys = [generate_dsa_key_pair() for i in range(n)]
# Select M private keys from the N keys
priv_keys = random.sample(keys, m)
# Generate M signatures using the private keys
signatures = [sign_text("help me", priv_key) for priv_key in priv_keys]
# Create scriptPubKey by concatenating the N public keys
scriptPubKey = b''.join([key.publickey().export_key(format='DER') for key in keys])
# Create scriptSig by concatenating the M signatures
scriptSig = b''.join(signatures)
return scriptPubKey, scriptSig
def save_script_to_file(script, filename):
"""Saves the given script to the specified file."""
with open(filename, 'wb') as f:
f.write(script)
def execute_p2ms_script(scriptPubKey, scriptSig):
"""Executes the given P2MS script by verifying the signatures using the public keys."""
# Split scriptPubKey into individual public keys
pub_keys = [DSA.import_key(key) for key in scriptPubKey.split(b'\x00\x02\x81\x81') if key]
# Split scriptSig into individual signatures
signatures = [sig for sig in scriptSig.split(b'\x00\x02\x81\x81') if sig]
# Check if the number of signatures matches the number of public keys
if len(signatures) != len(pub_keys):
return False
# Verify each signature using the corresponding public key
for i, sig in enumerate(signatures):
verifier = DSS.new(pub_keys[i], 'fips-186-3')
if not verifier.verify("help me ".encode(), sig):
return False
return True
if __name__ == '__main__':
n = int(input("Enter the number of public keys (N): "))
m = int(input("Enter the number of signatures (M): "))
# Create P2MS script
scriptPubKey, scriptSig = create_p2ms_script(n, m)
# Save script
I have tried to hash the object but then my code wouldn't work. I have bolded the line, can someone please explain to me?
for context, my code is meant to replicate a P2MS function.
edit: the full traceback is as follows:
Exception has occurred: AttributeError
'bytes' object has no attribute 'oid'
File ", line 14, in sign_text signature = signer.sign(text.encode())
File , line 26, in <listcomp> signatures = [sign_text("help me", priv_key) for priv_key in priv_keys]
File , line 26, in create_p2ms_script signatures = [sign_text("help me", priv_key) for priv_key in priv_keys]
File , line 66, in <module> scriptPubKey, scriptSig = create_p2ms_script(n, m)
AttributeError: 'bytes' object has no attribute 'oid'
The bytes object is what's returned by text.encode(). It's a UTF-8 encoded string and it does not have an attribute oid.
That is not what DSS.sign(msg_hash) expects. It expects a hash object (which conveniently has an attribute oid), see also PyCryptoDome doc on sign. So a Crypto.Hash object has to be created first as described here:
from Crypto.Hash import SHA256
# ... line 14
hash_object = SHA256.new(data=text.encode())
signature = signer.sign(hash_object)
In your question, you said that passing a hash object did not work. However, that's the proper way to do it. You cannot make a wish on an API and expect things to work. See the API as a contract. It specifies what you need to give and what you get in return.
Make your code run without throwing exceptions. Once you're there and you still encounter errors after passing a hash, please post another question. A thorough explanation of expected and actual behavior beyond "would not work" would also be helpful.

Encrypt/Decrypt message using PGPy

I am trying out PGPy and I get an error whenever I encrypt or decrypt a message.
These are the codes that I use. Documentation and examples can be found here
Code to encrypt a message using the public key:
import pgpy
key_pub = '''BEGIN PUBLIC KEY BLOCK...END PUBLIC KEY BLOCK'''.lstrip()
message = "It worked!"
# import ASCII formatted public key
pub_key = pgpy.PGPKey()
pub_key.parse(key_pub)
# create new message
text_message = pgpy.PGPMessage.new(message)
# encrypt a message using pub key
encrypted_message = pub_key.encrypt(text_message)
print(encrypted_message)
This one gave the output but with a message
UserWarning: Selected compression algorithm not in key preferences
encrypted_message = pub_key.encrypt(text_message)
which I don't get why.
Code to decrypt the message using the priv key:
import pgpy
key_priv ='''*BEGIN PRIV KEY BLOCK...END PRIV KEY BLOCK'''.lstrip()
cipher_text = '''BEGIN PGP MESSAGE...END PGP MESSAGE'''.lstrip()
# import ASCII formatted private key
priv_key = pgpy.PGPKey()
priv_key.parse(key_priv)
message_from_blob = pgpy.PGPMessage.from_blob(cipher_text)
# decrypts a message using priv key
decrypted_message = priv_key.decrypt(cipher_text)
print(decrypted_message)
And this one does not work at all.This is the error I get.
Traceback (most recent call last):
File "C:/Users..practice.py", line 13, in <module>
decrypted_message = priv_key.decrypt(cipher_text)
File "C:\Users...venv\lib\site-packages\pgpy\decorators.py", line 126, in _action
self.check_attributes(key)
File "C:\Users...venv\lib\site-packages\pgpy\decorators.py", line 111, in check_attributes
raise PGPError("Expected: {attr:s} == {eval:s}. Got: {got:s}"
pgpy.errors.PGPError: Expected: is_unlocked == True. Got: False
Assuming that you have an RSA public and private key, encrypting a string or file can be done simply:
PUBLIC_KEY_FILE = 'path/to/keyfile/my_pub_key.asc'
pub_key, _ = pgpy.PGPKey.from_file(str(PUBLIC_KEY_FILE))
# Encrypt string
txt_msg = pgpy.PGPMessage.new("Hello PGPy World")
print('txt_msg.is_encrypted')
print(txt_msg.is_encrypted)
print('txt_msg.message')
print(txt_msg.message)
encrypted_txt_msg = pub_key.encrypt(txt_msg)
print('encrypted_txt_msg.is_encrypted')
print(encrypted_txt_msg.is_encrypted)
print('encrypted_txt_msg.message')
print(encrypted_txt_msg.message)
# Encrypt file
f_t_e = pgpy.PGPMessage.new(str(FILE_TO_ENCRYPT),file=True)
print('f_t_e.is_encrypted')
print(f_t_e.is_encrypted)
encrypted_f_t_e = pub_key.encrypt(f_t_e)
print('encrypted_f_t_e.is_encoded')
print(encrypted_f_t_e.is_encrypted)
Assuming you have the RSA private key associated with the above public key (in the recipient list) and a text file containing the passphrase, decrypting a string or file is quite straightforward:
# Load passphrase from file
with open(PASSPHRASE_FILE,'r') as ppfp:
PASSPHRASE = ppfp.readline().replace('\n','')
print(PASSPHRASE)
# Load private key
PRIVATE_KEY_FILE='path/to/keyfile/my_prv_key.gpg'
prv_key, _ = pgpy.PGPKey.from_file(str(PRIVATE_KEY_FILE))
# Unlock private key
print(prv_key.is_protected) # Should be True
with prv_key.unlock(PASSPHRASE):
print(prv_key.is_unlocked) #Should be True
# Decrypt string
decrypted_txt_msg = prv_key.decrypt(encrypted_txt_msg)
print('decrypted_txt_msg.is_encrypted')
print(decrypted_txt_msg.is_encrypted)
print('decrypted_txt_msg.message')
print(decrypted_txt_msg.message)
# Decrypt file
decrypted_f_t_e = prv_key.decrypt(encrypted_f_t_e)
print('decrypted_f_t_e.is_encrypted')
print(decrypted_f_t_e.is_encrypted)

urlsafe_b64decode: return binascii.a2b_base64(s)

I'm working on python cryptography by following tutorial.
here is the code:
import os
import base64
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
BASE_DESTINATION = os.path.dirname(os.path.abspath(__file__))
KEY_DESTINATION = os.path.join(BASE_DESTINATION, 'keys/')
def generate_key_from_password(path = KEY_DESTINATION):
passwordProvided = str(input("Enter Password: ")) # enter your password
password = passwordProvided.encode()
salt = os.urandom(16)
# salt = b'aeroplane'
kdf = PBKDF2HMAC(
algorithm= hashes.SHA256(),
length= 32,
salt= salt,
iterations= 100_000,
backend = default_backend()
)
key = base64.urlsafe_b64decode(kdf.derive(password)) # generated successed!
print("key: ", key)
generate_key_from_password()
when key is generated with base64.urlsafe_b64decode(kdf.derive(password)) I get error: binascii.Error: Invalid base64-encoded string: number of data characters (5) cannot be 1 more than a multiple of 4
I visited many answers but nothing quite really fit for my code. ie: I added '=' and '==' to password string to solve padding problem and adding padding by calculation password = str(password) + ('=' * len(password)) and but it didn't work either.
Here is the full error:
Enter Password: pass
Traceback (most recent call last):
File "gfp.py", line 33, in <module>
generate_key_from_password()
File "gfp.py", line 28, in generate_key_from_password
key = base64.urlsafe_b64decode(kdf.derive(password)) # generated successed!
File "C:\Users\user\Anaconda3\envs\IRIS-REC\lib\base64.py", line 133, in urlsafe_b64decode
return b64decode(s)
File "C:\Users\user\Anaconda3\envs\IRIS-REC\lib\base64.py", line 87, in b64decode
return binascii.a2b_base64(s)
binascii.Error: Invalid base64-encoded string: number of data characters (5) cannot be 1 more than a multiple of 4
The solution is provided by #Topaco in the comments. (Thank you!)
The problem was in the syntax.
it should be base64.urlsafe_b64encode(...) instead of base64.urlsafe_b64decode(kdf.derive(password)). I've seen the code multiple times but somehow I missed this everytime.
Thank you #topaco

ecdsa signing key format

I am trying to create a public/private key pair using python.
I have created a private key using the following method:
private_key = ''.join(['%x' % random.randrange(16) for x in range(0, 64)])
using this private key I have attempted to use a ecdsa graph to generate to corresponding public key
def privateKeyToPublicKey(s):
sk = ecdsa.SigningKey.from_string(s, curve=ecdsa.SECP256k1)
vk = sk.verifying_key
return ('\04' + sk.verifying_key.to_string())
I have not been able to create the signing key (sk) due to a formatting error where my string is in the wrong format. But I am not sure how/what format the string s should be for SigningKey to work.
I get the following error when running the script:
Traceback (most recent call last):
File "address.py", line 23, in <module>
privateKeyToPublicKey(private_key)
File "address.py", line 20, in privateKeyToPublicKey
sk = ecdsa.SigningKey.from_string(s, curve=ecdsa.SECP256k1)
File "/usr/local/lib/python3.6/dist-packages/ecdsa/keys.py", line
149, in from_string
assert len(string) == curve.baselen, (len(string), curve.baselen)
AssertionError: (64, 32)
Here's a more complete code sample for clarity about the answer. Python3.
from ecdsa import SigningKey, SECP256k1
import sha3, random, binascii
private_key = ''.join(['%x' % random.randrange(16) for x in range(0, 64)])
private_key = bytes(private_key, 'utf-8')
private_key = binascii.unhexlify(private_key)
priv = SigningKey.from_string(private_key, curve=SECP256k1)
pub = priv.get_verifying_key().to_string()
keccak = sha3.keccak_256()
keccak.update(pub)
address = keccak.hexdigest()[24:]
print(address, priv.to_string().hex())
I have realised my error, the input must be in bytes, the private key in hex format. In python2 you may use:
private_key.decode('hex')
or you may use
binascii.unhexlify
in python3

pysodium crypto_box_open throwing ValueError

I'm using libsodium with the python bindings in pysodium 0.6.6
When using crypto_box and crypto_box_open, I always get a ValueError. Here is a simple example:
import pysodium
import random
serverPK, serverSK = pysodium.crypto_box_keypair()
clientPK, clientSK = pysodium.crypto_box_keypair()
testText = "test message"
nonce1 = str(random.random())
nonce2 = str(random.random())
cipherText = pysodium.crypto_secretbox(testText,nonce1,clientPK)
message = pysodium.crypto_secretbox_open(cipherText, nonce2, clientSK)
print message
And here is the error:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/.../pysodium-0.6.6/pysodium/__init__.py", line 181, in crypto_box_open
__check(sodium.crypto_box_open(msg, padded, ctypes.c_ulonglong(len(padded)), nonce, pk, sk))
File "/Users/.../pysodium-0.6.6/pysodium/__init__.py", line 70, in __check
raise ValueError
ValueError
crypto_box_keypair() creates a key pair, to be used with crypto_box().
crypto_secretbox() is not asymmetric encryption: a single key is used both to encrypt and to decrypt. If this is actually what you want, the key can be generated that way:
key = pysodium.randombytes(pysodium.crypto_secretbox_KEYBYTES)
For a given key, the nonce has to be unique for each message, and has to be pysodium.crypto_secretbox_NONCEBYTES bytes long. It can be a simple counter, or a random nonce:
nonce = pysodium.randombytes(pysodium.crypto_secretbox_NONCEBYTES)
Unlike the secret key, a nonce can be public. But it has to be the same for encrypting and for decrypting:
cipherText = pysodium.crypto_secretbox(testText, nonce, key)
message = pysodium.crypto_secretbox_open(cipherText, nonce, key)
The libsodium documentation provides examples on how to use box and secretbox, which can be easily translated to pysodium equivalents.

Categories

Resources