AES Encryption in Swift cannot decrypt in C# - c#

I'm currently trying to call a web service with basic authorization and I've spent 3 days on it. But, still stuck on the way to encrypt by swift code.
I already have had the encrypt function working fine by C# but I'm looking for a swift one. Any help, please!
This is my swift code to encrypt a string with Key and Initialization Vector (IV) generated from UUID. Project included CryptoSwift
func getCredential() -> String {
let secretKey = "RlBUX09SVF9Nb2JpbGVfXw=="
let sessionClaim = "this is the secret session claim"
// Create IV
let uuid = SwiftyUUID.Version4UUID()
let plainData = NSData(bytes: uuid as [UInt8], length: uuid.count)//uuidString.data(using: String.Encoding(rawValue: String.Encoding.utf8.rawValue))
// Get IV String
let base64String = plainData.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0))
var encryptedClaimString: String = try! sessionClaim.encrypt(cipher: AES(key: Array(secretKey.utf8), iv: uuid, blockMode: .CBC, padding: PKCS7()))
//var encryptedClaimString = try! sessionClaim.aesEncrypt( key: secretKey, iv: uuidString)
encryptedClaimString += ":"
encryptedClaimString += base64String
let utf8String = encryptedClaimString.data(using: String.Encoding.utf8)
let credentials = utf8String?.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0))
return credentials!;
}
After do some dissect, I got exactly key and iv but the function cannot decrypt to get the sessionClaim as original input. this is the C# decrypt function
public static string Decrypt(byte[] encrypted, byte[] key, byte[] iv)
{
string value = null;
// Create an Rijndael object
// with the specified key and IV.
using (Rijndael rijAlg = Rijndael.Create())
{
rijAlg.Key = key;
rijAlg.IV = iv;
// Create a decrytor to perform the stream transform.
ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);
// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream(encrypted))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
value = srDecrypt.ReadToEnd();
}
}
}
}
return value;
}

Related

How can we identify tampered byte[] in C# AES decryption

We have implemented AES encryption in our project. And it works fine.
But, if the user tampers the byte[] then the Decrypt function returns a wrong plain text with different symbols like � in it.
We want to handle this case by determining that the data is tampered.
Please find below the code of Decrypt function:
public static string Decrypt(string encryptedText)
{
try
{
// First convert the base64 string to byte[].
var cipherText = Convert.FromBase64String(encryptedText);
if (cipherText == null || cipherText.Length <= 0)
throw new ArgumentNullException("cipherText");
string plaintext = null;
// Create an Aes object
using (Aes aesAlg = Aes.Create())
{
aesAlg.Key = Encoding.ASCII.GetBytes("abc");
aesAlg.IV = Encoding.ASCII.GetBytes("xyz");
// Create a decryptor to perform the stream transform.
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
using (MemoryStream msDecrypt = new MemoryStream(cipherText))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
// Read the decrypted bytes from the decrypting stream
plaintext = srDecrypt.ReadToEnd();
}
}
}
}
return plaintext;
}
catch (Exception ex)
{
return null;
}
}
Please help me to solve my issue.
Thanks!

TRIPLEDES padding is invalid and cannot be removed. c# decrypt

