I'm trying to RSA encrypt a word 2 characters at a time padding with a space using Python but not sure how I go about it.
For example if the encryption exponent was 8 and a modulus of 37329 and the word was 'Pound' how would I go about it? I know I need to start with pow(ord('P') and need to take into consideration that the word is 5 characters and I need to do it 2 characters at a time padding with a space. I'm not sure but do I also need to use <<8 somewhere?
Thank you
Here's a basic example:
>>> msg = 2495247524
>>> code = pow(msg, 65537, 5551201688147) # encrypt
>>> code
4548920924688L
>>> plaintext = pow(code, 109182490673, 5551201688147) # decrypt
>>> plaintext
2495247524
See the ASPN cookbook recipe for more tools for working with mathematical part of RSA style public key encryption.
The details of how characters get packed and unpacked into blocks and how the numbers get encoded is a bit arcane. Here is a complete, working RSA module in pure Python.
For your particular packing pattern (2 characters at a time, padded with spaces), this should work:
>>> plaintext = 'Pound'
>>> plaintext += ' ' # this will get thrown away for even lengths
>>> for i in range(0, len(plaintext), 2):
group = plaintext[i: i+2]
plain_number = ord(group[0]) * 256 + ord(group[1])
encrypted = pow(plain_number, 8, 37329)
print group, '-->', plain_number, '-->', encrypted
Po --> 20591 --> 12139
un --> 30062 --> 2899
d --> 25632 --> 23784
If you want to efficiently code the RSA encryption using python, my github repository would definitely to understand and interpret the mathematical definitions of RSA in python
Cryptogrphic Algoritms Implementation Using Python
RSA Key Generation
def keyGen():
''' Generate Keypair '''
i_p=randint(0,20)
i_q=randint(0,20)
# Instead of Asking the user for the prime Number which in case is not feasible,
# generate two numbers which is much highly secure as it chooses higher primes
while i_p==i_q:
continue
primes=PrimeGen(100)
p=primes[i_p]
q=primes[i_q]
#computing n=p*q as a part of the RSA Algorithm
n=p*q
#Computing lamda(n), the Carmichael's totient Function.
# In this case, the totient function is the LCM(lamda(p),lamda(q))=lamda(p-1,q-1)
# On the Contrary We can also apply the Euler's totient's Function phi(n)
# which sometimes may result larger than expected
lamda_n=int(lcm(p-1,q-1))
e=randint(1,lamda_n)
#checking the Following : whether e and lamda(n) are co-prime
while math.gcd(e,lamda_n)!=1:
e=randint(1,lamda_n)
#Determine the modular Multiplicative Inverse
d=modinv(e,lamda_n)
#return the Key Pairs
# Public Key pair : (e,n), private key pair:(d,n)
return ((e,n),(d,n))
Related
I'm signing my messages using my code below:
def sign_msg_hash(self, msg_hash: HexBytes):
signature = self._kms_client.sign(
KeyId=self._key_id,
Message=msg_hash,
MessageType="DIGEST",
SigningAlgorithm="ECDSA_SHA_256",
)
act_signature = signature["Signature"]
return (act_signature)
However, when trying to do the following:
sign = kms.sign_msg_hash(transaction)
vks = ecdsa.VerifyingKey.from_public_key_recovery_with_digest(
sign, transaction, curve=ecdsa.SECP256k1, hashfunc=sha256
)
I get the following error:
ecdsa.util.MalformedSignature: Invalid length of signature, expected 64 bytes long, provided string is 72 bytes long
Now, when trying to use sign[:64] instead, it sometimes works but at other times gives me the following error:
raise SquareRootError("%d has no square root modulo %d" % (a, p))
ecdsa.numbertheory.SquareRootError: 6631794589973073742270549970789085262483305971731159368608959895351281521222 has no square root modulo 115792089237316195423570985008687907853269984665640564039457584007908834671663
I'm not sure if this has anything to do with the encoding of the signature from the KMS or not.
So after some digging, it turned it out it was a matter of encodings, as the kms sign function returns the signature in DER format.
Hence, what I did was the following:
I took the signature and split it, and got the r and s values as follows:
decoding=ecdsa.util.sigdecode_der(sign,int("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141",16))
I then concatenated the result (note that r and s should be in big-endian order):
new_sign = decoding[0].to_bytes(32, byteorder= 'big') + decoding[1].to_bytes(32, byteorder= 'big')
I then used the new sign:
vks = ecdsa.VerifyingKey.from_public_key_recovery_with_digest(
new_sign, transaction, curve=ecdsa.SECP256k1, hashfunc=sha256
)
Another thing I encountered was with kms_client.get_public_key()
First call the method:
kms_pub_key_bytes = kms_client.get_public_key(KeyId=key_id)["PublicKey"]
Then get the last 128 chars which are essentially the r and s values
kms_pre_sha3 = kms_pub_key_bytes.hex()[-128:]
Then do what you want with the public key.
Related blog post on Medium
I using getting the encrypted message from a third party sever.
I am using the tools in enter link description here
to test and its correct
The Encrypted Text =
"WiI9g5qo+ztSlqHMbpiezHZ2dBkQ2gprGJZyWtwcMTWPoxzLsMmujE9xDeFK4XYMfBdZGh2naMwP3LfbPy/06mazrSs66WRM1oxhz56L2UzTKyCWCl+ld7RlN7aPwfEw2j9VN50YCkMLfQRfIAXTspKQb6o5QQw8ey0cINdtWSHClz/uXiCFqiYJfItxY1rAZkE1Qj0b0izGQFJ9/44Zfw0dJtzCXYgXTPZftPeGTdoX/HnZJpUvfqmLIAdgAyoXi5BxL5bgSs30yaB4bRxJJj7DKpVbAgZmx0ecjmiGDh7t78A16pZ2kz+OIUkuc/hxvUaVehsH1pVdqycpUJfbgy+to0AY/+BBd38GGvv8YdTCa99bSHRGaZuUglLKN/2J0pZmfrIARIdgrV2yDK+IN4hTVKf1jprtfvhvkG+eRyDfoLL9rg8+ZEtdYUdZgDdF3ftmHKTzgxI6leMWX7WFRTHjxVYFVk0yWA9xXk6s/WcG6IFeGYPVF94IcLeC2eAjaMasusF+C6qyFWi6nuyFK2Gr1utvG6kg84Hu0KKYg42MHXIR1AtQW3MWaqosb54y0GutQtnD47l84/PdJvUhuE/a7uyfCjjtyh2sRRLX3WDosyRZsqjLea9EIX6oNmQMZd1WRxM86Ggt6bVOc9KY5Z7HLpLyb0lLF4sdyzfBNJB7u7vqkBzsEss1Yq+sXD0N"
Key = "1234567812345678"
And the result
After AES Decrypted Output (Base64):
And after Decode Plain Text
THe result is :
{"upPacketSN":-1,"upDataSN":-1,"topic":"v1/up/ad","timestamp":1619672797621,"tenantId":"2000034792","serviceId":"","protocol":"lwm2m","productId":"15044315","payload":{"APPdata":"MTIzNDU2NzgsNTM0OCwwNywwOCwyNTUsMTc5LDEyOCwwMDAwLDQyNTksMSw2LDAsMCwwLDAsMCwwLDI0LDMuNzAsMy43MCwxLDEsNiwsMCwwLDAsMCwwLDAsMCwwLDAsMCwtMjU1LC0yNTUsLTI1NSwxLjY5LDEuOCw0OTk4LDYwLDE0NDAsNjA="},"messageType":"dataReport","deviceType":"","deviceId":"523ede8bb7e34dd4a1bd74028d63749e","assocAssetId":"","IMSI":"undefined","IMEI":"864162041961023"}
One of the thing is that IV must be NULL to get the correct answer.
And so if i implement in Python AES CBC using pycryptodome library in the following like:
class AES_CBC:
def add_to_16(self, value):
while len(value) % 16 != 0:
value += '\0'
return str.encode(value) # 返回bytes
#解密方法
def decrypt_oralce(self, key, text):
# 初始化加密器
# 偏移量 16个0
iv = "0000000000000000"
aes = AES.new(self.add_to_16(key), AES.MODE_CBC, self.add_to_16(iv))
#优先逆向解密base64成bytes
base64_decrypted = base64.decodebytes(text.encode(encoding='utf-8'))
#
decrypted_text = str(aes.decrypt(base64_decrypted), encoding='utf-8') # 执行解密密并转码返回str
unpad = lambda s : s[0:-ord(s[-1])]
#PADDING = '\0'
#print decrypted_text.rstrip(PADDING) #zeropadding只见诶去掉结尾\0
# print(unpad(decrypted_text))
return unpad(decrypted_text)
if __name__ == '__main__':
aes = AES_CBC()
#加密
key = "1234567812345678"
enc_msg = "WiI9g5qo+ztSlqHMbpiezHZ2dBkQ2gprGJZyWtwcMTWPoxzLsMmujE9xDeFK4XYMfBdZGh2naMwP3LfbPy/06mazrSs66WRM1oxhz56L2UzTKyCWCl+ld7RlN7aPwfEw2j9VN50YCkMLfQRfIAXTspKQb6o5QQw8ey0cINdtWSHClz/uXiCFqiYJfItxY1rAZkE1Qj0b0izGQFJ9/44Zfw0dJtzCXYgXTPZftPeGTdoX/HnZJpUvfqmLIAdgAyoXi5BxL5bgSs30yaB4bRxJJj7DKpVbAgZmx0ecjmiGDh7t78A16pZ2kz+OIUkuc/hxvUaVehsH1pVdqycpUJfbgy+to0AY/+BBd38GGvv8YdTCa99bSHRGaZuUglLKN/2J0pZmfrIARIdgrV2yDK+IN4hTVKf1jprtfvhvkG+eRyDfoLL9rg8+ZEtdYUdZgDdF3ftmHKTzgxI6leMWX7WFRTHjxVYFVk0yWA9xXk6s/WcG6IFeGYPVF94IcLeC2eAjaMasusF+C6qyFWi6nuyFK2Gr1utvG6kg84Hu0KKYg42MHXIR1AtQW3MWaqosb54y0GutQtnD47l84/PdJvUhuE/a7uyfCjjtyh2sRRLX3WDosyRZsqjLea9EIX6oNmQMZd1WRxM86Ggt6bVOc9KY5Z7HLpLyb0lLF4sdyzfBNJB7u7vqkBzsEss1Yq+sXD0N"
#解密
dec_text = aes.decrypt_oralce(key, enc_msg)
print(key)
print(dec_text)
EDITED:
i got a different result using same IV 16'0'
and
the result from the web is
{"upPacketSN":-1,"upDataSN":-1,"topic":"v1/up/ad","timestamp":1619687373640,"tenantId":"2000034792","serviceId":"","protocol":"lwm2m","productId":"15044315","payload":{"APPdata":"MTIzNDU2NzgsNTM0OCwwNywwOCwyNTUsMTc5LDEyOCwwMDAwLDQ1MDQsMSw2LDAsMCwwLDAsMCwwLDI0LDMuNzAsMy43MCwxLDEsNiwsMCwwLDAsMCwwLDAsMCwwLDAsMCwtMjU1LC0yNTUsLTI1NSwxLjY5LDEuOCw0OTk4LDYwLDE0NDAsNjA="},"messageType":"dataReport","deviceType":"","deviceId":"523ede8bb7e34dd4a1bd74028d63749e","assocAssetId":"","IMSI":"undefined","IMEI":"864162041961023"}
while the result of my code is :
KE#`QS[UDc~
1,"upDataSN":-1,"topic":"v1/up/ad","timestamp":1619687373640,"tenantId":"2000034792","serviceId":"","protocol":"lwm2m","productId":"15044315","payload":{"APPdata":"MTIzNDU2NzgsNTM0OCwwNywwOCwyNTUsMTc5LDEyOCwwMDAwLDQ1MDQsMSw2LDAsMCwwLDAsMCwwLDI0LDMuNzAsMy43MCwxLDEsNiwsMCwwLDAsMCwwLDAsMCwwLDAsMCwtMjU1LC0yNTUsLTI1NSwxLjY5LDEuOCw0OTk4LDYwLDE0NDAsNjA="},"messageType":"dataReport","deviceType":"","deviceId":"523ede8bb7e34dd4a1bd74028d63749e","assocAssetId":"","IMSI":"undefined","IMEI":"864162041961023"}
can anyone help me which part i get wrong?
Thanks
Currently the code is not using a "null-IV", which means an array filled with bytes set to zero. Instead it is using an array filled with '0' characters, which have value 0x30 in hexadecimals or 48 in decimals; distinctly not zero.
To create a null-IV please have a look here on how to indicate byte values within byte arrays in Python. The same trick is used within the add_to_16 loop, where the \0 escape is used to indicate a zero byte.
Note that padding a key or IV is very bad practice. Those need to consist of randomized bytes. Beware that working crypto code is not the same thing as secure crypto code, which should probably be your goal.
for a university exercise I want to develop a simple hotp server-client system in python. In this case the client sends a password and a one time password to the server. The server knows the secret, calculates the current hotp and compares the values it receives. So far, so good. With plaintext this works perfectly fine and the calculated values are the same I get when I use the iOS App "OTP Auth". But there is also the possibility to calculate the OTP in combination with base32. So I added a few lines to encode the plaintext to base32 but now the output in not correct.
Let's assume we're using the secret "1234" so the plaintext output would be "110366". That's working. But if I'm encoding the secret to base32 the output should be "807244" but my program calculates "896513". Anybody know why this is happening?
I've already tried to use different secrets and checked it on different apps. Always the same result.
import hmac
import hashlib
import array
import base64
counter = 0
digits = 6 #Anzahl der Zeichen
def hotp(secret, c):
global digits
counter = extendCounter(c)
hmac_sha1 = hmac.new(secret, counter, hashlib.sha1).hexdigest()
return truncate(hmac_sha1)[-digits:]
def truncate(hmac_sha1):
offset = int(hmac_sha1[-1], 16)
binary = int(hmac_sha1[(offset * 2):((offset * 2) + 8)], 16) & 0x7fffffff
return str(binary)
def extendCounter(long_num):
byte_array = array.array('B')
for i in reversed(range(0, 8)):
byte_array.insert(0, long_num & 0xff)
long_num >>= 8
return byte_array
def main():
secret = "1234"
bSecret = secret.encode("UTF-8")
bSecret = base64.b32encode(bSecret)
otp = hotp(bSecret, counter)
one_time_password = otp
I expect 807244 as the output but the output is 896513
First, it's important to point out that the result of secret.encode('UTF-8') has exactly the same type as the result of base64.b32encode(bSecret) (and for that matter base64.b64encode(bSecret)) -- they all return bytes objects. Also worth noting is that the implementation of hmac in Python has no mention of base64/base32 encoding. So the short answer is that your expected result of 807244 is only valid if the shared secret is a base64/UTF-8 encoded blob.
This quick snippet shows that really you can give any bytes you like to hotp and it will come up with some result (because hotp is called multiple times in the example, counter is changed)
# ... everything from your example above ...
secret = "1234"
secret_bytes = secret.encode("UTF-8")
secret_bytes
>>> b'1234'
b32_secret = base64.b32encode(bSecret)
b32_secret
>>> b'GEZDGNA='
b64_secret = base64.b64encode(bSecret)
b64_secret
>>> b'MTIzNA=='
hotp(secret_bytes, counter) # just a UTF-8 blob works
>>> '110366'
hotp(b32_secret, counter) # base32/UTF-8 also works
>>> '896513'
hotp(b64_secret, counter) # base64/UTF-8 works as well
>>> '806744'
If you have more detail of why you expected 807244 for a base32/UTF8 blob, I'll be happy to amend this answer.
Found the mistake:
Instead of translating the secret to base32, the secret must be a Base32 decoded value. Also instead of encoding this value, it must be decoded ("base64.b32decode(bytes(saved_secret, 'utf-8'))")
So the correct main looks like this:
def main():
secret = "V6X27L5P" #Base32 value
secret = base64.b32decode(bytes(secret, 'utf-8'))
one_time_password = hotp(secret, counter)
Given a public key exponent and modulus like the following, how can I encrypt a string and send it to a server as text?
publicKey: 10001,
modulus: 'd0eeaf178015d0418170055351711be1e4ed1dbab956603ac04a6e7a0dca1179cf33f90294782e9db4dc24a2b1d1f2717c357f32373fb3d9fd7dce91c40b6602'
I am trying to replicate the functionality provided by the javascript rsa library http://www.ohdave.com/rsa/ in python. In javascript, it looks something like this:
setMaxDigits(67); //sets a max digits for bigInt
var key = new RSAKeyPair('10001', '10001', 'd0eeaf178015d0418170055351711be1e4ed1dbab956603ac04a6e7a0dca1179cf33f90294782e9db4dc24a2b1d1f2717c357f32373fb3d9fd7dce91c40b6602');
var encrypted = encryptedString(key, 'message');
console.log(encrypted); //prints '88d58fec172269e5186592dd20446c594dbeb82c01edad41f841666500c9a530e24a282c6527ec66f4c826719f12478c6535bdc2baef86e4ff26906a26398413'
I imagine there is a way to do this with the PyCrypto library but I couldn't find any examples that use the exponent and modulus.
Edit 1:
Using the solution below, it appears to be working. Since I'm using python 2.7 I modified it to look like this:
from Crypto.PublicKey.RSA import construct
from binascii import unhexlify
from codecs import encode
e = long(10001)
n = int(encode('d0eeaf17801.....5d041817005535171', 'hex'), 16)
key = construct((n, e))
a = key.encrypt('hello', None)
print(a)
('.X?\xdc\x81\xfb\x9b(\x0b\xa1\xc6\xf7\xc0\xa3\xd7}U{Q?\xa6VR\xbdJ\xe9\xc5\x1f\x
f9i+\xb2\xf7\xcc\x8c&_\x9bD\x00\x86}V[z&3\\]_\xde\xed\xdc~\xf2\xe1\xa9^\x96\xc3\
xd5R\xc2*\xcb\xd9\x1d\x88$\x98\xb0\x07\xfaG+>G#\xf7cG\xd8\xa6\xf3y_ 4\x17\x0b\x0
3z\x0cvk7\xf7\xebPyo-\xa1\x81\xf5\x81\xec\x17\x9e\xfe3j\x98\xf2\xd5\x80\x1d\xdd\
xaf\xa4\xc8I\xeeB\xdaP\x85\xa7',)
Now I want to convert this encrypted text to a string to send via a post request. But this doesn't seem to work:
a.decode('utf-8')
With PyCrypto, you can use the Crypto.PublicKey.RSA.construct() function. You'll need to convert the modulus to an int. Here's an example (assuming big-endian):
from Crypto.PublicKey.RSA import construct
e = int('10001', 16)
n = int('d0eeaf...0b6602', 16) #snipped for brevity
pubkey = construct((n, e))
Then you can do the usual things (like encrypt) with the key:
from Crypto.Cipher import PKCS1_OAEP
cipher = PKCS1_OAEP.new(pubkey)
ciphertext = cipher.encrypt(b'abcde')
Edit: Note that your public exponent, 10001, is mostly likely hexadecimal. This would correspond to the common public exponent 65537. I've updated the above to reflect that.
I tried an alternative way using Crypto.Cipher.PKCS1_OAEP motivated by: https://cryptobook.nakov.com/asymmetric-key-ciphers/rsa-encrypt-decrypt-examples and it just worked.
PS: There seems to be something wrong with modulus given, as modulus n must be the product of two large primes, thus should not be an even number. A tiny modification of n has been applied to make the example code runnable.
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
import binascii
e = int('10001', 16)
n = int('d0eeaf178015d0418170055351711be1e4ed1dbab956603ac04a6e7a0dca1179cf33f90294782e9db4dc24a2b1d1f2717c357f32373fb3d9fd7dce91c40b6601', 16)
# Construct a `RSAobj` with only ( n, e ), thus with only PublicKey
rsaKey = RSA.construct( ( n, e ) )
pubKey = rsaKey.publickey()
print(f"Public key: (n={hex(pubKey.n)}, e={hex(pubKey.e)})")
# Export if needed
pubKeyPEM = rsaKey.exportKey()
print(pubKeyPEM.decode('ascii'))
# Encrypt message using RSA-OAEP scheme
msg = b'Hello, world.'
encryptor = PKCS1_OAEP.new(pubKey)
encrypted = encryptor.encrypt(msg)
print("Encrypted:", binascii.hexlify(encrypted))
Why is this transposition decryption code not working with some keys?
def transencrypt(word,key):
'''Traspositon encryption function. This function is used to encrypt a line
using the transposition encryption method. To know how transpositon encryption
works you can visit here https://en.wikipedia.org/wiki/Transposition_cipher.'''
count1=0
count2=0
encrypted=''
encryptbox=['']*key
while count1<key:
count2=count1
while count2<len(word):
encryptbox[count1]+=word[count2]
count2+=key
encrypted+=encryptbox[count1]
count1+=1
return encrypted
def transdecrypt(word,key):
'''This Function is for the decrypting the encrypted strings encrypted by
transencrypt().This function only requires the encrypted string and the key
with which it has been decrypted.'''
import math
count1=0
count2=0
decrypted=''
col=int(math.ceil(len(word)/key))
decryptbox=['']*col
while count1<col:
count2=count1
while count2<len(word):
decryptbox[count1]+=word[count2]
count2+=col
decrypted+=decryptbox[count1]
count1+=1
return decrypted
print(transencrypt('hello world',5))
print(transdecrypt('h dewlolrol',5))
OP's original code source
I have tried encrypting "hello world" with key 5, but at the time of decrypting I am getting the wrong result. Using other keys works fine.
The problem is that the string length (11) doesn't divide evenly into the key (5), so the string "hello world" encodes into the groups h d-ew-lo-lr-ol i.e. "h dewlolrol". Which is fine, but the decrypt routine chops "h dewlolrol" into h d-ewl-olr-ol and generates the wrong result, "heoo wlldlr".
A couple of possible ways to fix this:
1) Replace the encryptbox string with an array and pad the encryption units into even width segments: h d-ew -lo -lr -ol i.e. "h dew lo lr ol "
This will allow your decrypt routine to work but you'll end up with spaces at the end of the decryption and the encrypted string will be a different size than the original.
OR
2) Dynamically adjust your decryption logic to figure out, based on the length of the remaining string to decode, and remaining number of expected segments, how much the segments must shrink. This means your decrypt routine can't be as similar to the encrypt routine as it is now. But it will allow you to handle the output of the current encrypt routine and the encrypted string can remain the same length as the original.
Below is a rough rework along the lines of approach #2 above -- you can see it allows the encrypt routine to remain simple but the decrypt routine has to be more complex to make up for it:
import math
def transencrypt(string, key):
'''
Transpositon encryption function. This function is used to encrypt a line
using the transposition encryption method. To learn how transpositon encryption
works you can visit here https://en.wikipedia.org/wiki/Transposition_cipher.
'''
encrypted = ''
length = len(string)
for start in range(key):
for offset in range(start, length, key):
encrypted += string[offset]
return encrypted
def transdecrypt(string, key):
'''
This function is for the decrypting the strings encrypted by
transencrypt(). This function only requires the encrypted
string and the key with which it was decrypted.
'''
decrypted = ''
length = len(string)
width = int(math.ceil(length / key))
for start in range(width):
offset = start
remaining_key = key
remaining_length = length
remaining_width = width
while offset < length:
decrypted += string[offset]
offset += remaining_width
remaining_key -= 1
if remaining_key > 0:
remaining_length -= remaining_width
remaining_width = int(math.ceil(remaining_length / remaining_key))
return decrypted[:length]
if __name__ == '__main__':
import sys
string = sys.argv[1]
key = int(sys.argv[2])
print(transencrypt(string, key))
print(transdecrypt(transencrypt(string, key), key))
**OUTPUT*
> python3 test.py "hello world" 5
h dewlolrol
hello world
>