I'm trying to connect to a api in my python app .
so the api documentations comes with php and asp sample code but no python
I'm pretty good with php but have no experience with encryption ... I'm trying to re-write python code for the api using php sample .
They use this class for RSA
https://github.com/AlaFalaki/Pclass/blob/master/libraries/rsa.class.php
(Since its a RSA lib im guessing python RSA lib would take care of this part ) :
static function rsa_sign($message, $private_key, $modulus, $keylength) {
$padded = RSA::add_PKCS1_padding($message, false, $keylength / 8);
$number = RSA::binary_to_number($padded);
$signed = RSA::pow_mod($number, $private_key, $modulus);
$result = RSA::number_to_binary($signed, $keylength / 8);
return $result;
}
Here is the problem php sign function takes 4 arguments uses some internal functions ... But python rsa has 3 and one of them is just the hash method !
rsa.sign(message, priv_key, hash)
Parameters:
message – the message to sign. Can be an 8-bit string or a file-like object. If message has a read() method, it is assumed to be a file-like object.
priv_key – the rsa.PrivateKey to sign with
hash – the hash method used on the message. Use ‘MD5’, ‘SHA-1’, ‘SHA-256’, ‘SHA-384’ or ‘SHA-512’.
i've tried little experiment to see if i get same output
so i've singed a simple text string in php
echo base64_encode(RSA::rsa_sign(sha1("test"),$private_key,$modulus,$key_length));
i got
something like
dKt+4CocMNdIrtYCUr8aZykR8CpfmYUEEVONMuAPlM5mR70AoyzMhGjcEGB9fKLVC4rr5xt66w2ZmHqWO+p834rJmo9Fj57udRSY5wFs0VokMF2S2SMFn5WTYYmMBuWciRzZybWnfXcSIyp9Ibi28cdwl5hXJOMpXEJrNQLFy2s=
next i extracted private_key , public_key , modulus from a xml file that they gave me with api containing my keys ( using the same RSA class ) like
$xmlObj = simplexml_load_string($xmlRsakey);
$this->modulus = RSA::binary_to_number(base64_decode($xmlObj->Modulus));
$this->public_key = RSA::binary_to_number(base64_decode($xmlObj->Exponent));
$this->private_key = RSA::binary_to_number(base64_decode($xmlObj->D));
$this->key_length = strlen(base64_decode($xmlObj->Modulus))*8;
i made a python dictionary with them
def keys():
obj = {
'modulus' : "14417185111734127374105962730273......." ,
'public_key' : "61111" ,
'private_key' : "3739752306322843055980611965983321761993....." ,
'key_length' : 1024 ,
}
return obj
and i've tried to sign a string in python
def sign(request):
api = keys()
message = 'test'
crypto = rsa.sign(message.encode('utf-8'), api['private_key'] , 'SHA-1')
b64 = base64.b64encode(crypto)
return HttpResponse(b64)
but i get :
'str' object has no attribute 'n'
and that was my failed experiment
As i said i dont have any experience with encryption or rsa .... i want some advice from someone who worked with this stuff .
Should i give up and use php to encrypt/decrypt ?
They use this class for RSA
https://github.com/AlaFalaki/Pclass/blob/master/libraries/rsa.class.php
My advice: Run away screaming.
RSA is a mine field of security issues. There are a lot of things that you can screw up. So when someone implements the primitives in PHP using the BC extension, that's the security equivalent of standing naked in front of a firing squad and expecting to have no holes.
Encrypting with PKCS1 padding allows near-trivial message decryption
Screwing up your parameters can completely remove all security from your crypto
Home-grown RSA is ripe with side-channel attacks
Recommendation: Use Libsodium Instead
There are both PHP and Python bindings available for libsodium.
If RSA is Unavoidable...
If you really want RSA and not modern cryptography, check out phpseclib.
<?php
use
$rsa = new RSA();
// HIGHLY RECOMMENDED FOR SECURITY:
$rsa->setEncryptionMode(RSA::ENCRYPTION_OAEP);
$rsa->setMGFHash('sha256');
$rsa->loadKey($yourPEMencodedRSAPublicKey);
$ciphertext = $rsa->encrypt($plaintext);
If you're going to encrypt with RSA, you must follow these cryptography rules. If your library doesn't let you follow these rules, it's time to switch to libsodium.
Also note that encrypting large messages with RSA is both slow and dangerous: There's usually nothing preventing messages from being reordered, which can be really bad.
The solution here is: Use symmetric-key authenticated encryption and use RSA to encrypt the public key. That's what EasyRSA does, although I'm not aware of any Python equivalents, so I can't recommend that as a solution.
Of course, if you use libsodium's crypto_box API you don't have to worry about that!
Related
I am trying to encrypt a password to sent to through an API for authentication. My situation is quite similar to another encrypt example
The python part is form see the get_pwd_rsa function
Python part:
import rsa
key = rsa.PublicKey(n, e)
encropy_pwd = rsa.encrypt(message, key)
binascii.b2a_hex(encropy_pwd)
And I have try using phpseclib to solve my problem, but the encrypt result is not correct:
use phpseclib\Crypt\RSA;
$rsa = new RSA();
$rsa->loadKey([
'n' => new \phpseclib\Math\BigInteger($n, 16),
'e' => new \phpseclib\Math\BigInteger($e, 16),
]);
$ciphertext = $rsa->encrypt($message);
return bin2hex($ciphertext);
The encrypt result is not correct and I'm not sure which part am I missing.
To add to what James said, my suspicion is that whatever Python library you're using is doing PKCS1 padding if it's doing any padding at all.
Contrast this with phpseclib which uses OAEP padding by default. PKCS1 padding can be enabled but it defaults to the more secure OAEP padding.
Further, both PKCS1 and OAEP encryption are randomized. I say that because I kinda wonder if you're saying the PHP code is wrong because it doesn't give the same result as the python code. It shouldn't. If you run a round of RSA encryption with phpseclib twice with the same plaintext and key each time you'll get a different ciphertext each time. PKCS1 and OAEP were designed this way to protect against known plaintext attacks.
I'm trying to create a JWT (JSON web token) in a node service which then needs to be checked in a PHP service.
I'm creating the token as per the spec as far as I can tell, and I'm encrypting the signature with the Node crypto library. I've read that the only algorithm that'll work reliably between these technologies is aes-128-cbc so that's the one I'm using.
I had some luck using mcrypt_decrypt, but it was still not 100% correct which is still a fail. Also because that library is deprecated I'd rather use openssl_decrypt, which I cannot get to work at all, it simply returns false.
The secret and init vector are both stored in a database in fields of type varchar(16), so they are the same in both bits of code. I'm using a 16byte blocksize so matching that with 16byte secret and iv.
I've tried different combinations of binary, hex and base64 formats but cannot get the openssl_decrypt function to return anything but false.
This question comes down to how can I encrypt a string in node and decrypt it in PHP? Or what is wrong with my current usage of these methods?
Node v7.4.0
var crypto = require('crypto');
var secret = crypto.randomBytes(16);
var iv = crypto.randomBytes(16);
var header = { type:'JWT', alg: 'aes-128-cbc' };
var payload = { iss: 'auth-token', exp: Date.now() + 86400, token: <some uuid> };
var data = new Buffer(JSON.stringify(header)).toString('base64') + '.' + new Buffer(JSON.stringify(payload)).toString('base64');
var cipher = crypto.createCipheriv('aes-128-cbc', secret, iv);
var encrypted = cipher.update(data, 'utf8', 'base64') + cipher.final('base64');
var JWT = data + '.'+ encrypted;
PHP v7.0.13 (also tried v7.1.1)
list($header64, $payload64, $sigEnc) = explode('.', $_POST['jwt']);
$header = base64_decode ($header64);
$payload = base64_decode ($payload64);
$signature = openssl_decrypt($sigEnc, 'aes-128-cbc', $secret, null, $iv); // secret and iv are both straight out of the database
Update
I've changed my objective here now and used a hash, which is possibly the correct way. So in the Node service I create a SHA256 hash of the base64 header and payload using a random key stored in the database. Then in the PHP service I do the same and compare the hashes. This is a better approach, which I should have taken before.
But there is still the question of how can you reliably encrypt a string in Node and decrypt it in PHP?
There are libraries but they seem a little overkill for what should be fairly straightforward.
It should be fairly straightforward but apparently it isn't.
With cryptography you either understand it well enough to implement the algorithms yourself from scratch, or you don't try because it's too risky to get it wrong. It's bad enough when you make a mistake like you did when nothing works, but it is way worse when it seems to work but is weak and vulnerable to some attack that you didn't think about.
If you're serious about security then use the right tool for the job. In Node you have:
https://www.npmjs.com/package/jwt
https://www.npmjs.com/package/jsonwebtoken
https://www.npmjs.com/package/jwt-simple
https://www.npmjs.com/package/express-jwt
and many more.
For PHP you have:
https://github.com/firebase/php-jwt
https://github.com/namshi/jose
https://github.com/lcobucci/jwt
https://github.com/emarref/jwt
https://github.com/Spomky-Labs/jose
https://github.com/nov/jose-php
See https://jwt.io/ for more info, more tools and more tutorials.
If you want to learn how to do it correctly yourself without using a library, then read the source code of those libraries - they are all open source, free software.
I am migrating an entire PHP API, and while I used PyCrypto before, I am not sure how to translate the following encryption call, since I need the exact same result. The PHP call is:
define('KEY', "-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC81t5iu5C0JxYq5/XNPiD5ol3Z
w8rw3LtFIUm7y3m8o8wv5qVnzGh6XwQ8LWypdkbBDKWZZrAUd3lybZOP7/82Nb1/
noYj8ixVRdbnYtbsSAbu9PxjB7a/7LCGKsugLkou74PJDadQweM88kzQOx/kzAyV
bS9gCCVUguHcq2vRRQIDAQAB
-----END PUBLIC KEY-----");
$cypher = "";
$result = openssl_public_encrypt($plain, $cypher, KEY, OPENSSL_PKCS1_PADDING);
echo bin2hex($cypher);
Assuming everything goes right, this prints the content from $cypher, passed to hexadecimal. For a sample input "azzzzzzzzzzzzdfdf" I get something like: "2281aeebc1166cdfb2f17a0a0775d927ca5a9ad999bae0e4954f58bd8082fdf7efe1fd284876530341f714456d7eb8cd44c57b20ab27029b84d5dc77a674bede3fe9065282931404286082e9df8607bdcff0818b90324dfee7d76b566d0f99bebc5cc913372c276ba373712128f1bcc226b59367cff93f7cdd6dbde25b366863".
I must assume this value as right, since the code was taken from an existing API I am migrating. However, trying the same with PyCrypto (yes, I am migrating the API to be available in Python), I use the following code:
def bin2hex(s):
return "".join([hex(ord(c))[2:].zfill(2) for c in s])
KEY = """-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC81t5iu5C0JxYq5/XNPiD5ol3Z
w8rw3LtFIUm7y3m8o8wv5qVnzGh6XwQ8LWypdkbBDKWZZrAUd3lybZOP7/82Nb1/
noYj8ixVRdbnYtbsSAbu9PxjB7a/7LCGKsugLkou74PJDadQweM88kzQOx/kzAyV
bS9gCCVUguHcq2vRRQIDAQAB
-----END PUBLIC KEY-----"""
from Crypto.Cipher import PKCS1_v1_5
from Crypto.PublicKey import RSA
encrypter = PKCS1_v1_5.new(RSA.importKey(KEY))
print bin2hex(encrypter.encrypt("azzzzzzzzzzzzdfdf"));
While I expect the same value be returned and printed, the value finally is "3dd94ffabd01bb0e94010c0fedbcd4eb648f12e5d9e6d934b77ae86f76681d8a1b790cad9fddf6e6720415b4d645e525c33c402fa9778739b8e461790387e9508f7158a5fdc5723f5fc26d166b11a00759f0e0ee3ba6719a2e7c6b918f66e1311d1fff878ee2ca8762e1d6120f1e9585a76cdc7719ca20129ae76182b4277170".
Using PKCS1_OAEP outputs "290f60f37088c2cb46ae9221b01ff46a463f270ef7cf70bbea49de0b5ae43aec34a0eb46e694cf22f689eb77e808c590fdc30eda09f9d3f3cb8c15e0505bf5a984c2a121bc9fa83c6b5ccf50235f072467b4ae9cdf0f0ee2e486626ffa62ad1aa715fbe29e8afe4ceab3ca5a5df4c1dc75d7f258285d7ff1f4f2b4dcb7a8413a".
It is easy to tell that I must fix my python code. How can I fix my python code so it returns the exact same result as the given PHP call?
Your code is fine. PKCS#1 v1.5 padding in pyCrypto is randomized (source). So the encryption will always be different even if you use the same key and plaintext. This is a desirable property.
If you want to check compatibility between pyCrypto and PHP's OpenSSL extension, then you would need to encrypt in one, decrypt in the other and check that you got what you expected.
PKCS#1 v1.5 padding should not be used nowadays, because there are efficient attacks against it. OAEP is a much better alternative.
I've struggled with the same problem for quite long but finally managed to solve this.
The main issue was setting proper SHA hashes, in case of PHP's openssl_public_encrypt() it should be SHA1.
Here's the code for openssl_public_encrypt() with OPENSSL_PKCS1_OAEP_PADDING:
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
def encrypt_key(message):
with open('public_key', 'rb') as key_file:
pub_key = key_file.read()
public_key = load_pem_public_key(pub_key, default_backend())
ciphertext = public_key.encrypt(
message,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA1()),
algorithm=hashes.SHA1(),
label=None
)
)
return ciphertext
basically a copy of cryptography.io documentation regarding RSA.
Is there an equivalent functions in PHP that will allow interoperability with the .Net Rijndael AES encryption/decryption? (The encryption .Net code is below).
Basically, if I encrypt in .Net can I decrypt in PHP and vice-versa?
string outStr = null; // Encrypted string to return
RijndaelManaged aesAlg = null; // RijndaelManaged object used to encrypt the data.
// Generate the key from the shared secret and the salt.
Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedSecret, _salt);
// Create a RijndaelManaged object
aesAlg = new RijndaelManaged();
aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
// Create a decryptor to perform the stream transform.
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
// prepend the IV
msEncrypt.Write(BitConverter.GetBytes(aesAlg.IV.Length), 0, sizeof(int));
msEncrypt.Write(aesAlg.IV, 0, aesAlg.IV.Length);
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(plainText);
}
}
outStr = Convert.ToBase64String(msEncrypt.ToArray());
}
You can, maybe, but I cannot in good conscience recommend it.
PHP offers two extensions that can, in principle, do the job. OpenSSL suffers from not being documented beyond function prototypes, and Mcrypt suffers from being an absolute minefield if you don't happen to know exactly what you're doing. I wouldn't use either if I could possibly get away with it.
If you do attempt this, you will need to implement authentication yourself. You will need to implement padding yourself. If you screw up, you will get no indication even if the library knows perfectly well it's been asked to do something absurd, for the most part it will (silently!) guess at what you meant and continue on (patches for much of this are available, but not yet in mainline).
Godspeed.
As long as PHP and .Net follow specs encryption/decryption should work. You may check this topic for more info and examples Using PHP mcrypt with Rijndael/AES
As #Andrew says, once you get everything in the same spec, it should work. AES is quite a well used algorithm so librarys in both languages should match up. Any problems are usually to do with the password to key derivation functions in the different languages. This project tries to solve the intercommunication issues between .NET and PHP. It implements Rfc2898DeriveBytes() in PHP using a pbkdf2 with 1000 iterations of HMACSHA1 which I guess is what .NET uses by default.
It is a rather simple AES string encryption so there is no authentication. The padding issue is solved by using base64 encoding and rtrimming null characters on decryption so it is NOT a binary safe implementation. I take no credit or responsibility for this code, nor have I tested it between the different environments, but I feel it could help someone fill in the gaps.
I need to encrypt in android a certain text using a secret key. In PHP the encryption code looks like this
$this->securekey = hash('sha256',$textkey,TRUE);
$this->iv = mcrypt_create_iv(32, MCRYPT_DEV_URANDOM);
return base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $this->securekey, $input, MCRYPT_MODE_ECB, $this->iv));
For Base64 I added the commons codec from apache.org (commons-codec-1.6.jar) in Netbeans for my Android application. There is no error in the code. But when I run the application and call the function that use the codec the application stop and need a fore close.
In the logCat says:
Android Runtime: java.lang.NoSuchMethodError:
org.apache.commons.codec.binary.Base64.decodeBase64
Here is my code :
public static String crypt(String input, String key){
byte[] crypted = null;
try{
SecretKeySpec skey = new SecretKeySpec(org.apache.commons.codec.binary.Base64.decodeBase64(key), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skey);
crypted = cipher.doFinal(input.getBytes());
}catch(Exception e){
}
return org.apache.commons.codec.binary.Base64.encodeBase64String(crypted);
}
I am not sure if my code do the same encryption as the PHP code. I found this link http://www.androidsnippets.com/encrypt-decrypt-between-android-and-php between Android and PHP but it doesn't use Base64, just for mcrypt_encrypt. Can anyone help me to obtain the same encryption as the PHP server.
Thanks in advance.
Your error is simply because you forgot to add the Apache codec library to your runtime environment. Just compiling against it is not enough; the library needs to be actually present on the Android device.
You cannot get the same encryption on Android using the default Java libraries, you probably require the Bouncy Castle library. The PHP code in your example uses Rijndael with a block size of 32 bytes. AES is a subset of Rijndael with block sizes of 16 bytes. This is known as MCRYPT_RIJNDAEL_128 in PHP mcrypt.
Some other implementation details:
ECB does not use an IV (by now I've replaced the default mcrypt_encrypt sample by something better);
mcrypt_encrypt does not perform PKCS5Padding, I think it uses spaces;
input.getBytes() is not portable, it uses the platform default encoding, which may be different from the PHP encoding;
Finally some security warnings:
just using SHA-256 on passwords is considered insecure, use PBKDF2;
ECB is considered insecure, use CBC;
MCRYPT_DEV_URANDOM is not secure (which basically means PHP encryption is worthless, you are better off using a PHP openssl wrapper);
Good luck!