I am using RNCryptor in Swift and C#.NET . I need a cross platform AES encryption and because of this, I am using RNCryptor.
When I encrypt some plain text in Swift,I can decrypt it in Swift correctly without any error. But when I encrypt some text in C# and then I want to decrypt it in Swift,I got an error " The operation couldn’t be completed. (RNCryptorError error 2.)"
My code in C# :
public static string EncryptQRCode(string qrCodeString){
var qrEncryptor = new Encryptor ();
return qrEncryptor.Encrypt (qrCodeString, "password");
}
public static string DecryptQRCode(string qrEncryptedString){
var qrDecryptor = new Decryptor();
return qrDecryptor.Decrypt (qrEncryptedString, "password");
}
My Code in Swift:
func Encrypt(msg:String, pwd:String) -> String{
let data = msg.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
let chiper = RNCryptor.encryptData(data!, password: pwd)
let base = chiper.base64EncodedDataWithOptions(NSDataBase64EncodingOptions(rawValue: 0))
let stringBase = String(data: base, encoding: NSUTF8StringEncoding)
return stringBase!
}
func Decrypt(msg:String, pwd:String) -> String{
let encodedData:NSData = NSData(base64EncodedString: msg, options: NSDataBase64DecodingOptions(rawValue: 0))!
do{
let decryptedData = try RNCryptor.decryptData(encodedData, password: pwd)
let decrypytedString = String(data: text, encoding: NSUTF8StringEncoding)
return decryptedString!
}
catch let error as NSError{
print(error.localizedDescription)
print(error.localizedDescription)
}
return "AN ERROR OCCURED"
}
For example:
"ABC", with password "behdad" in C#, Encryptor returned:
"AgHfT2VvVOorlux0Ms47K46fG5lQOP2YhYWq2KeIKh+MisCDqZfrLF+KsJyBR2EBNC3wQpaKev0X4+9uuC5vliVoHkLsEi6ZI7ZIZ8qVUEkYGQ=="
When I decrypt it in C#, it returned "ABC".
But when I pass this Base64Encoded string to my Swift Decryptor function,it returned:
RNCryptorError error 2.
For Example:
qrCodeString = "ABC".
public static string EncryptQRCode returns =
"AgF6P5Ya0SifSymd3LqKdH+kGMCFobiziUhwwB6/lfZgAA9N+F5h350MyigoKo9qgUpMXX3x9FxZXwUOJODL4is3R62EGvZWdJBzjSNCef7Ouw=="
The "msg" is returned data from EncryptQRCode(The Base64 Encoded String).
pwd = "password"
encoded data = <02017a3f 961ad128 9f4b299d dcba8a74 7fa418c0 85a1b8b3 894870c0 1ebf95f6 60000f4d f85e61df 9d0cca28 282a8f6a 814a4c5d 7df1f45c 595f050e 24e0cbe2 2b3747ad 841af656 7490738d 234279fe cebb>
decryptedString and decryptedData do not have values due to the error occurred.
RNCryptorError error 2
UnknownHeader = 2
Unrecognized data format. Usually this means the data is corrupt.
This means that the data passed is not in the correct format.
The best programming advice I ever got was one night in the computer room when I asked Rick Cullman for help and he said: "Read the documentation."
That is why I suggested displaying the inputs and outputs, you will catch that.
There are many places where hexadecimal is need to see what is happening and to debug.
Opened an issue in RNCryptor Swift to add the error codes to the documentation.
In C# when you want to encrypt the text,you have to use Schema.V3 for encryption. Decryptor in Swift cannot identify the Schema Version of Base 64 encoded string.
string encrypted = encryptor.Encrypt (YOUR_PLAIN_TEXT, YOUR_PASSWORD,Schema.V3);
Related
This C# code:
private static string CreateHashKey(object myString)
{
byte[] buffer = JsonSerializer.SerializeToUtf8Bytes(myString);
var cryptoTransform = MD5.Create();
return BitConverter.ToString(cryptoTransform.ComputeHash(buffer));
}
When given the string "Bangalore", produces this hashed value: "92-E7-92-78-E7-D9-37-C1-AF-AC-D7-E6-B2-CD-B6-5E".
However, I cannot reproduce this in R, playing around a bit:
library(digest)
digest::digest(serialize("Bangalore", connection = NULL),"md5")
"e85798ec7dd5003d8d464f6c5d8de5c5"
digest::digest(serialize("Bangalore", connection = NULL,ascii = TRUE),"md5")
"5377a11b9792c774ddd726361c56d8f2"
digest::digest(charToRaw('Bangalore'),"md5")
"bf19f50fed3db016cb78cdb029db3034"
digest::digest(serialize(charToRaw('Bangalore'),connection = NULL,ascii = TRUE),"md5")
"be7696c777f9418e8e853084d6ddf0ae"
library(openssl)
md5("Bangalore") # "1bc99cb2f4153c2d0d8025ee5575b2a0"
This post suggests converting to bytes would fix the problem, but it hasn't so far...
Why do I get an different hash when running with python and C# the same key and message?
I'm trying to generate a JWT token using Jose.JWT.encode(payload, secretKey, JwsAlgorithm.ES256, header) (see https://github.com/dvsekhvalnov/jose-jwt) to use with Apple's new token-based APNs system.
The JWT encode method requires the secretKey to be in CngKey format.
Here's my code converting the .p8 file from Apple to a CngKey object:
var privateKeyContent = System.IO.File.ReadAllText(authKeyPath);
var privateKey = privateKeyContent.Split('\n')[1];
//convert the private key to CngKey object and generate JWT
var secretKeyFile = Convert.FromBase64String(privateKey);
var secretKey = CngKey.Import(secretKeyFile, CngKeyBlobFormat.Pkcs8PrivateBlob);
However, on the last line, the following error is thrown.
System.Security.Cryptography.CryptographicException was unhandled by user code
HResult=-2146885630
Message=An error occurred during encode or decode operation.
Source=System.Core
StackTrace:
at System.Security.Cryptography.NCryptNative.ImportKey(SafeNCryptProviderHandle provider, Byte[] keyBlob, String format)
at System.Security.Cryptography.CngKey.Import(Byte[] keyBlob, String curveName, CngKeyBlobFormat format, CngProvider provider)
at System.Security.Cryptography.CngKey.Import(Byte[] keyBlob, CngKeyBlobFormat format)
at tokenauthapi.App_Start.TokenInitSendMessage.<send>d__0.MoveNext() in C:\token-push-prototype\token-auth-api\token-auth-api\App_Start\TokenInitSendMessage.cs:line 31
InnerException:
The input isn't in the wrong format as there's a separate error for that (that appears when I change the blob type).
This code is running in a .NET WebApi v4.6.
I've searched high and low but haven't been able to decipher what this error is referring to. Any help would be greatly appreciated. Thank you.
The security key (p8) provided by Apple for DeviceCheck also contained newlines. I used the following to get a valid CngKey:
var privateKeyContent = File.ReadAllText("pathToApplePrivateKey.p8");
var privateKeyList = privateKeyContent.Split('\n').ToList();
var privateKey = privateKeyList.Where((s, i) => i != 0 && i != privateKeyList.Count - 1)
.Aggregate((agg, s) => agg + s);
CngKey key = CngKey.Import(Convert.FromBase64String(privateKey), CngKeyBlobFormat.Pkcs8PrivateBlob);
Turns out the .p8 file I was using had newlines in the middle of it for some reason. Possible that notepad added it (and saved it?). I was splitting by newlines to get the private key and therefore it was truncating the key. Once I removed the newlines it worked fine.
If you get the error occurred during encode or decode operation error, check whether your .p8 (or other) private key is malformed and is the right length.
I met the same issue. I use this:
var privateKey = privateKeyContent.Split('\n')[1];
Then I analyze token file downloaded from Apple. I found there are more \n in the file. I am not sure where this format is different or apple changed.
Then I use the following codes to load the token, works.
Actually, we can directly use this token string.
var privateKeyContent = System.IO.File.ReadAllText(authKeyPath);
var privateKeyList = privateKeyContent.Split('\n');
int upperIndex = privateKeyList.Length;
StringBuilder sb = new StringBuilder();
for(int i= 1; i< upperIndex - 1; i++ )
{
sb.Append(privateKeyList[i]);
Debug.WriteLine(privateKeyList[i]);
}
I have a C# application that receives webhook notifications from PayPal and I want to verify the signature as described in PayPal docs:
https://developer.paypal.com/docs/integration/direct/rest-webhooks-overview/#event-types
The code snippet in the docs is for Java, not C#. The first thing I don't know is in which format the CRC32 should be appended (decimal, hex, ???). I have tried several variants and I have following code so far, always with VerifyData() returning false:
string transmissionSig = HttpContext.Request.Headers["PAYPAL-TRANSMISSION-SIG"];
string transmissionId = HttpContext.Request.Headers["PAYPAL-TRANSMISSION-ID"];
string transmissionTime = HttpContext.Request.Headers["PAYPAL-TRANSMISSION-TIME"];
string signatureAlgorithm = HttpContext.Request.Headers["PAYPAL-AUTH-ALGO"]; //signatureAlgorithm == "SHA256withRSA"
string certUrl = HttpContext.Request.Headers["PAYPAL-CERT-URL"];
uint crc = calculateCrc32(eventBody);
string expectedSignature = String.Format("{0}|{1}|{2}|{3}", transmissionId, transmissionTime, webhookId, crc);
string certData = new System.Net.WebClient().DownloadString(certUrl);
X509Certificate2 cert = new X509Certificate2(getBytes(certData));
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)cert.PublicKey.Key;
byte[] signature = Convert.FromBase64String(transmissionSig);
byte[] expectedBytes = getBytes(expectedSignature);
bool verified = rsa.VerifyData(expectedBytes, CryptoConfig.MapNameToOID("SHA1"), signature);
What am I doing wrong?
UPDATE
I use this class for CRC calculation: https://github.com/damieng/DamienGKit/blob/master/CSharp/DamienG.Library/Security/Cryptography/Crc32.cs
Example eventBody (from webhook simulator):
{"id":"WH-2WR32451HC0233532-67976317FL4543714","create_time":"2014-10-23T17:23:52Z","resource_type":"sale","event_type":"PAYMENT.SALE.COMPLETED","summary":"A successful sale payment was made for $ 0.48 USD","resource":{"id":"80021663DE681814L","create_time":"2014-10-23T17:22:56Z","update_time":"2014-10-23T17:23:04Z","amount":{"total":"0.48","currency":"USD"},"payment_mode":"ECHECK","state":"completed","protection_eligibility":"ELIGIBLE","protection_eligibility_type":"ITEM_NOT_RECEIVED_ELIGIBLE,UNAUTHORIZED_PAYMENT_ELIGIBLE","clearing_time":"2014-10-30T07:00:00Z","parent_payment":"PAY-1PA12106FU478450MKRETS4A","links":[{"href":"https://api.paypal.com/v1/payments/sale/80021663DE681814L","rel":"self","method":"GET"},{"href":"https://api.paypal.com/v1/payments/sale/80021663DE681814L/refund","rel":"refund","method":"POST"},{"href":"https://api.paypal.com/v1/payments/payment/PAY-1PA12106FU478450MKRETS4A","rel":"parent_payment","method":"GET"}]},"links":[{"href":"https://api.paypal.com/v1/notifications/webhooks-events/WH-2WR32451HC0233532-67976317FL4543714","rel":"self","method":"GET"},{"href":"https://api.paypal.com/v1/notifications/webhooks-events/WH-2WR32451HC0233532-67976317FL4543714/resend","rel":"resend","method":"POST"}]}
And it's CRC that I'm getting and appending to expectedSignature: 3561502039
you should get algorithm from the header in stead of hard-coding it. SHA256 is the currently supported algorithm I think.
I'm trying to encrypt some data in Mono C#, send it to a NodeJS server and decrypt it there. I'm trying to figure out what algorithms to use to match the two.
I send the encrypted string encoded with base64. So I do something like this in Javascript, where I know the key which was used to encrypt the data in my C# application:
var decipher = crypto.createDecipher('aes192',binkey, biniv);
var dec = decipher.update(crypted,'base64','utf8');
dec += decipher.final('utf8');
console.log("dec", dec);
In Mono I create my Cypher with:
using System.Security.Cryptography;
using (Aes aesAlg = Aes.Create("aes192"))
I need to pass the correct string to Aes.Create() in order to have it use the same algorithm, but I can't find what it should be. "aes192" is not correct it seems.
I don't need aes192 this was just a tryout. Suggest a different encryption flavor if it makes sense. Security is not much of an issue.
Here are links to .NET and Nodejs docs:
http://msdn.microsoft.com/en-us/library/system.security.cryptography.aes.aspx
http://nodejs.org/api/crypto.html
This code works for my Node.js side, but please replace the static iv, otherwhise aes encryption would be useless.
var crypto = require('crypto');
function encrypt(data, key) {
key = key || new Buffer(Core.config.crypto.cryptokey, 'binary'),
cipher = crypto.createCipheriv('aes-256-cbc', key.toString('binary'), str_repeat('\0', 16));
cipher.update(data.toString(), 'utf8', 'base64');
return cipher.final('base64');
}
function decipher(data, key) {
key = key || new Buffer(Core.config.crypto.cryptokey, 'binary'),
decipher = crypto.createDecipheriv('aes-256-cbc', key.toString('binary'), str_repeat('\0', 16));
decipher.update(data, 'base64', 'utf8');
return decipher.final('utf8');
}
function str_repeat(input, multiplier) {
var y = '';
while (true) {
if (multiplier & 1) {
y += input;
}
multiplier >>= 1;
if (multiplier) {
input += input;
} else {
break;
}
}
return y;
}
I hope this helps You.
NOTE: You need to deliver an 265bit aka 32 character key for this algorithm to work.
POSSIBLE .NET SOLUTION: This may help you Example
You should simply write new AesManaged().
You don't need to call Create().
You then need to set Key and IV, then call CreateDecryptor() and put it in a CryptoStream.
It turned out to be a stupid mistake. I thought the create function in Node.js could take a variable argument count. Turns out you need to call the createDecipheriv() instead.
Just for the record, you can easily check the padding and mode by looking at those properties in the Aes object. The defaults are CBC and PKCS7. That padding is also used in nodejs crypto. So a for a 128 key size my code to decrypt a base64 encoded string would be:
var crypto = require('crypto');
var binkey = new Buffer(key, 'base64');
var biniv = new Buffer(iv, 'base64');
var decipher = crypto.createDecipheriv('aes-128-cbc', binkey, biniv);
var decrypted = decipher.update(crypted,'base64','utf8');
decrypted += decipher.final('utf8');
console.log("decrypted", decrypted);
I'm making the equivalent java code for the code below. But I can make something that returns the same result for encodedString. What Java class can I use for achieve the same result?
//Set the Hash method to SHA1
HMAC hash;
switch (validation)
{
case MachineKeyValidation.MD5:
hash = new HMACMD5();
break;
case MachineKeyValidation.SHA1:
default:
hash = new HMACSHA1();
break;
}
//Get the hash validation key as an array of bytes
hash.Key = HexToByte(validationKey);
//Encode the password based on the hash key and
//converts the encrypted value into a string
encodedString = Convert.ToBase64String(hash.ComputeHash(Encoding.Unicode.GetBytes(password)));
Thanks in advance!
:)
I found a solution for the translation code.
There was two main problem. When a request a HMACSHA1 I'm not talking about a SHA1 algorithm, but a HmacSHA1. And there is a difference between the encoding from Java and C#. I was using the correct key, and the correct algorithm, but the encoding was differente.
SecretKeySpec signingKey = new SecretKeySpec(key, "HmacSHA1");
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(signingKey);
// The big problem is difference between C# and Java encoding
byte[] rawHmac = mac.doFinal(data.getBytes("UTF-16LE"));
result = new String(Base64.encode(rawHmac));
See this question about computing hash functions in Java.
And look at the javadoc for java.security.MessageDigest.getInstance(String algorithm).
Edited to add:
Try running the following app to see what providers you have registered.
import java.security.Provider;
import java.security.Security;
public class SecurityTest {
public static void main(String[] args) {
Provider[] providers = Security.getProviders();
for (Provider p : providers) {
System.out.println(p.toString());
}
}
}
You should have at least a few Sun providers listed. If not, you may need to download some security libraries.