I have a problem when performing a decryption in TRIPLEDES that a provider sends me in HEX: EF69FF79BBD7E8E4EF69FF79BBD7E8E4 with the following key "0123456789ABCDEFFEDCBA9876543210", applying the following method:
public IActionResult GetTokenTemp1()
{
TripleDESCryptoServiceProvider tDESalg = new TripleDESCryptoServiceProvider();
MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();
tDESalg.Key = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes("0123456789ABCDEFFEDCBA9876543210"));
byte[] cipherBytes = Convert.FromBase64String("EF69FF79BBD7E8E4EF69FF79BBD7E8E4");
string finalDecrypt = _3desTest.DecryptTextFromMemory(cipherBytes, tDESalg.Key, tDESalg.IV);
return Ok(finalDecrypt);
}
public static string DecryptTextFromMemory(byte[] Data, byte[] Key, byte[] IV)
{
try
{
// Create a new MemoryStream using the passed
// array of encrypted data.
MemoryStream msDecrypt = new MemoryStream(Data);
TripleDESCryptoServiceProvider de = new TripleDESCryptoServiceProvider();
var descritor = de.CreateDecryptor(Key, IV);
// Create a CryptoStream using the MemoryStream
// and the passed key and initialization vector (IV).
CryptoStream csDecrypt = new CryptoStream(msDecrypt,
descritor,
CryptoStreamMode.Read);
// Create buffer to hold the decrypted data.
byte[] fromEncrypt = new byte[Data.Length];
// Read the decrypted data out of the crypto stream
// and place it into the temporary buffer.
csDecrypt.Read(fromEncrypt, 0, fromEncrypt.Length);
string es = new UTF8Encoding().GetString(fromEncrypt);
//Convert the buffer into a string and return it.
return new UTF8Encoding().GetString(fromEncrypt);
}
catch (CryptographicException e)
{
Console.WriteLine("A Cryptographic error occurred: {0}", e.Message);
return null;
}
}
When I leave the default padding or any other to zero or none, I get the following error "adding is invalid and cannot be removed.",
but when I leave the padding at zero or none tripleDescryptorService.Padding = PaddingMode.None I get a format:
padding.none
I don't know what to do very well, when I do it on this page:
https://neapay.com/online-tools/des-calculator.html?data=EF69FF79BBD7E8E4EF69FF79BBD7E8E4&key=0123456789ABCDEFFEDCBA9876543210&algo=3DES&decr=true
I get the desired result.
I'm already desperate, I'm not very expert in encryption.
Thank you so much
The website uses neither a padding nor an IV. Therefore in the code the padding must be disabled and the ECB mode must be applied.
Furthermore the website expects a hex encoded key and ciphertext and returns the decrypted data also hex encoded, which therefore must not be UTF-8 decoded in the code:
public static byte[] DecryptTextFromMemory(byte[] encryptedData, byte[] key)
{
using (TripleDESCryptoServiceProvider tripleDES = new TripleDESCryptoServiceProvider())
{
tripleDES.Key = key;
tripleDES.Padding = PaddingMode.None;
tripleDES.Mode = CipherMode.ECB;
byte[] decryptedData = new byte[encryptedData.Length];
using (MemoryStream msDecrypt = new MemoryStream(encryptedData))
{
ICryptoTransform decryptor = tripleDES.CreateDecryptor(tripleDES.Key, null);
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
csDecrypt.Read(decryptedData, 0, decryptedData.Length);
}
}
return decryptedData;
}
}
For the hex encoding and decoding you can use arbitrary methods, e.g. from here.
With this the code:
byte[] data = HexStringToByteArray("EF69FF79BBD7E8E4EF69FF79BBD7E8E4");
byte[] key = HexStringToByteArray("0123456789ABCDEFFEDCBA9876543210");
Console.WriteLine(ByteArrayToHexString(DecryptTextFromMemory(data, key)));
returns the result of the website:
00000000003331720000000000333172
Please note: Your last change is not useful because it applies conversions and algorithms that are not consistent with the website.

How do you create SHA-2 and AES encrypted string to use in HTTP request header in .NET?

