I have this python code:
import os
from base64 import b64encode
from typing import Union
from hashlib import sha1
from hmac import new
PREFIX = bytes.fromhex("42")
SIG_KEY = bytes.fromhex("F8E7A61AC3F725941E3AC7CAE2D688BE97F30B93")
DEVICE_KEY = bytes.fromhex("02B258C63559D8804321C5D5065AF320358D366F")
def deviceId(data: bytes = None) -> str:
if isinstance(data, str): data = bytes(data, 'utf-8')
identifier = PREFIX + (data or os.urandom(20))
mac = new(DEVICE_KEY, identifier, sha1)
return f"{identifier.hex()}{mac.hexdigest()}".upper()
def signature(data: Union[str, bytes]) -> str:
data = data if isinstance(data, bytes) else data.encode("utf-8")
return b64encode(PREFIX + new(SIG_KEY, data, sha1).digest()).decode("utf-8")
I need to convert it to kotlin code. After few hours i wrote this:
import java.util.*
import javax.crypto.Mac
import android.util.Base64
import javax.crypto.spec.SecretKeySpec
fun String.decodeTheHex(): ByteArray = chunked(2).map { it.toInt(16).toByte() }.toByteArray()
fun ByteArray.toHexString() = joinToString("") { "%02x".format(it) }
const val HMAC_SHA_1 = "HmacSHA1"
const val NDC_MSG_SIG_KEY = "F8E7A61AC3F725941E3AC7CAE2D688BE97F30B93"
const val DEVICE_ID_KEY = "02B258C63559D8804321C5D5065AF320358D366F"
fun getSignature(data: String): String {
val mac = Mac.getInstance("HmacSHA1")
mac.init(SecretKeySpec(NDC_MSG_SIG_KEY.decodeTheHex(), HMAC_SHA_1))
return Base64.encodeToString("42".decodeTheHex() + mac.doFinal(data.toByteArray()), Base64.NO_WRAP)
}
fun getDeviceId(): String {
val mac = Mac.getInstance("HmacSHA1")
mac.init(SecretKeySpec(DEVICE_ID_KEY.decodeTheHex(), HMAC_SHA_1))
val bytes = ByteArray(20)
Random().nextBytes(bytes)
val id = "42" + bytes.toHexString()
return (id + mac.doFinal("2".toByteArray(Charsets.UTF_8) + bytes).toHexString()).uppercase()
}
But the result after compiling is not correct (the app doesn't allow the result, i mean the app, for that i'm sending the request. And i know, that the result from function getDeviceId() is completly wrong). How to convert it?
Related
I'm trying to convert the following c# function to python
the orginal C# function
public static string Decrypt(string Key, string toDecrypt) // key.length = 16 Byte
{
if (Key != "") {
try {
byte[] toEncryptArray = Convert.FromBase64String(toDecrypt);
byte[] keyArray = UTF8Encoding.UTF8.GetBytes(Key);
TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
tdes.Key = keyArray;
tdes.Mode = CipherMode.ECB;
tdes.Padding = PaddingMode.Zeros;
ICryptoTransform cTransform = tdes.CreateDecryptor();
byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0,
toEncryptArray.Length);
tdes.Clear();
return UTF8Encoding.UTF8.GetString(resultArray);
} catch {
return "";
}
} else
return "";
}
that's my try in python
import base64
from Crypto.Cipher import DES3
from Crypto.Random import get_random_bytes
from Crypto.Util.Padding import pad, unpad
mac='OtTL6k9yLn6VJIZRXRj1OA=='
key='nOwy4iuE8c8WcktG'
toEncryptArray = base64.b64encode(mac.encode('utf-8'))
keyArray = key.encode('utf-8')
print(DES3.adjust_key_parity(keyArray))
cipher = DES3.new(keyArray, DES3.MODE_ECB)
decriptText = cipher.decrypt(toEncryptArray)
print(decriptText)
the result that i get in python is not equal to the c# result
I have this requirement where I need to encrypt a dictionary in python using password based AES256 encryption. I was only given a sample vb.net project by my boss to take reference from regarding the steps that I need to follow for encryption but I am not really aware of how vb.net works. I have converted as much code from vb.net to python as much I could but still the output that my code produces is different from the vb.net project. Can someone please explain what exactly am I missing out on due to which the encryption output of my python program is different than the encryption output of the vb.net program? Thanks in advance!
Here is the vb.net code
Imports System.IO
Imports System.Security.Cryptography
Imports System.Text
Imports System.Web.Script.Serialization
Imports System.Net
Imports System.Security.Cryptography.X509Certificates
Imports System.Net.Security
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
loginrequest()
End Sub
Private Sub loginrequest()
Dim lobjLogin As New loginrequest
lobjLogin.jsonProperty1 = "jsonProperty1"
lobjLogin.jsonProperty2 = "jsonProperty2"
lobjLogin.jsonProperty3 = "jsonProperty3"
lobjLogin.jsonProperty4 = "jsonProperty4"
Dim lRequestJson As String = ""
lRequestJson = (New JavaScriptSerializer()).Serialize(lobjLogin)
Dim lchecksum As String = EncryptText(lRequestJson, "key")
End Sub
Public Function EncryptText(pInput As String, password As String) As String
Dim bytesToBeEncrypted As Byte() = Encoding.UTF8.GetBytes(GenerateSHA256String(pInput))
Dim passwordBytes As Byte() = Encoding.UTF8.GetBytes(password)
passwordBytes = SHA256.Create().ComputeHash(passwordBytes)
Dim bytesEncrypted As Byte() = AES_Encrypt(bytesToBeEncrypted, passwordBytes)
Dim result As String = Convert.ToBase64String(bytesEncrypted)
Return result
End Function
Private Function AES_Encrypt(bytesToBeEncrypted As Byte(), passwordBytes As Byte()) As Byte()
Dim encryptedBytes As Byte() = Nothing
Dim saltBytes As Byte() = New Byte() {1, 2, 3, 4, 5, 6, _
7, 8}
Using ms As New MemoryStream()
Using AES As New RijndaelManaged()
AES.KeySize = 256
AES.BlockSize = 128
Dim key = New Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000)
AES.Key = key.GetBytes(AES.KeySize / 8)
AES.IV = key.GetBytes(AES.BlockSize / 8)
AES.Mode = CipherMode.CBC
Using cs = New CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write)
cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length)
cs.Close()
End Using
encryptedBytes = ms.ToArray()
End Using
End Using
Return encryptedBytes
End Function
Private Function GenerateSHA256String(ByVal inputString) As String
Dim sha256 As SHA256 = SHA256Managed.Create()
Dim bytes As Byte() = Encoding.UTF8.GetBytes(inputString)
Dim hash As Byte() = sha256.ComputeHash(bytes)
Dim stringBuilder As New StringBuilder()
For i As Integer = 0 To hash.Length - 1
stringBuilder.Append(hash(i).ToString("X2"))
Next
Return stringBuilder.ToString()
End Function
End Class
Public Class loginrequest
Public Property jsonProperty1 As String
Public Property jsonProperty2 As String
Public Property jsonProperty3 As String
Public Property jsonProperty4 As String
End Class
Here is the corresponding python code that I wrote
import base64
from Crypto.Cipher import AES
from Crypto.Protocol.KDF import PBKDF2
from Crypto.Util.Padding import pad
import hashlib, json
payload = {
"jsonProperty1": "jsonProperty1",
"jsonProperty2": "jsonProperty2",
"jsonProperty3": "jsonProperty3",
"jsonProperty4": "jsonProperty4",
}
payload = json.dumps(payload)
bytesToBeEncrypted = hashlib.sha256(payload.encode("utf-8")).hexdigest()
class AESCipher(object):
def __init__(self, key, interactions=1000):
self.bs = AES.block_size
self.key = hashlib.sha256(key.encode("utf-8")).hexdigest()
self.interactions = interactions
def pkcs7padding(self, data, block_size=16):
if type(data) != bytearray and type(data) != bytes:
raise TypeError("Only support bytearray/bytes !")
pl = block_size - (len(data) % block_size)
return data + bytearray([pl for i in range(pl)])
def encrypt(self, raw):
import os
raw = "".join([x.upper() for x in bytesToBeEncrypted])
keyiv = PBKDF2(self.key, os.urandom(8), 48, self.interactions)
key = keyiv[:32]
iv = keyiv[32:48]
cipher = AES.new(key, AES.MODE_CBC, iv)
encoded = raw.encode("utf-8")
encodedpad = self.pkcs7padding(encoded)
ct = cipher.encrypt((encodedpad))
cip = base64.b64encode(ct)
print(cip, len(cip))
enc = AESCipher("key")
dec = enc.encrypt(bytesToBeEncrypted)
Please note that I took some reference from some other threads as well regarding my python code because encryption is a new concept for me.
P.S. I also found out that the vb.net code is using .toString("X2") to generate a hexadecimal string in uppercase but unfortunately, I was not able to find the corresponding equivalent in python for the same. Could that be a problem?
<?php
$password = "SECRETPASSWORD";
$nonce = random_bytes(32); # requires PHP 7
date_default_timezone_set("UTC");
$timestamp = date(DATE_ATOM);
$encodedNonce = base64_encode($nonce);
$passSHA = base64_encode(sha1($nonce . $timestamp . sha1($password, true), true));
?>
it generates the below result with a 28 characters password digest, which I am using in soap requests, and it works fine
password_digest = '/pBYmwwc2cM87CUr8oB4Wkmyc0Q='
nonce = '���>�!��g��q�[�`�R��=J�o�'
nonce_base_64_encode = 'uqbkProhsR3JZxjC93HWW8BghQFSgqo9Sv9vGgUa4hs='
timestamp = '2022-01-13T18:28:52+00:00'
I need to do this same in python, but python is somehow generating longer password_digest and the soap request fails. I don't know if I am not generating the random_bytes correctly in python or some other issue. Below is python code:
import secrets
import hashlib
import datetime
clearPassword = 'MYSECRETPASSWORD'
created_at_timestamp_utc = datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")
def getSha1String(string):
stringInBytes = string.encode('ascii')
hash_object = hashlib.sha1(stringInBytes)
hex_dig = hash_object.hexdigest()
return hex_dig
def getBase64NonceString(nonce):
nonce_bytes = nonce.encode('ascii')
base64_bytes = base64.b64encode(nonce_bytes)
base64_nonce = base64_bytes.decode('ascii')
return base64_nonce
def getBase64String(string):
string_bytes = string.encode('ascii')
base64_bytes = base64.b64encode(string_bytes)
base64_string = base64_bytes.decode('ascii')
return base64_string
nonce = secrets.token_bytes(32)
base64_nonce = getBase64Nonce(nonce)
sha1_password = getSha1String(clearPassword)
password_digest = getBase64String(getSha1String(str(nonce) + created_at_timestamp_utc + sha1_password))
Your python code has 3 problems:
You're using binary output from sha1() in PHP, but hex output in python. Use digest(), not hexdigest(). This is why your output is longer in python.
Your timestamp format is incorrect.
The PHP format DATE_ATOM is "Y-m-d\TH:i:sP", where P outputs the UTC offset in the format +00:00. Unfortunately Python's strftime() doesn't seem to have an equivalent, but it's all in UTC anyway and your python code simply specifies the static string Z. So change that to +00:00, otherwise your tokens won't match.
Eg: timestamp = datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S+00:00')
You're using SECRETPASSWORD in PHP and MYSECRETPASSWORD in python, and I am emabarrased at how long I bashed my head against that one without noticing.
Succint working code:
import hashlib, datetime, base64
password = 'SECRETPASSWORD'
timestamp = '2022-01-13T18:28:52+00:00'
nonce = base64.b64decode('uqbkProhsR3JZxjC93HWW8BghQFSgqo9Sv9vGgUa4hs=')
def quickSHA(input):
return hashlib.sha1(input).digest()
def makeToken(password, timestamp, nonce):
return base64.b64encode(
quickSHA( nonce + timestamp + quickSHA(password) )
)
print makeToken(password, timestamp, nonce)
Output: /pBYmwwc2cM87CUr8oB4Wkmyc0Q=
My NodeJS & Python scripts don't return the same hash, what could cause this issue?
Node.js
const { createHmac } = require("crypto");
var message = 'v1:1583197109:'
var key = 'Asjei8578FHasdjF85Hfjkasi875AsjdiAas_CwueKL='
const digest = Buffer.from(key, "base64");
const hash = createHmac("sha256", digest)
.update(message)
.digest("hex");
console.log(hash)
> 7655b4f816dc7725fb4507a20f2b97823979ea00b121c84b76924fea167dcaf7
Python3
message = 'v1:1583197109:'
key = 'Asjei8578FHasdjF85Hfjkasi875AsjdiAas_CwueKL=' + '=' #add a "=" to avoid incorrect padding
digest = base64.b64decode(key.encode('utf-8'))
hash_ = hmac.new(digest, message.encode('utf-8'), hashlib.sha256)
hash_result = hash_.hexdigest()
print(hash_result)
> c762b612d7c56d3f9c95052181969b42c604c2d41b7ce5fc7f5a06457e312d5b
I guess it could be the extra = to avoid the incorrect padding but my key ends with a single =.
Node.js Buffer.from(..., 'base64') can consume the input in the "urlsafe" base64 (https://nodejs.org/api/buffer.html#buffer_buffers_and_character_encodings), and _ is not a valid Base64 character for python, while it is for node.
Adding altchars that correspond to the "urlsafe" version of Base64 to python code yields equal hashes.
const { createHmac } = require("crypto");
var message = 'v1:1583197109:'
var key = 'Asjei8578FHasdjF85Hfjkasi875AsjdiAas_CwueKL='
const digest = Buffer.from(key, "base64");
const hash = createHmac("sha256", digest)
.update(message)
.digest("hex");
console.log(hash) // 7655b4f816dc7725fb4507a20f2b97823979ea00b121c84b76924fea167dcaf7
message = 'v1:1583197109:'
key = 'Asjei8578FHasdjF85Hfjkasi875AsjdiAas_CwueKL=' + '=' #add a "=" to avoid incorrect padding
digest = base64.b64decode(key.encode('utf-8'), altchars='-_')
hash_ = hmac.new(digest, message.encode('utf-8'), hashlib.sha256)
hash_result = hash_.hexdigest()
print(hash_result) # 7655b4f816dc7725fb4507a20f2b97823979ea00b121c84b76924fea167dcaf7
Also, python's b64decode has validate kwarg, which would check the input string and "fail loud" instead of ignoring incorrect characters
I am trying to convert a base64 string back to a GUID style hex number in python and having issues.
Base64 encoded string is: bNVDIrkNbEySjZ90ypCLew==
And I need to get it back to: 2243d56c-0db9-4c6c-928d-9f74ca908b7b
I can do it with the following PHP code but can't work out how to to it in Python
function Base64ToGUID($guid_b64) {
$guid_bin = base64_decode($guid_b64);
return join('-', array(
bin2hex(strrev(substr($guid_bin, 0, 4))),
bin2hex(strrev(substr($guid_bin, 4, 2))),
bin2hex(strrev(substr($guid_bin, 6, 2))),
bin2hex(substr($guid_bin, 8, 2)),
bin2hex(substr($guid_bin, 10, 6))
));
}
Here is the GUIDtoBase64 version:
function GUIDToBase64($guid) {
$guid_b64 = '';
$guid_parts = explode('-', $guid);
foreach ($guid_parts as $k => $part) {
if ($k < 3)
$part = join('', array_reverse(str_split($part, 2)));
$guid_b64 .= pack('H*', $part);
}
return base64_encode($guid_b64);
}
Here are some of the results using some of the obvious and not so obvious options:
import base64
import binascii
>>> base64.b64decode("bNVDIrkNbEySjZ90ypCLew==")
'l\xd5C"\xb9\rlL\x92\x8d\x9ft\xca\x90\x8b{'
>>> binascii.hexlify(base64.b64decode("bNVDIrkNbEySjZ90ypCLew=="))
'6cd54322b90d6c4c928d9f74ca908b7b'
Python port of the existing function (bitstring required)
import bitstring, base64
def base64ToGUID(b64str):
s = bitstring.BitArray(bytes=base64.b64decode(b64str)).hex
def rev2(s_):
def chunks(n):
for i in xrange(0, len(s_), n):
yield s_[i:i+n]
return "".join(list(chunks(2))[::-1])
return "-".join([rev2(s[:8]),rev2(s[8:][:4]),rev2(s[12:][:4]),s[16:][:4],s[20:]])
assert base64ToGUID("bNVDIrkNbEySjZ90ypCLew==") == "2243d56c-0db9-4c6c-928d-9f74ca908b7b"
First off, the b64 string and the resultant GUID doesn't match if we decode properly.
>>> import uuid
>>> import base64
>>> u = uuid.UUID("2243d56c-0db9-4c6c-928d-9f74ca908b7b")
>>> u
UUID('2243d56c-0db9-4c6c-928d-9f74ca908b7b')
>>> u.bytes
'"C\xd5l\r\xb9Ll\x92\x8d\x9ft\xca\x90\x8b{'
>>> base64.b64encode(u.bytes)
'IkPVbA25TGySjZ90ypCLew=='
>>> b = base64.b64decode('bNVDIrkNbEySjZ90ypCLew==')
>>> u2 = uuid.UUID(bytes=b)
>>> print u2
6cd54322-b90d-6c4c-928d-9f74ca908b7b
The base64 encoded version of the resultant GUID you posted is wrong. I'm not sure I understand the way you're encoding the GUID in the first place.
Python has in its arsenal all the tools required for you to be able to answer this problem. However, here's the rough scratching I did in a python terminal:
import uuid
import base64
base64_guid = "bNVDIrkNbEySjZ90ypCLew=="
bin_guid = base64.b64decode(base64_guid)
guid = uuid.UUID(bytes=bin_guid)
print guid
This code should give you enough of a hint to build your own functions. Don't forget, the python shell gives you a powerful tool to test and play with code and ideas. I would investigate using something like IPython notebooks.
I needed to do this to decode a BASE64 UUID that had been dumped from Mongodb. Originally the field had been created by Mongoose. The code I used, based on the code by #tpatja is here:
def base64ToGUID(b64str):
try:
bytes=base64.urlsafe_b64decode(b64str)
except Exception as e:
print("Can't decode base64 ", e)
s = bitstring.BitArray(bytes).hex
return "-".join([s[:8],s[8:][:4],s[12:][:4],s[16:][:4],s[20:]])
Based on good answers above, I wrote a version that does not require the bitstring package and includes validations and support for more input options.
import base64
import regex
import uuid
from typing import Optional
def to_uuid(obj) -> Optional[uuid.UUID]:
if obj is None:
return None
elif isinstance(obj, uuid.UUID):
return obj
elif isinstance(obj, str):
if regex.match(r'[0-9a-fA-F]{8}[-]{0,1}[0-9a-fA-F]{4}[-]{0,1}[0-9a-fA-F]{4}[-]{0,1}[0-9a-fA-F]{4}[-]{0,1}[0-9a-fA-F]{12}', obj):
return uuid.UUID(hex=obj)
elif regex.match(r'[0-9a-zA-Z\+\/]{22}[\=]{2}', obj):
b64_str = base64.b64decode(obj).hex()
uid_str = '-'.join([b64_str[:8], b64_str[8:][:4], b64_str[12:][:4], b64_str[16:][:4], b64_str[20:]])
return uuid.UUID(hex=uid_str)
raise ValueError(f'{obj} is not a valid uuid/guid')
else:
raise ValueError(f'{obj} is not a valid uuid/guid')