I have a project I'm working on that uses an API for it request, but in order to preform them I need to generate the token first.
Before the API was update everything was working, after the update I don't know how to adjust my code to make it work again.
This was the code that worked before the update (Android | Kotlin):
fun hmacHash(str: String, secret: String): String {
val sha256HMAC = Mac.getInstance("HmacSHA256")
val secretKey = SecretKeySpec(secret.toByteArray(), "HmacSHA256")
sha256HMAC.init(secretKey)
return convertToHex(sha256HMAC.doFinal(str.toByteArray()))
}
fun convertToHex(data: ByteArray): String {
val buf = StringBuilder()
for (b in data) {
var halfbyte = (b.toInt() shr 4) and (0x0F.toByte()).toInt()
var two_halfs = 0
do {
buf.append(if (halfbyte in 0..9) ('0'.toInt() + halfbyte).toChar() else ('a'.toInt() + (halfbyte - 10)).toChar())
halfbyte = (b and 0x0F).toInt()
} while (two_halfs++ < 1)
}
return buf.toString()
}
Which was equivalent to this PHP code:
hash_hmac('sha256', $string, $privateKey);
But now after the update the php code looks like this:
hash_hmac('sha256', $string, hex2bin($privateKey));
And I don't know how to adjust my code to make it work with this new change.
From what I can deduce, the PHP code made that change because $privateKey went from being plain text to being hex-encoded. So hex2bin was needed to change it back to plain text (hex2bin changes hex-encoded text to plain text; a confusingly named function if you ask me).
Since your secret is plain text, you don't need to change anything to match. But there are other ways to improve your code. For example, converting a byte array to a hex-encoded string is much easier than that.
fun hmacHash(str: String, secret: String): String {
val sha256HMAC = Mac.getInstance("HmacSHA256")
val bytes = secret.toByteArray()
val secretKey = SecretKeySpec(bytes, "HmacSHA256")
sha256HMAC.init(secretKey)
return convertToHex(sha256HMAC.doFinal(str.toByteArray()))
}
fun convertToHex(data: ByteArray): String =
data.joinToString("") { "%02x".format(it) }
Related
I've been trying to get this for hours now, and I can't find what's wrong. I'm using a php RESTful API that I made to encrypt data using asymmetric encryption.
First, I save my user's public key in the server by exporting it in android:
fun exportPublicKey() : String {
val publicKey = getPublicKey()
return android.util.Base64.encodeToString(
publicKey!!.encoded,
android.util.Base64.NO_WRAP
)
}
This allows me in the PHP server to do that:
$public_key_core = $_POST["public_key"];
$public_key = "-----BEGIN PUBLIC KEY-----\n" . $public_key_core . "\n-----END PUBLIC KEY-----";
I am unsure that's the right way but openssl seems to be "ok" with that key ?
I then tested my keystore in local using both keys, and it works just fine using this code:
Encrypt:
fun encryptAsymmetricData(data: String, usePrivateKey : Boolean = true): ByteArray {
val cipher : Cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding")
val encryptedBytes: ByteArray
if (usePrivateKey){
cipher.init(Cipher.ENCRYPT_MODE, getPrivateKey())
encryptedBytes = cipher.doFinal(data.toByteArray(Charsets.UTF_8))
} else {
cipher.init(Cipher.ENCRYPT_MODE, getPublicKey())
encryptedBytes= cipher.doFinal(data.toByteArray(Charsets.UTF_8))
}
return encryptedBytes
}
Decrypt:
fun decryptAsymmetricData(data: ByteArray): String{
val cipher : Cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding")
cipher.init(Cipher.DECRYPT_MODE, getPrivateKey())
return cipher.doFinal(data).toString(Charsets.UTF_8)
}
Using this works because I do ".toByteArray(Charsets.UTF_8)" on the encryptData result.
Now here's the problem, I use base64 encoding and do the following to encrypt in PHP:
openssl_public_encrypt($token->token, $encrypted_token, $user->public_key);
openssl_public_encrypt($user->id, $encrypted_id, $user->public_key);
[...]
'encrypted_user_id' => base64_encode($encrypted_id),
'encrypted_token' => base64_encode($encrypted_token)
But when I try to decrypt this in Android I'm getting an exception "javax.crypto.IllegalBlockSizeException" caused by this code:
val tokenBA = String(getDecoder().decode(this.encryptedToken), Charsets.UTF_8).toByteArray(Charsets.UTF_8)
val userIDBA = String(getDecoder().decode(this.encryptedUserId), Charsets.UTF_8).toByteArray(Charsets.UTF_8)
val token = App.encryptionController.decryptAsymmetricData(tokenBA)
val userID = App.encryptionController.decryptAsymmetricData(userIDBA)
(The logic being, I use base64 to send back my data in PHP, so I convert it to UTF8 in Android, then get the associated ByteArray to decrypt it ?)
I know that the encryption works in "local" but it doesn't when using both PHP and KeyStore, so I guess the problem is coming either from the PHP encryption, or from the way I try to decrypt it in android, but I can't seem to find what wrong, could you guys help me there please ?
Thank you by advance!
Ok, after searching and making sure the issue wasn't the public key stored in the PHP server, I found the answer. It was caused by the way to convert the "base64" string in an actual ByteArray in the App. This worked:
val token = App.encryptionController.decryptAsymmetricData(getDecoder().decode(encryptedToken))
val userID = App.encryptionController.decryptAsymmetricData(getDecoder().decode(encryptedUserId))
This is only working because I do the "base64_encode" in the server, for some (bad) reason I thought it was needed to go back to UTF8 to get the ByteArray in the app.
I want to crypt privateKey & publicKey to sha1 in base64 with Swift, but the output is not the one I see in PHP urlencode base64_encode which I tried in Codecademy:"https://www.codecademy.com/courses/web-beginner-en-StaFQ/0/3?curriculum_id=5124ef4c78d510dd89003eb8".
Pls see the following codes in Swift and Codecademy:
Swift:
//pls see func dataFromHexadecimalString() details here "http://stackoverflow.com/questions/26501276/convert-string-to-hex-string-in-swift/26502285#26502285"
extension String {
func dataFromHexadecimalString() -> NSData? {
let trimmedString = self.stringByTrimmingCharactersInSet(NSCharacterSet(charactersInString: "<> ")).stringByReplacingOccurrencesOfString(" ", withString: "")
var error: NSError?
let regex = NSRegularExpression(pattern: "^[0-9a-f]*$", options: .CaseInsensitive, error: &error)
let found = regex?.firstMatchInString(trimmedString, options: nil, range: NSMakeRange(0, count(trimmedString)))
if found == nil || found?.range.location == NSNotFound || count(trimmedString) % 2 != 0 {
return nil
}
let data = NSMutableData(capacity: count(trimmedString) / 2)
for var index = trimmedString.startIndex; index < trimmedString.endIndex; index = index.successor().successor() {
let byteString = trimmedString.substringWithRange(Range<String.Index>(start: index, end: index.successor().successor()))
let num = UInt8(byteString.withCString { strtoul($0, nil, 16) })
data?.appendBytes([num] as [UInt8], length: 1)
}
return data
}
}
func URLEcodekey() -> String {
let appid="a1b2c34d5e"
let privateKey="ef7d6s0d"
let areaid="101020900"
let time="201507191254"
let publicKey="http://open.weather.com.cn/data/?areaid=\(areaid)&type=forecast_v&date=\(time)&appid=\(appid)"
let cPrivateKey=privateKey.dataUsingEncoding(NSUTF8StringEncoding)!
let cPublicKey=publicKey.dataUsingEncoding(NSUTF8StringEncoding)!
var cHMAC = [CUnsignedChar](count: Int(CC_SHA1_DIGEST_LENGTH), repeatedValue: 0)
CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA1), cPublicKey.bytes, Int(cPublicKey.length), cPrivateKey.bytes, Int(cPrivateKey.length), &cHMAC)
let hexKeyString=NSMutableString(capacity: Int(CC_SHA1_DIGEST_LENGTH))
for byte in cHMAC{
hexKeyString.appendFormat("%02hhx", byte)
}
println("hexKeyString:\(encryptedKey)")
let binaryData = hexKeyString.dataFromHexadecimalString()
let base64String = binaryData?.base64EncodedStringWithOptions(nil)
println("base64String:\(base64String)")
var urlEncodeKey=base64String!.stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet())!
println("urlEncodeKey:\(urlEncodeKey)")
return urlEncodeMessage
}
the outputs are:
hexKeyString:d4433d42b1505c00a4aa80205171d0d04754d254
base64String:1EM9QrFQXACkqoAgUXHQ0EdU0lQ=
urlEncodeKey:1EM9QrFQXACkqoAgUXHQ0EdU0lQ=
PHP in Codecademy:
echo urlencode(base64_encode(hash_hmac('sha1', " http://open.weather.com.cn/data/?areaid=101020900&type=forecast_v&date=201507191254&appid=a1b2c34d5e", "ef7d6s0d", TRUE)));
the output is:
A5O59Y%2BFbGjhVwaI9JNB7DkcX%2F4%3D // the output is much like the
example in API, which I think maybe the right one.
So, how can I receive the right urlEncodeKey for my privateKey & publicKey like in PHP?
Thank you very much in advance!
You should read more about cryptography and hashing. In your case, there's no public key, private key, ... SHA stands for Secure hash algorithm and what you're trying to get is Hash based authentication code. Check Wikipedia articles about HMAC, SHA-1, Public key, ... I strongly recommend to read more about it otherwise you can create more damage if you misunderstand it.
Back to your problem. It's in one character:
Swift code - let publicKey="http://open.weather.com.cn...
PHP code - hash_hmac('sha1', " http://open.weather.com.cn...
Do you see where the problem is? In your PHP code, there's one space character just before http. This character is not in your Swift code.
Honestly, I didn't check your whole code, because I don't know why you're trying to convert it from hexadecimal string, etc. Used some parts only and rewrote it from scratch for you. Here's working example:
func URLEncodedKey() -> String? {
let appid = "a1b2c34d5e"
let time = "201507191254"
let areaid = "101020900"
let key = "ef7d6s0d"
let string = " http://open.weather.com.cn/data/?areaid=\(areaid)&type=forecast_v&date=\(time)&appid=\(appid)"
// ^ <- in your PHP example, there's space
guard let keyData = key.dataUsingEncoding(NSUTF8StringEncoding),
stringData = string.dataUsingEncoding(NSUTF8StringEncoding),
outputData = NSMutableData(length: Int(CC_SHA1_DIGEST_LENGTH)) else {
return nil
}
outputData.length = Int(CC_SHA1_DIGEST_LENGTH)
CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA1),
keyData.bytes, keyData.length,
stringData.bytes, stringData.length,
outputData.mutableBytes)
return outputData
.base64EncodedStringWithOptions([])
.stringByAddingPercentEncodingWithAllowedCharacters(.URLQueryAllowedCharacterSet())
}
Return value is:
`Optional("A5O59Y+FbGjhVwaI9JNB7DkcX/4=")`
Which is what you get when you decode your PHP output.
Just replace URLQueryAllowedCharacterSet with any of the following character sets:
class func URLUserAllowedCharacterSet() -> NSCharacterSet
class func URLPasswordAllowedCharacterSet() -> NSCharacterSet
class func URLHostAllowedCharacterSet() -> NSCharacterSet
class func URLPathAllowedCharacterSet() -> NSCharacterSet
class func URLQueryAllowedCharacterSet() -> NSCharacterSet
class func URLFragmentAllowedCharacterSet() -> NSCharacterSet
Depends on your use case. IOW in which part of the URL your would like to use it.
I am converting .net project to PHP . But unable to convert the following code:
public static string HashString(string value)
{
SHA1 hasher = new SHA1CryptoServiceProvider();
UTF8Encoding enc = new UTF8Encoding();
byte[] hashInBytes = hasher.ComputeHash(enc.GetBytes(value));
return Convert.ToBase64String(hashInBytes);
}
So far I have done this but result is not same:
function HashString($str) {
return base64_encode(sha1($str));
}
Please help, thanks.
The reason behind the difference is, PHP uses ASCII encoding for hash calculations.
In C#, you can replace UTF8Encoding with ASCIIEncoding in order to have same results.
Finally I found the solution:
This is the final code which is equivalent to .net code:
function HashString($str) {
return base64_encode(sha1($str,true));
}
I have added "true" with sha1 function.
working with project where I need to have SHA512 hash of string in C++ program and in PHP.
I'm using RAD Studio XE2 C++ builder with DCPCrypt library (ver. DCPcrypt v2 Beta 3 - RECOMMENDED).
Here is my C++ code, so very simple test program with Edit1(text input) and Edit2(hash output) and one button:
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Edit2->Text = SHA512( Edit1->Text );
}
//---------------------------------------------------------------------------
String __fastcall TForm1::SHA512(String input){
if (input.Length() ) {
TDCP_sha512 *sha512 = new TDCP_sha512(this);
unsigned char array[192];
String output = "";
sha512->Init();
sha512->UpdateStr( Edit1->Text );
sha512->Final((void*)array);
for (int i = 0; i < 64; ++i) {
output += IntToHex(array[i], 2);
}
sha512->Free();
return output.LowerCase();
}
}
When I type "TEST" into Edit1 field and press button, I will have hash result:
0881048f3717a7cc363ca50e2dd7229efceb30786f0dc33b1b2cbcc3929055d00ed594f96127156aab01f30b6f6d28b100cbd7975e168bc39e50251d96c6beef
When I try same at PHP code:
<?PHP
echo openssl_digest('TEST', 'sha512');
?>
This will give me totally different result:
7bfa95a688924c47c7d22381f20cc926f524beacb13f84e203d4bd8cb6ba2fce81c57a5f059bf3d509926487bde925b3bcee0635e4f7baeba054e5dba696b2bf
I can produce match if I compare string "1" in both ends, but after adding more letters, C++ code does not show same results.
When using some "online" sha512 forms I found from web I'm getting same results than in PHP code.
Any ideas?
I have a problem (server side) when i encrypt data from a client and i send to webserver with Post Method.
i Use this Method to Encrypt from a C# Client
public string Encrypt3DES(string strString)
{
DESCryptoServiceProvider DES = new DESCryptoServiceProvider();
DES.Key = Encoding.GetBytes(this.Key);
DES.Mode = CipherMode.ECB;
DES.Padding = PaddingMode.Zeros;
ICryptoTransform DESEncrypt = DES.CreateEncryptor();
byte[] Buffer = encoding.GetBytes(strString);
return Convert.ToBase64String(DESEncrypt.TransformFinalBlock(Buffer, 0, Buffer.Length));
}
When i send ecrypted String to PHP if there was a + in that string, php read it with a blank space. If instead there'isnt any '+' i haven't any problem.
For Example this is a Encrypted String 4aY+na42iaPg+aep== in C# when i read in php it's
4aY a42iaPg aep== so if i decrypt if dont match with the correct word.
i use this script to start read method post
if (isset($_POST['doConvalid'])){
if ($_POST['doConvalid']=='Convalid')
{
foreach($_POST as $keys => $values) {
$data[$keys] =($values); // post variables are filtered
}
$cheking=$data['check'];
echo("Show checking = $checking"); //Here i read string with blank space instead +
Is there a way to fix it?
Yes base64 decodes + to space use this:
echo str_replace(" ","+",$_POST['string']);
http://en.wikipedia.org/wiki/Base64#URL_applications
You could replace the '+' with a different character (something not used by base64) for sending. Then replace that character back to '+' for decoding.