I am trying to write client code that requests for information from an external API over the web.
The API is simple enough (to me) except for the stipulations of how to generate the authorisation key.
First some context:
There are 6 string values required to start with:
token
password
devId
salt
orgId
givenKey
Now for the encryption stuff. First up SHA2.
hashedString = SHA2(token + password + devId)
Followed by AES.
authKey = AES(salt + orgId + "=" + hashedString)
The AES parameters are specified as follows:
Mode = ECB
Padding = PKCS5Padding
Secret Key = givenKey
My problem is that I know next to nothing about cryptography.
Below is the code I have attempting to accomplish the above.
// Generate Authorisation key
byte[] fieldsBytes = Encoding.ASCII.GetBytes(token + password + devId);
byte[] keyBytes = Encoding.ASCII.GetBytes(secretKey);
SHA512 shaM = new SHA512Managed();
string hashedFields = Encoding.ASCII.GetString(shaM.ComputeHash(fieldsBytes));
byte[] encryptedBytes = EncryptStringToBytes_Aes(salt + orgId + "=" + hashedfields,
keyBytes, keyBytes);
string encryptedString = Encoding.ASCII.GetString(encryptedBytes);
private byte[] EncryptStringToBytes_Aes(string plainText, byte[] Key, byte[] IV)
{
// Check arguments.
if (plainText == null || plainText.Length <= 0)
throw new ArgumentNullException("plainText");
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("IV");
byte[] encrypted;
// Create an Aes object
// with the specified key and IV.
using (Aes aesAlg = Aes.Create())
{
aesAlg.Key = Key;
aesAlg.IV = IV;
aesAlg.Padding = PaddingMode.PKCS7;
aesAlg.Mode = CipherMode.ECB;
// Create an encryptor to perform the stream transform.
ICryptoTransform encryptor = aesAlg.CreateEncryptor();
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();
}
}
}
// Return the encrypted bytes from the memory stream.
return encrypted;
}
This code gets a "401" from the external service.
My first issue is that there does not seem to be a NET method named SHA2. The closest I could find is SHA512 and I am not sure if SHA512 is a .NET implementation of SHA2.
Secondly, padding for AES has been specified as PKCS5Padding but again the closest (naming-wise) I could find is PKCS7 which I am not sure about how similar it is to PKCS5.
There is also the matter of an Initialisation Vector (IV), which the AES parameters don't specify but I see C# AES examples including. In the code, I have set it to have the same value as the Key (which I believe
is what the API calls "secret key") out of sheer desperation but I have tried making the request without setting IV to any value and still get back a 401.
I should probably also mention that I am using ASCII encoding to convert to-and-from bytes because I first tried using UTF8 but when it came to actually making the HTTP request, I was getting an exception
saying that header values (remember we are generating an authorisation key that will be tucked in a HTTP request header) should only be encoded in ASCII.
Any help pointing me in the right direction will be immensely appreciated as I am woefully out of my depth with this cryptography stuff.
Don't worry, crypto can feel overwhelmingly complicated. I think you're close.
SHA2 is a family of hash functions. In practice, "SHA2" usually means SHA2-256 or occasionally SHA2-512. My guess is that your external API is probably using the more common SHA2-256.
This answer on crypto.stackexchange explains that PKCS#5 is essentially a subset of PKCS#7. I'd be willing to bet that the API you're calling made the same mistake described in that answer and should really be calling it PKCS7Padding. Your code is fine!
The IV isn't the same thing as the secret key (or just the "key" for AES). The IV should be random for every encryption run. You aren't supposed to derive it from the input plaintext or the input key. Fortunately, AesCryptoServiceProvider.GenerateIV() will generate one for you. It's up to you to prepend it to your output stream, though.
Using Encoding.ASCII.GetBytes() to get the plaintext and secret key bytes makes sense to me. I don't think that's causing a problem.
Stealing from this excellent answer to a similar question (go give them a vote!), I'd try code like this:
static byte[] AesEncrypt(byte[] data, byte[] key)
{
if (data == null || data.Length <= 0)
{
throw new ArgumentNullException($"{nameof(data)} cannot be empty");
}
if (key == null || key.Length != AesKeySize)
{
throw new ArgumentException($"{nameof(key)} must be length of {AesKeySize}");
}
using (var aes = new AesCryptoServiceProvider
{
Key = key,
Mode = CipherMode.CBC,
Padding = PaddingMode.PKCS7
})
{
aes.GenerateIV();
var iv = aes.IV;
using (var encrypter = aes.CreateEncryptor(aes.Key, iv))
using (var cipherStream = new MemoryStream())
{
using (var tCryptoStream = new CryptoStream(cipherStream, encrypter, CryptoStreamMode.Write))
using (var tBinaryWriter = new BinaryWriter(tCryptoStream))
{
// prepend IV to data
cipherStream.Write(iv);
tBinaryWriter.Write(data);
tCryptoStream.FlushFinalBlock();
}
var cipherBytes = cipherStream.ToArray();
return cipherBytes;
}
}
}
Unless there's something else weird going on with this API, I'd guess it's probably #3 above that is causing your request to fail.
I finally managed to get it to work.
A big part of the problem was that API documentation did not specify that hashedString has to
be composed of hexadecimal characters.
It also didn't say that authKey should be a base64 string.
I don't know if this is so standard that it goes without saying but knowing this could have
saved me hours of agony. I was converting the hashed/encrypted bytes back to ASCII
and much of it was unprintable characters that were causing the server to send back
a HTTP response with status 400 BAD_REQUEST.
It also required hashedString to be hashed using SHA256 but the documentation does not mention it.
Thanks to #Nate Barbettini's answer for steering me in the right direction on this.
Also, it appears that AES ECB mode does not require an initialisation vector unlike other
modes like CBC so I didn't specify an IV.
For padding I specified PKCS7 (again thanks to #Nate Barbettini for that).
With that here's the code that finally worked out for me.
string hashedFields = ComputeSha256HashHex(authToken + password + devId);
string encryptedString = AesEncryptToBase64String(saltString + orgId + "=" + hashedFields, secretKey);
private string AesEncryptToBase64String(string plainText, string key)
{
// Convert string arguments into byte arrays
byte[] keyBytes = Encoding.ASCII.GetBytes(key);
byte[] plainTextBytes = Encoding.ASCII.GetBytes(plainText);
// Check arguments.
if (plainText == null || plainText.Length <= 0)
throw new ArgumentNullException("plainText");
if (key == null || key.Length <= 0)
throw new ArgumentNullException("key");
byte[] encrypted;
// Create an Aes object
// with the specified key and IV.
using (Aes aesAlg = Aes.Create())
{
aesAlg.Key = keyBytes;
aesAlg.Padding = PaddingMode.PKCS7;
aesAlg.Mode = CipherMode.ECB;
// Create an encryptor to perform the stream transform.
ICryptoTransform encryptor = aesAlg.CreateEncryptor();
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();
}
}
}
// Return encrypted bytes as Base 64 string
return Convert.ToBase64String(encrypted);
}
private string ComputeSha256HashHex(string plainText)
{
using (SHA256 sha256Hash = SHA256.Create())
{
// ComputeHash - returns byte array
byte[] bytes = sha256Hash.ComputeHash(Encoding.UTF8.GetBytes(plainText));
// Convert byte array to a string
return BytesToHexString(bytes);
}
}
private string BytesToHexString(byte[] bytes)
{
// Convert byte array to a string
StringBuilder builder = new StringBuilder();
for (int i = 0; i < bytes.Length; i++)
{
builder.Append(bytes[i].ToString("x2"));
}
return builder.ToString();
}

