I am trying to add encryption to my chat application . but facing an issue while doing so , i cant
add the data to firebase , it shows an error Unhandled Exception: Invalid argument: Instance of 'Encrypted'. how can i send a message that is encrypted from client side and send it to firebase and then receive the message , then decrypt it . because i am using the library encrypt 5.0.0 for this and to decrypt it , it only accept the Encrypted as parameter .
here is class for encryption and decryption .
import 'package:encrypt/encrypt.dart' as encrypt;
class MyEncryptionDecryption{
///for encrption
static final key = encrypt.Key.fromLength(32);
static final iv = encrypt.IV.fromLength(16);
static final encrypter = encrypt.Encrypter(encrypt.AES(key));
static encryptAES(message ){
final encrypted = encrypter.encrypt(message ,iv: iv);
print(encrypted.base16);
print(encrypted.base64);
return encrypted;
}
static decryptedAES(message){
return encrypter.decrypt(message,iv: iv);
}
}
using it to send message to firebase as this ..
var aes = MyEncryptionDecryption.encryptAES(message);
Map<String, dynamic> messageInfoMap = {
"message": aes,
"sendBy": myUserName,
"ts": lastMessageTs,
"imgUrl": myProfilePic
};
DatabaseMethods()
.addMessage(chatRoomId, messageId, messageInfoMap)
.then((value) {
Map<String, dynamic> lastMessageInfoMap = {
"lastMessage": aes,
"lastMessageSendTs": lastMessageTs,
"lastMessageSendBy": myUserName,
"readStatus" : false ,
"count" : count,
"show" : true,
};
DatabaseMethods().updateLastMessageSend(chatRoomId, lastMessageInfoMap);
and here are the Database functions ..
Future addMessage(String chatRoomId , String messageId,messageInfoMap) async {
return await FirebaseFirestore.instance.
collection("chatrooms").
doc(chatRoomId).collection('chats').doc(messageId).set(messageInfoMap);
}
updateLastMessageSend(String chatRoomId, lastMessageInfoMap){
return FirebaseFirestore
.instance.collection('chatrooms').doc(chatRoomId)
.update(lastMessageInfoMap);
}
Encrypted is an object. You can retrieve actual value using the fields defined in the API of Encrypted, e.g. the property bytes to get the byte representation of the object or base64 if you require the ciphertext as text.
Of course, you can revert back to Encrypted using one of the constructors: just the Encrypted constructor for bytes or fromBase64 for text.
Related
I have an ionic/angular frontend where I have the user registration via firebase. I already retrieve my firebase token, send it to my .net backend and verify the token with the [Authorize] annotation.
After authorization, I want to decode the token and use the id of the user for further processing.
Step get the token from the "Authorization" header
string authHeader = this.HttpContext.Request.Headers["Authorization"];
var decodedFirebaseToken = await fireBaseAuthenticationHelper.GetUserFromFirebaseIdAsync(authHeader.Substring("Bearer ".Length).Trim());
Step retrieve the decoded token
public async Task<FirebaseToken> GetUserFromFirebaseIdAsync(string token)
{
FirebaseToken decodedToken = await FirebaseAuth.DefaultInstance
.VerifyIdTokenAsync(token);
return decodedToken;
}
The problem now is that the FirebaseAuth.DefaultInstance is always null and throws a null pointer exception. I don't know where or how to initialize the DefaultInstance.
On the FirebaseAuth class is a comment:
public sealed class FirebaseAuth : IFirebaseService
{
//
// Summary:
// Gets the auth instance associated with the default Firebase app. This property
// is null if the default app doesn't yet exist.
public static FirebaseAuth DefaultInstance { get; }
So I am pretty sure I have to initialize it somewhere but I can't find where.
You have to initialize the SDK by creating a new FirebaseApp first. See the code samples in https://firebase.google.com/docs/admin/setup
Here's something I don't understand.
I've played with some code examples on how to encrypt a string in the browser and decrypt the ciphered text on the server and I don't see how this is secure at all.
All of the examples assume that the same iv and the key used to encrypt the string will be sent to the server so it knows how to decrypt it.
Isn't that like taping the keys to the safe?
Am I supposed to send the iv and key object with the payload, along with the encrypted string?
I have to be missing something here.
async function generateKey() {
return await window.crypto.subtle.generateKey({
name: "AES-CBC",
length: 256
},
false,
["encrypt", "decrypt"]);
}
async function encryptString(data, key, iv) {
return await window.crypto.subtle.encrypt(
{
name: "AES-CBC",
iv,
},
key,
data
);
}
async function decryptString(data, key, iv) {
const decrypted = await window.crypto.subtle.decrypt(
{
name: "AES-CBC",
iv,
},
key,
data
);
return new TextDecoder("utf-8").decode(new Uint8Array(decrypted));
}
async function example() {
try {
// Create the IV and Key
const iv = window.crypto.getRandomValues(new Uint8Array(16));
const key = await generateKey();
// Convert the string I want to encrypt into an ArrayBuffer
const data = new TextEncoder("utf-8").encode('Hello World!');
// Encrypt the ArrayBuffer
const ciphertext = await encryptString(data, key, iv);
console.log(ciphertext);
// Now I decrypt the obscured string using the same key and iv I used to encrypt it.
const decrypted = await decryptString(ciphertext, key, iv);
// Hello World!
console.log(decrypted);
} catch(error) {
console.log(error);
}
}
Actually, the Symmetric Key between browser and server must be established using Asymmetric key first.
Like HTTS (TLS or SSL) is encrypted channel using Asymmetric key (Public key at Browser and Private key at server).
Thus you may rely on Encrypted Channel using HTTPS (TLS) to protect your Symmetric keys or alternatively, you may develop your own mechanism to protect it using Asymmetric keys, for which you will have to generate pair of keys (common for all users - at server or for every user), publish public key to clients and clients will use that public key to encrypt Symmetric key (and iv) which you will decrypt using server side private key. Once Asymmetric key is established, you can use it for fixed duration as per security requirements and then repeat the process, say after every 1 hour or 6 hours or 24 hours, etc.
In my ASP .NET Core Web API, I'm manually generating a token for mocking purposes. However, it appears the token generator assumes the signature has a private key, while that's not the case. The key is public, retrieved from a JWKS. I don't know how to make this clear to the token generator. Can anyone help me?
This is my code:
private async Task<string> GenerateSecurityToken(string _expDate, string _issuer, string _audience, string _wellKnownEndpoint)
{
var configurationManager = new ConfigurationManager<OpenIdConnectConfiguration>(_wellKnownEndpoint, new OpenIdConnectConfigurationRetriever());
OpenIdConnectConfiguration openIdConfig = await configurationManager.GetConfigurationAsync(CancellationToken.None);
SecurityKey securityKey = openIdConfig.SigningKeys.Last();
var tokenDescriptor = new SecurityTokenDescriptor()
{
Subject = new ClaimsIdentity(),
Expires = DateTime.UtcNow.AddMinutes(double.Parse(_expDate)),
Audience = _audience,
Issuer = _issuer,
SigningCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.RsaSha256Signature)
};
var tokenHandler = new JwtSecurityTokenHandler();
SecurityToken token = tokenHandler.CreateToken(tokenDescriptor); // Raises exception
return tokenHandler.WriteToken(token);
}
And this is the raised exception:
System.InvalidOperationException: IDX10638: Cannot create the SignatureProvider, 'key.HasPrivateKey' is false, cannot create signatures.
The identity provider use private key to encrypt token , and when validating token , you can get public key cryptography to sign tokens and verify that they're valid. So you can use a traditional way to get access token from identity provider using delegating flows , rather than use public key to issue access token manually .
I am working on the web application using react as front-end and spring mvc as back-end. I need to store some user information in local storage of the browser. I do not want to store that info in local storage as a plain text. So I thought to go for AES encryption at server side and pushing those data back to JS side. For that I need client side decryption framework. I found crypto-js as very useful for all these things. I am not able to understand where I am lacking at client side to decrypt and decode.
I am explaining my Spring Side Encryption Code first which is absolutely fine:
public class EncryptDecrypt {
private static final String SECRET_KEY_1 = "ssdkF$HUy2A#D%kd";
private static final String SECRET_KEY_2 = "weJiSEvR5yAC5ftB";
private IvParameterSpec ivParameterSpec;
private SecretKeySpec secretKeySpec;
private Cipher cipher;
public EncryptDecrypt() throws UnsupportedEncodingException, NoSuchPaddingException, NoSuchAlgorithmException {
ivParameterSpec = new IvParameterSpec(SECRET_KEY_1.getBytes("UTF-8"));
secretKeySpec = new SecretKeySpec(SECRET_KEY_2.getBytes("UTF-8"), "AES");
cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
}
public String encrypt(String toBeEncrypt) throws NoSuchPaddingException, NoSuchAlgorithmException,
InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] encrypted = cipher.doFinal(toBeEncrypt.getBytes());
return Base64.encodeBase64String(encrypted);
}
}
At the client side, I am not able to decode and decrypt the code with simple things. Here is my client side code:
var CryptoJS = require("crypto-js");
var data = "Ggydx4oA1+SKBw+unA8BUUm2tnvkQbp1terdF2PEGFYSEZL/ye08op/0b0BauGtIl1dBIodrlKXo2de3MykYmocd3ctxFtIIki01V+M8XeQj6B384o0G+H7NpVx5tCJjPDvdqVRObtxCTqu3r8QRzYTNcMM5bRhbYxCYl8/NRyPQJnmcJDlRBeVOoJiQNA7Qd5UJD/mNivoyMUfYGV7/DlpylQWWwEAHVdgcb865i8jnf3vqURehAXYoaD6Bgodi1EM4H007uv0o6NEOk3H4jQ==";
var key = "weJiSEvR5yAC5ftB";
// Decode the base64 data so we can separate iv and crypt text.
var rawData = atob(data);
var iv = "ssdkF$HUy2A#D%kd";
var crypttext = rawData.substring(16);
console.log(rawData);
// Decrypt...
var plaintextArray = CryptoJS.AES.decrypt(
{ ciphertext: CryptoJS.enc.Base64.parse(crypttext) },
key,
{ iv: iv }
);
console.log(plaintextArray);
console.log(CryptoJS.enc.Base64.stringify(plaintextArray));
var decryptedData = JSON.parse(CryptoJS.enc.Base64.stringify(plaintextArray).toString(CryptoJS.enc.Utf8));
console.log(decryptedData);
P.S: I have sent JSON to client side and so that I am parsing it in the end. I am newbie for encryption and decryption. I am really stuck with what my client side code should look a like. Please help.
You shouldn't pass string as key in CryptoJS. In this case it considers this string not as key, but as password. And generate key from password by using PBKDF. Working example below:
var data = "Ggydx4oA1+SKBw+unA8BUUm2tnvkQbp1terdF2PEGFYSEZL/ye08op/0b0BauGtIl1dBIodrlKXo2de3MykYmocd3ctxFtIIki01V+M8XeQj6B384o0G+H7NpVx5tCJjPDvdqVRObtxCTqu3r8QRzYTNcMM5bRhbYxCYl8/NRyPQJnmcJDlRBeVOoJiQNA7Qd5UJD/mNivoyMUfYGV7/DlpylQWWwEAHVdgcb865i8jnf3vqURehAXYoaD6Bgodi1EM4H007uv0o6NEOk3H4jQ==";
var rawData = CryptoJS.enc.Base64.parse(data);
var key = CryptoJS.enc.Latin1.parse("weJiSEvR5yAC5ftB");
var iv = CryptoJS.enc.Latin1.parse("ssdkF$HUy2A#D%kd");
var plaintextData = CryptoJS.AES.decrypt(
{ ciphertext: rawData },
key,
{ iv: iv });
var plaintext = plaintextData.toString(CryptoJS.enc.Latin1);
console.log(plaintext);
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/crypto-js.js"></script>
BTW, you shouldn't use the same IV every time. In this case you miss the base purpose of IV and CBC mode. Your overall security becomes equal to ECB mode.
I am trying to encrypt a string with Windows RT. Before it was possible to use the ProtectData in the system.security namespace but that does not exist in WinRT. I tried to use the following code but it does not work.
public static async Task<string> EncryptSting(string data)
{
DataProtectionProvider provider = new DataProtectionProvider();
IBuffer unprotectedData = CryptographicBuffer.ConvertStringToBinary(data, BinaryStringEncoding.Utf8);
//crashes here
IBuffer protectedData = await provider.ProtectAsync(unprotectedData);
return CryptographicBuffer.ConvertBinaryToString(BinaryStringEncoding.Utf8, protectedData);
}
public static async Task<string> DecryptString(string data)
{
DataProtectionProvider provider = new DataProtectionProvider();
IBuffer inputData = CryptographicBuffer.ConvertStringToBinary(data, BinaryStringEncoding.Utf8);
//crashes here
IBuffer unprotectedData = await provider.UnprotectAsync(inputData);
return CryptographicBuffer.ConvertBinaryToString(BinaryStringEncoding.Utf8, unprotectedData);
}
Edit: The execption is
The supplied handle is invalid. (Exception from HRESULT: 0x80090026)
and it occurs on the 3rd line when encrypting and decrypting
According to the documentation, the constructor you're using can only be used for decryption, not for encryption:
Constructor used for decryption operations. Use this constructor before calling the UnprotectAsync or UnprotectStreamAsync methods.
For encryption, you must use the other constructor, which specifies if the data should be encrypted for the local machine, current user, specific user, etc.
I don't know why it doesn't work for decryption in your case, but if encryption doesn't work, I'm not sure what you're trying to decrypt...
Try to do the following:
public static async Task<string> EncryptSting(string data)
{
DataProtectionProvider provider = new DataProtectionProvider("LOCAL=user");
...
...
}
Cheers!