I decompiled an iOS app and I saw the privatekey and method used to encrypt a string. I used charles to catch this data and have a string like this:
Charles package data
/random1/name/BQPnTF9MX8A3FbV1V5jtFozQnSkNtBK5AFJyTnzBJZgFkXIZyWlvxd3LzH6eIQznMLW7U8V3M5FDU9j9zGrkajIc5VjqIS1q8Sy+L9tLPE51aIy0xlKVlRgqjWGe0HGUBBAtlTk+rOZEeR/+TODnEN79mYtgWTNpscRr9dy6DoWw7wvE7MiLIibdCjQ4PbcFQ/EpvIjgWOzCorbobYbEUoI/aw== HTTP/1.1
Then I looked at iOS code and saw their method deassembler
So I wrote a php server to decode this encrypted string:
require __DIR__ . '/../autoload.php';
$password = "e12d33re";
$base64Encrypted = "BQN7evDaWMlRXiOOeCEIkL6+3K2dLRKv/e9tYTxrSVMTojf6gMPL7hW7gfuYHt622CIlfon5vsGpv9ykM6WbbMPdH7Q56lcbRPA2KO9aquYR5fM8e0fGGb7AQzPs3G0CJAAYG0E9i8cG1VH3uVP6VWjK5LkpRuUOk8QuoG1j3eP0fUZVY8RSjKyFZpbLlDIrANg4T5DmkigVTEN82QYCbLv2Iw==";
$cryptor = new \RNCryptor\Decryptor();
$plaintext = $cryptor->decrypt($base64Encrypted, $password);
echo "Base64 Encrypted:\n$base64Encrypted\n\n";
echo "Plaintext:\n$plaintext\n\n";
But I cannot decrypt it.
I checked the version of it by the code and see that it is version 5
$base64Encrypted = "BQN7evDaWMlRXiOOeCEIkL6+3K2dLRKv/e9tYTxrSVMTojf6gMPL7hW7gfuYHt622CIlfon5vsGpv9ykM6WbbMPdH7Q56lcbRPA2KO9aquYR5fM8e0fGGb7AQzPs3G0CJAAYG0E9i8cG1VH3uVP6VWjK5LkpRuUOk8QuoG1j3eP0fUZVY8RSjKyFZpbLlDIrANg4T5DmkigVTEN82QYCbLv2Iw==";
$actualVersion = ord(substr(base64_decode($base64Encrypted), 0, 1));
I see rncryptor lib in ios binary file.
The first picture, i decrypt with private key sucessfully( They encrypted v4 string with RNcryptor)
http: //i.stack.imgur.com/Kq5m1.png
The second picture, they used unknown method to encrypt, but surely 100% rncryptor( They don't encrypt v4 string)
http: //i.stack.imgur.com/NfScg.png
I see rncryptor lib in ios binary file.
The first picture, i decrypt with private key sucessfully( They encrypted v4 string with RNcryptor)
version 1
The second picture, they used unknown method to encrypt, but surely 100% rncryptor( They don't encrypt v4 string)
version 2
Related
I would like to achieve something I thought was pretty straighforward:
A) My VB.NET client (ideally targetting Framework 4.0) sends a text string to my Apache/PHP Server via an HTTPS POST request.
B) My Server responds with a Signature of that text string.
Private key used by the Server is always the same, and public key used by Client is already embeded within the source code.
After investigating and reading through a lot of documentation, I came up with the following strategy and have two questions:
Is my strategy efficient?
The code provided below does not work (.VerifyData returns FALSE). What am I missing?
Strategy
Server Side
Apache/PHP: Because that is the only server language I am familiar
with, but I could switch if recommended.
OpenSSL: Because I use PHP
PEM files: Because I use OpenSSL
RSA key size is 2048 bits: Recommended minimum in 2019
Algorythm is SHA256: Because everyone seems to use that one
Header text/plain, UTF8: Why not?
Client Side
VB.Net Framework 4.0 Client Profile: Because I want to maximise legacy (VSTO 2013)
System.Security.Cryptography.RSACryptoServiceProvider
PEM public key info loaded via XML string
HTTPS should be TLS1.0 or higher: Because I target .Net Framework 4.0 (TLS1.1 is recommended if possible)
Source Code
Server Side (generate .pem key files, only once)
<?
// Create new Keys Pair
$new_key_pair = openssl_pkey_new(array(
"private_key_bits" => 2048,
"private_key_type" => OPENSSL_KEYTYPE_RSA,
));
//Save Private Key
openssl_pkey_export($new_key_pair, $private_key_pem, "my passphrase to protect my private key; add random characters like $, ?, #, & or ! for improved security");
file_put_contents('private_key.pem', $private_key_pem);
//Save Public Key
$details = openssl_pkey_get_details($new_key_pair);
$public_key_pem = $details['key'];
file_put_contents('public_key.pem', $public_key_pem);
?>
Server Side (target of POST requests)
<?
header('Content-Type: text/plain; charset=utf-8');
// Verify connection is secure
if(empty($_SERVER['HTTPS']) || $_SERVER['HTTPS']=="off")
{
echo "Unauthorized Access";
exit;
}
// Data to Sign
$data = base64_decode(file_get_contents('php://input'));
//Load Private Key
$private_key_pem = openssl_pkey_get_private('file:///path/protected/by/dotHtaccess/private_key.pem', "my passphrase to protect my private key; add random characters like $, ?, #, & or ! for improved security");
//Create Signature
openssl_sign($data, $signature, $private_key_pem, OPENSSL_ALGO_SHA256);
echo base64_encode($signature);
?>
Client Side
imports System.Diagnostics
Sub mySignatureTest()
Dim oURI As Uri = New Uri("https://www.example.com/targetpage.php")
Dim sData As String = "myStringToSign"
Dim sResponse As String
'# Get POST request Response
Using oWeb As New System.Net.WebClient()
Try
System.Net.ServicePointManager.SecurityProtocol = System.Net.ServicePointManager.SecurityProtocol And Not System.Net.ServicePointManager.SecurityProtocol.Ssl3 'Override defautl Security Protocols: Prohibit SSL3
oWeb.Encoding = Encoding.UTF8
Debug.Print("SSL version is " & System.Net.ServicePointManager.SecurityProtocol.ToString)
Debug.Print("Sending " & sData)
Debug.Print("To " & oURI.ToString)
Debug.Print("Encoding is " & oWeb.Encoding.ToString)
sResponse = oWeb.UploadString(oURI, "POST", Convert.ToBase64String(Encoding.UTF8.GetBytes(sData)))
Debug.Print("Server reponse = " & sResponse)
Catch ex As Exception
MsgBox("Connection with server failed: " & ex.ToString, vbCritical + vbOKOnly, "Add-In")
End Try
End Using
'#Verify RSA SHA256 Signature
Dim sDataToSign As String = sData
Dim sSignatureToVerify As String = sResponse
Using myRSA As New System.Security.Cryptography.RSACryptoServiceProvider
'XML format obtain from PEM file by hand copy/paste here: https://superdry.apphb.com/tools/online-rsa-key-converter
myRSA.FromXmlString("<RSAKeyValue><Modulus>SomeLongBase64StringHere</Modulus><Exponent>SomeShortBase64StringHere</Exponent></RSAKeyValue>")
Debug.Print("Signature verification = " & myRSA.VerifyData(Encoding.UTF8.GetBytes(sDataToSign), _
System.Security.Cryptography.CryptoConfig.MapNameToOID("SHA256"), _
Convert.FromBase64String(sSignatureToVerify)).ToString)
End Using
End Sub
Once question 2) has been resolved, I hope we can let this code evolve so that it provides a good but yet simple example of how to implement .NET Client verification of OpenSSL Server signature. I was unable to locate a simple and clear example on the Internet.
This can typically be used for licensing purposes where a Server would provide a file containing an expiration date as well as a signature of that date, in order for the Client App to confirm this expiration date was not altered for example by the Owner of the Client computer.
Is there a coldfusion alternaitive to this php function: openssl_verify:
openssl_verify() verifies that the signature is correct for the
specified data using the public key associated with pub_key_id. This
must be the public key corresponding to the private key used for
signing.
I've looked all over but there doesn't seem to be any. Thanks in advance for any info?
There are no built in functions, AFAIK. However, java supports signature verification, which you could adapt with a bit of java code.
Convert the data you want to verify into binary. The exact steps depends on what you are verifying, but say it is a physical file:
dataBytes = fileReadBinary( "c:\path\someFile.zip" );
Decode the signature value into binary. Again, the "how" depends on the signature format. If it is a base64 encoded string:
signatureBytes = binaryDecode( base64SignatureString, "base64" );
Load the certificate from your keystore (or from a file) and extract the public key:
// Example: "C:\ColdFusion\jre\lib\security\cacerts"
fis = createObject("java", "java.io.FileInputStream").init( pathToKeyStore );
keyStore = createObject("java", "java.security.KeyStore").getInstance("JKS");
// Default keystore password is "changeit" (do not keep the default. change it)
keyStore.load(fis, keyStorePassword.toCharArray());
publicKey = keyStore.getCertificate( "yourCertAlias" ).getPublicKey();
Create a Signature object to perform the verification. Initialize it with the appropriate algorithm (ie SHA1withRSA, etcetera), public key and the data to verify:
sign = createObject("java", "java.security.Signature").getInstance("SHA1withRSA");
sign.initVerify( publicKey );
sign.update( dataBytes );
Finally, feed in the signature and verify its status:
isVerified = sign.verify(signatureBytes);
writeDump( isVerified );
For more details, see Lesson: Generating and Verifying Signatures and Weaknesses and Alternatives.
You could attempt using cfexecute along with the OpenSSL CLI. https://www.openssl.org/docs/manmaster/apps/verify.html
For a project I'm working on, I'm using the Amazon AWS SDK for PHP, and I needed to retrieve a password for a server environment in plain text format. However, the documentation for the ec2 method confirmed what we found: the method would only return an encrypted string. On the surface, this was good, because the AWS SDK for PHP uses an unencrypted HTTP POST request to send and receive data via cURL, invisibly to the user. So we don't our password data just flying around the web.
The problem was that there was nothing explaining how to decrypt the string. I had my private key as a PEM file, but there was no method or documentation for what to do with that string to make it usable. Several attempts yielded nothing, and I was beginning to think that I needed to rethink my strategy for the project I'm on, but then I found the code from the last version of the AWS SDK for PHP, and it revealed how to go about decrypting the string to produce a plain text form of the password.
The answer I found was that the getPasswordData method returns a string that is BOTH base64 encoded AND encrypted. You need to decode it with base64_decode() before you can successfully decrypt it with PHP's OpenSSL library. The following function takes care of both:
/**
* #param obj $ec2_client The EC2 PHP client, from the AWS SDK for PHP
* #param string $client_id The ID of the client whose password we're trying to get.
* #return mixed The unencrypted password for the client, or false on failure.
*/
function aws_get_ec2_password($ec2_client, $client_id){
// First, run getPasswordData to get the Password Data Object.
$pw_obj = $ec2_client->getPasswordData($client_id);
// Next, use the local get() method to isolate the password
$pw_b64 = $pw_obj->get("PasswordData");
// Decode the password string.
$pw_encrypted = base64_decode($pw_b64);
// Now, get your PEM key.
//
// You can also use a raw string of the PEM key instead of get_file_contents(),
// or adjust the function so that you can pass it as an argument.
//
// Technically, this step might not be necessary, as the documentation for
// openssl_private_decrypt() suggests that $key can just be the path, and it will
// create the key object internally.
$key = openssl_get_privatekey(file_get_contents("path/to/key.pem"));
// Create an empty string to hold the password.
$pw = "";
// Finally, decrypt the string and return (will return false if decryption fails).
if(openssl_private_decrypt($pw_encrypted, $pw, $key)){
return $pw;
}else{
return false;
}
}
I hope this helps someone else avoid the headaches it gave me!
I use PHP to sign a string with openssl_sign. So far all ok.
The problem is that I want to verify the signature from Windows. For that I pass the certificate, the message, and it's signature to the windows app.
How do I use CryptVerifyDetachedMessageSignature to force using the certificate that the PHP code used?
I tried it, but it returns "asn1 bad tag value met" on the signature created by PHP ...
Thanks...
It's hard to say since you haven't posted your code or a sample signature / plaintext / key. But, in lieu of that, here's how I'd do it (with phpseclib):
<?php
include('Crypt/RSA.php');
$rsa = new Crypt_RSA();
//$rsa->setPassword('password');
$rsa->loadKey('...'); // private key
$plaintext = '...';
$rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
$signature = $rsa->sign($plaintext);
$rsa->loadKey('...'); // public key
echo $rsa->verify($plaintext, $signature) ? 'verified' : 'unverified';
?>
If the signature mode is PSS just do $rsa->setSignatureMode() (without any parameters) instead.
If the signature and plaintext are both in the same blob you'll need to separate it per whatever file format you're using.
No luck.
I finally resorted to openssl_pkcs7_sign which outputs a S/MIME compatible message, which I can handle in Windows.
I'm having trouble to decrypt a cipher text in client side/javascript which is encrypted in server side/PHP.
For encryption in PHP, I'm using the phpseclib and here is my sample code block:
define('PUK', 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDwMKuLMVuo7vHDwFCbpgxx+RNf xSVC2GRWq9rW0fl4snnhRdrpprPRUO1RMV0wA/ao+fvzi1Sl0lcws3srpe28iLAj Wh5vFM/pFstwzjoHBv/6n4rQouIVy2NT18aFrxlQUCs4DHy5eOo21MjQXrgHwCeP HEc7OK+VpaQ/yKKX8wIDAQAB');
include ('Crypt/RSA.php');
$rsa = new Crypt_RSA();
$plaintext = 'My Test Msg';
$rsa -> loadKey(PUK);
$ciphertext = $rsa -> encrypt($plaintext);
//echo $ciphertext;//This also not working!
//echo strrev(base64_encode($ciphertext)); //this is also not working! ref: http://www.frostjedi.com/phpbb3/viewtopic.php?f=46&t=141187
echo base64_encode($ciphertext);
For decryption in client side/Javascript, I'm using "jsencrypt" and here is the code block:
var decrypt = new JSEncrypt();
decrypt.setPrivateKey('MIICXQIBAAKBgQDwMKuLMVuo7vHDwFCbpgxx+RNfxSVC2GRWq9rW0fl4snnhRdrp prPRUO1RMV0wA/ao+fvzi1Sl0lcws3srpe28iLAjWh5vFM/pFstwzjoHBv/6n4rQ ouIVy2NT18aFrxlQUCs4DHy5eOo21MjQXrgHwCePHEc7OK+VpaQ/yKKX8wIDAQAB AoGBAL2EuaZvwLIwL6VUVoYp5AH+FVJo3Ti8Q5e7rEX6kgyxTsf4dX4NIi9T2p1J BQ2A4xx7e1i0pIreyBtOUy6ik0y7e3MlmZidG91pz2KllQqwAMKrOZgPTBWBF7fr xIZERfOlZcIRrqp8ECbeHDyO6fUbfQm+o7vkxMypwjixBslJAkEA+mF8Sxvw+7D6 ntev+XsYj9Xp4wumqR2hK4WcXAAWbFmcd29tgTMKfcgw0Ru6FCGQdUvqu61PniS4 ie+u6zPORwJBAPWUos5KvEixkgSUY0PZOQavRwoXS1GEEvkjlFOyqWqUiKViT9iy UsXKxk3NAVMqIdF5RdAQ/ob9NxtxiuSxYvUCQQDUfFsBWwsebsmieCVNslvb5YyC NOcRaqXWy6MwqJpfBYW2Doh+NxTWPki/japTX1C7WtwwvhpteXhrB1AJJ4QNAkB1 RrsM6vHJgUsq9rYE07qA77lsHz2vuvPYmF4gLkTrie1LlYxt/pK6tCBJTSphzdAC mfh16XezfT8Q0wMyPWf1AkAxS//2T3J1g+dbG9dEKREcpwANxlFIEnOm9XsFd2vO I6Jr0ksaS4o0IeUBDWmMFOgCWVPdJkGrlqlVPQ6P9owA');
var uncrypted = decrypt.decrypt(ciphertext); //The ciphertext is obtained from the server by an AJAX call.
BUT the "uncrypted" is found always to be null!
Please note that if I use the mentioned Private/Public key pair either in phpseclib or jsencrypt then it is working fine. The problem is creating only on encryption in PHP and decryption in Javascript.
It will be really appreciable if anyone can help me on this regard.
What happens if you encrypt in Javascript and try to decrypt in PHP? Does that work?
If not then if you could post the ciphertext produced by Java that'd be helpful. That'd give us a chance to breakdown the encoding of the plaintext and see which padding method - if any - is being used.
That said, in lieu of having that, my guess, off hand, would be that you need to do $rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1).