padding is invalid and cannot be removed c# decrypt

I saw that exists another questions about this topic, but I checked every question and I can't solve my problem..
This is my method to decrypt and another method to call decrypt method with required parameters:
public string Decrypt(AesOperationType operationType, byte[] criptotext, byte[] Key, byte[] initVector)
{
string plaintext = null;
using (Aes aesAlg = Aes.Create())
{
aesAlg.KeySize = 128;
aesAlg.Key = Key;
aesAlg.IV = initVector;
if (operationType == AesOperationType.Cbc)
{
aesAlg.Mode = CipherMode.CBC;
}
else if (operationType == AesOperationType.Cfb)
{
aesAlg.Mode = CipherMode.ECB;
}
//apelam functia de decriptare
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
using (MemoryStream msDecrypt = new MemoryStream(criptotext))
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
plaintext = srDecrypt.ReadToEnd();
}
}
}
Console.WriteLine("Start decrypt for criptotext : " + BitConverter.ToString(criptotext) + "\n");
Console.WriteLine("Plaintext after decrypt : " + plaintext + "\n");
return plaintext;
}
public byte[] Encrypt_Call()
{
var key = "1212121212121212";
var key_byte = Encoding.ASCII.GetBytes(key);
using (Aes aess = Aes.Create())
{
var iv = aess.IV;
cryptdecrypt object = new cryptdecrypt();
var result = object.Encrypt(AesOperationType.Cbc, "plaintext", key_byte, iv);
return result;
}
}
public void Decrypt_Call()
{
var key = "1212121212121212";
var key_byte = Encoding.ASCII.GetBytes(key);
using (Aes aess = Aes.Create())
{
var iv = aess.IV;
cryptdecrypt object = new cryptdecrypt();
var cryptotext = Encrypt_Call();
var result = object.Decrypt(AesOperationType.Cbc, cryptotext , key_byte, iv);
}
}
Encrypt method works fine, but at decryption method call, I face this error:
Padding is invalid and cannot be removed.
I also tried to put csDecrypt.FlushFinalBlock() this line before this line:
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
The error disappears and as result I get an empty string.
Any ideas to solve this?
Typically an invalid padding error means the decryption failed. In this case with CBC mode the IV is not specified so it will be junk (or random).
Either:
Specify an IV of block length (AES 16-bytes).
create a random IV on encryption and prepend it to the encrypted data. On decryption split the IV off and use for decryption. <–– Best option

decrpyt .Net Encrypted string in iOS

I made some AES encryption in c# and works like a charm. Code here:
public string EncryptStringAES(string plainText, string sharedSecret)
{
if (string.IsNullOrEmpty(plainText))
throw new ArgumentNullException("plainText");
if (string.IsNullOrEmpty(sharedSecret))
throw new ArgumentNullException("sharedSecret");
string outStr = null; // Encrypted string to return
RijndaelManaged aesAlg = null; // RijndaelManaged object used to encrypt the data.
try
{
_pkey = sharedSecret;
// generate the key from the shared secret and the salt
Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedSecret, _salt);
//_key = key.ToString();
// Create a RijndaelManaged object
aesAlg = new RijndaelManaged();
aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
_key = Encoding.ASCII.GetString(aesAlg.Key);
// 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());
}
}
finally
{
// Clear the RijndaelManaged object.
if (aesAlg != null)
aesAlg.Clear();
}
// Return the encrypted bytes from the memory stream.
return outStr;
}
/// <summary>
/// Decrypt the given string. Assumes the string was encrypted using
/// EncryptStringAES(), using an identical sharedSecret.
/// </summary>
/// <param name="cipherText">The text to decrypt.</param>
/// <param name="sharedSecret">A password used to generate a key for decryption.</param>
public string DecryptStringAES(string cipherText, string sharedSecret)
{
if (string.IsNullOrEmpty(cipherText))
throw new ArgumentNullException("cipherText");
if (string.IsNullOrEmpty(sharedSecret))
throw new ArgumentNullException("sharedSecret");
// Declare the RijndaelManaged object
// used to decrypt the data.
RijndaelManaged aesAlg = null;
// Declare the string used to hold
// the decrypted text.
string plaintext = null;
try
{
_pkey = sharedSecret;
// generate the key from the shared secret and the salt
Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedSecret, _salt);
//_key = key.ToString();
// Create the streams used for decryption.
byte[] bytes = Convert.FromBase64String(cipherText);
using (MemoryStream msDecrypt = new MemoryStream(bytes))
{
// Create a RijndaelManaged object
// with the specified key and IV.
aesAlg = new RijndaelManaged();
aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
_key = Encoding.ASCII.GetString(aesAlg.Key);
// Get the initialization vector from the encrypted stream
aesAlg.IV = ReadByteArray(msDecrypt);
// Create a decrytor to perform the stream transform.
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
plaintext = srDecrypt.ReadToEnd();
}
}
}
finally
{
// Clear the RijndaelManaged object.
if (aesAlg != null)
aesAlg.Clear();
}
return plaintext;
}
NOTE:
I am encryting a fair amout of data here(large json string)
Now the next step would be to make the same magic with iOS here is the problem that I am a newb when it comes to IOS and I am hoping someone can point me in the right direction.
Problems:
I so far failed to find an example on iOS how to make Rfc289 key with secret and salt key
I tryed this example the trick here is that the c# code does not work with large strings it only works with short strings.
Basicly I would like some suggestions or mybe some other aproach to make secure communication between rest api Web Service Application and iOS.
Thank you for help.
Add an SSL certificate to your WebAPI end point - then use fully secure communications endpoint to endpoint, you should not code anything except change http:// to https://

Categories