简体   繁体   中英

Encrypting in Angular and Decrypt on C#

I am having the code to encrypt data on Angular, But I don't know how to decrypt on server side

  var panno = CryptoJS.AES.encrypt("FEAPS8905Q", "myPassword").toString();

Encrypted as U2FsdGVkX19mi5mXlJ14Lj0XcJBbqMPDzi/UeNXK4Cw= in angular, after sending the encrypted using Http.post method, I am not getting the exact data, instead, getting楀뢖᷈鍩ԏ건뫨샞일䜍钚䁞

I used this reference also Decrypting on C# , but I am getting some data like壓섢⻫捼笺ﵑ戛ꔉ됒퍿誁累♟꘶콒ꚦ

public string Decrypt(string cipherText)
    {
        string EncryptionKey = "myPassword";
        cipherText = cipherText.Replace(" ", "+");
        byte[] cipherBytes = Convert.FromBase64String(cipherText);
        using (Aes encryptor = Aes.Create())
        {
            Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] {  
            0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76  });
            encryptor.Key = pdb.GetBytes(32);
            encryptor.Padding = PaddingMode.None;
            encryptor.IV = pdb.GetBytes(16);
            using (MemoryStream ms = new MemoryStream())
            {
                using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Write))
                {
                    cs.Write(cipherBytes, 0, cipherBytes.Length);
                    cs.Close();
                }
                cipherText = Encoding.Unicode.GetString(ms.ToArray());
            }
        }
        return cipherText;
    }

CryptoJS.AES.encrypt(text, password) implicitly derives encryption key and iv from your password using derivation algorithm which is kind of not-native for C#. Instead of relying on that implicit derivation - it's better to explicitly do that yourself, using well known algorithm such as PBKDF2.

Key derivation is needed because your password can have arbitrary size, but given algorithm (AES) needs key of specific size, for example 256 bits. So we need to go from arbitrary length password to fixed size key (in irreversible way).

Sample javascript code:

function encrypt (msg, pass) {
  // random salt for derivation
  var keySize = 256;
  var salt = CryptoJS.lib.WordArray.random(16);
  // well known algorithm to generate key
  var key = CryptoJS.PBKDF2(pass, salt, {
      keySize: keySize/32,
      iterations: 100
    });
  // random IV
  var iv = CryptoJS.lib.WordArray.random(128/8);      
  // specify everything explicitly
  var encrypted = CryptoJS.AES.encrypt(msg, key, { 
    iv: iv, 
    padding: CryptoJS.pad.Pkcs7,
    mode: CryptoJS.mode.CBC        
  });
  // combine everything together in base64 string
  var result = CryptoJS.enc.Base64.stringify(salt.concat(iv).concat(encrypted.ciphertext));
  return result;
}

Decrypting that in C# is now easy:

public static string Decrypt(string cipherText, string password) {
    byte[] cipherBytes = Convert.FromBase64String(cipherText);
    using (Aes encryptor = Aes.Create()) {
        // extract salt (first 16 bytes)
        var salt = cipherBytes.Take(16).ToArray();
        // extract iv (next 16 bytes)
        var iv = cipherBytes.Skip(16).Take(16).ToArray();
        // the rest is encrypted data
        var encrypted = cipherBytes.Skip(32).ToArray();
        Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(password, salt, 100);
        encryptor.Key = pdb.GetBytes(32);
        encryptor.Padding = PaddingMode.PKCS7;
        encryptor.Mode = CipherMode.CBC;
        encryptor.IV = iv;
        // you need to decrypt this way, not the way in your question
        using (MemoryStream ms = new MemoryStream(encrypted)) {
            using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Read)) {
                using (var reader = new StreamReader(cs, Encoding.UTF8)) {
                    return reader.ReadToEnd();
                }
            }
        }
    }
}    

If you understand the consequences, you can use fixed salt (or for example fixed salt per user in your application), and reduce number of iterations in PBKDF2. Don't use fixed IV though, and don't use part of key as IV either.

Encrypt in node.js:

    var crypto = require('crypto');
var key = '00000000000000000000000000000000'; //replace with your key
var iv = '0000000000000000'; //replace with your IV
var cipher = crypto.createCipheriv('aes256', key, iv)
var crypted = cipher.update(authorizationKey, 'utf8', 'base64')
crypted += cipher.final('base64');
console.log(crypted);

decrypt c#

    string keyString = "00000000000000000000000000000000"; //replace with your key
string ivString = "0000000000000000"; //replace with your iv

byte[] key = Encoding.ASCII.GetBytes(keyString);
byte[] iv = Encoding.ASCII.GetBytes(ivString);

using (var rijndaelManaged =
        new RijndaelManaged { Key = key, IV = iv, Mode = CipherMode.CBC })
        {
            rijndaelManaged.BlockSize = 128;
            rijndaelManaged.KeySize = 256;
            using (var memoryStream =
                   new MemoryStream(Convert.FromBase64String(AuthorizationCode)))
            using (var cryptoStream =
                   new CryptoStream(memoryStream,
                       rijndaelManaged.CreateDecryptor(key, iv),
                       CryptoStreamMode.Read))
            {
                return new StreamReader(cryptoStream).ReadToEnd();
            }
        }

source: https://gsferreira.com/archive/2015/02/how-to-encrypt-in-nodejs-and-decrypt-in-c-sharp/

work for me !

Found matching algorithms for both Angular and C# to encrypt & Decrypt text cipherPhrase text contain both key and IV separated by a demiliter pipe

Angular TypeScript Code

import * as CryptoJS from 'crypto-js';
 
 encrypt(plainText: string): string {    
let cipherPhrase = environment.angularCipherKeyIvPhrase;
// separate the key and Iv from the cipher phrase string

var key = CryptoJS.enc.Utf8.parse(cipherPhrase.split("|")[0]);
var iv = CryptoJS.enc.Utf8.parse(cipherPhrase.split("|")[1]);

var encrypted = CryptoJS.AES.encrypt(CryptoJS.enc.Utf8.parse(plainText), key,
  {
    keySize: 128 / 8,
    iv: iv,
    mode: CryptoJS.mode.CBC,
    padding: CryptoJS.pad.Pkcs7
  });
console.log(encrypted.toString());
return encrypted;
  }

Decrypt Typescript Function

decrypt(encryptedText: string): string {

let cipherPhrase = environment.angularCipherKeyIvPhrase;

// separate the key and Iv from the cipher phrase string
var key = CryptoJS.enc.Utf8.parse(cipherPhrase.split("|")[0]);
var iv = CryptoJS.enc.Utf8.parse(cipherPhrase.split("|")[1]);

var decrypted = CryptoJS.AES.decrypt(encryptedText, key,
  {
    keySize: 128 / 8,
    iv: iv,
    mode: CryptoJS.mode.CBC,
    padding: CryptoJS.pad.Pkcs7
  });

return decrypted.toString(CryptoJS.enc.Utf8);
}

C# function to encrypt / decrypt

        // encryption function similar to Angular cryptoJs
    public static string NgEncrypt(string plainText, string ngCipherKeyIvPhrase)
    {
        var cipherTextArray = ngCipherKeyIvPhrase.Split("|");
        string cipherPhrase = cipherTextArray[0];
        string salt  = cipherTextArray[1];

        byte[] encrypted;
        // Create a RijndaelManaged object  
        // with the specified key and IV.  
        using (var rijAlg = new RijndaelManaged())
        {
            rijAlg.Mode = CipherMode.CBC;
            rijAlg.Padding = PaddingMode.PKCS7;
            rijAlg.FeedbackSize = 128;

            rijAlg.Key = Encoding.UTF8.GetBytes(cipherPhrase);
            rijAlg.IV = Encoding.UTF8.GetBytes(salt);

            // Create a decrytor to perform the stream transform.  
            var encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);

            // Create the streams used for encryption.  
            using (var msEncrypt = new MemoryStream())
            {
                using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                {
                    using (var 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 Convert.ToBase64String(encrypted);
    }

    // decryption function similar to Angular cryptoJs
    public static string NgDecrypt(string encryptedText,string ngCipherKeyIvPhrase)
    {
        string plainText = string.Empty;
        var cipherTextArray = ngCipherKeyIvPhrase.Split("|");
        string cipherPhrase = cipherTextArray[0];
        string salt = cipherTextArray[1];

        byte[] cipherText = Convert.FromBase64String(encryptedText); 
        // Create an RijndaelManaged object  
        // with the specified key and IV.  
        using (var rijAlg = new RijndaelManaged())
        {
            //Settings  
            rijAlg.Mode = CipherMode.CBC;
            rijAlg.Padding = PaddingMode.PKCS7;
            rijAlg.FeedbackSize = 128;

            rijAlg.Key = Encoding.UTF8.GetBytes(cipherPhrase);
            rijAlg.IV = Encoding.UTF8.GetBytes(salt);

            // Create a decryptor to perform the stream transform.  
            var decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);
            
                // Create the streams used for decryption.  
                using (var msDecrypt = new MemoryStream(cipherText))
                {
                    using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                    {

                        using (var srDecrypt = new StreamReader(csDecrypt))
                        {
                        // Read the decrypted bytes from the decrypting stream  
                        // and place them in a string.  
                        plainText = srDecrypt.ReadToEnd();

                        }

                    }
                }

                return plainText;
         
        }

    }

Encrypt data in c#

Refer https://www.c-sharpcorner.com/article/encryption-and-decryption-using-a-symmetric-key-in-c-sharp/ to encrypt data

Decrypt data in angular

Install crypto-js from https://www.npmjs.com/package/crypto-js

//install crypto-js from https://www.npmjs.com/package/crypto-js and import it   

import * as CryptoJS from 'crypto-js';

encrypedcode="Awt%2FgsVGXoQc3sasAkDqbVoXbXlmc2xZzu14cpHxmu1J0pvGYjGCm1uGz0qcpF07"

decryptData(encryptedText:any) {

    try {
      let cipherPhrase = 'decryptkey';

var key = CryptoJS.enc.Utf8.parse(cipherPhrase);
var iv = CryptoJS.enc.Utf8.parse('');

      var decrypted = CryptoJS.AES.decrypt(encryptedText.trim(), key,
        {
          keySize: 128/8,
          iv: iv,
          mode: CryptoJS.mode.CBC,
          padding: CryptoJS.pad.Pkcs7
        });

      return decrypted.toString(CryptoJS.enc.Utf8);


    } catch (e) {
      console.log(e);
    }
  }

 let data=this.decryptData(this.encrypedcode);
console.log(data);

For Encryption the basic idea is to

  • Use PBKDF2 for key generation.
  • Use AES cipher for encryption.

You can read more about this encryption at : CrytoJs officialdocs

Since Decryption is at server(C#) code we need to provide it with values such as- Salt,iv,encrypted text out of our cipher. To pass it we encode this all info in base64 string and pass it to server.

For Encryption in Angular- (my project is in TS) If anyone has errors such as

  • "wordArray doesn't contain concat()"
  • Type 'WordArray' is not assignable to type 'string'

I was able to go through few documentations and was able to find the solution to be able to combine salt, iv and cipher text.

The issue was with Types we need to have a WordArray format of all these 3 to encode.

My approach is far-fetched but it works

  • Convert all LIb.WordArray to ByteArray so its easy to combine all 3 strings to one.
  • Before encoding to Base64 convert the combined to WordArray.
  • Now Send this encoded string to Server for extracting the salt,iv,encryted text at server end to decrypt it.

Make sure you have @types/crypto-js and crypto-js in your project. My version("crypto-js": "^4.0.0", "@types/crypto-js": "^3.1.47").

Encrytion At Angular side TypeScript :

encrypt(msg: string) {
//will have this at C# as well.
var pass = "secret";
var keySize = 256;
//random salt
var salt = CryptoJS.lib.WordArray.random(16);
// to generate key
var key = CryptoJS.PBKDF2(pass, salt, {
  keySize: keySize / 32,
  iterations: 1000,
});
// random IV
var iv = CryptoJS.lib.WordArray.random(128 / 8);
//will attach link where you can find these
var encrypted = CryptoJS.AES.encrypt(msg, key, {
  iv: iv,
  padding: CryptoJS.pad.Pkcs7,
  mode: CryptoJS.mode.CBC,
});

//Convert Lib.WordArray to ByteArray so we can combine them like Concat
var saltwords = this.wordArrayToByteArray(salt);
var ivwords = this.wordArrayToByteArray(iv);
var cryptedText = this.wordArrayToByteArray(encrypted.ciphertext);
// combine everything together in ByteArray.
var header = saltwords.concat(ivwords).concat(cryptedText);
//Now convert to WordArray.
var headerWords = this.byteArrayToWordArray(header);
//Encode this to sent to server
var encodedString = CryptoJS.enc.Base64.stringify(headerWords);
return encodedString;  
}

Type Conversions :

wordArrayToByteArray(wordArray) {
if (
  wordArray.hasOwnProperty("sigBytes") &&
  wordArray.hasOwnProperty("words")
) {
  length = wordArray.sigBytes;
  wordArray = wordArray.words;
}

var result = [],
  bytes,
  i = 0;
while (length > 0) {
  bytes = this.wordToByteArray(wordArray[i], Math.min(4, length));
  length -= bytes.length;
  result.push(bytes);
  i++;
 }
 return [].concat.apply([], result);
 }
byteArrayToWordArray(ba) {
var wa = [],
  i;
for (i = 0; i < ba.length; i++) {
  wa[(i / 4) | 0] |= ba[i] << (24 - 8 * i);
}

return CryptoJS.lib.WordArray.create(wa);
}
wordToByteArray(word, length) {
 var ba = [],
  xFF = 0xff;
 if (length > 0) ba.push(word >>> 24);
 if (length > 1) ba.push((word >>> 16) & xFF);
 if (length > 2) ba.push((word >>> 8) & xFF);
 if (length > 3) ba.push(word & xFF);

return ba;
}

Decryption at C# is same as mentioned in answer given by EVK.

 public static string Decrypt(string cipherText)
    {
        var password = "secret";
        byte[] cipherBytes = Convert.FromBase64String(cipherText);
        using (Aes encryptor = Aes.Create())
        {
            // extract salt (first 16 bytes)
            var salt = cipherBytes.Take(16).ToArray();
            // extract iv (next 16 bytes)
            var iv = cipherBytes.Skip(16).Take(16).ToArray();
            // the rest is encrypted data
            var encrypted = cipherBytes.Skip(32).ToArray();
            Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(password, salt, 1000);
            encryptor.Key = pdb.GetBytes(32);
            encryptor.Padding = PaddingMode.PKCS7;
            encryptor.Mode = CipherMode.CBC;
            encryptor.IV = iv;

            using (MemoryStream ms = new MemoryStream(encrypted))
            {
                using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Read))
                {
                    using (var reader = new StreamReader(cs, Encoding.UTF8))
                    {
                        return reader.ReadToEnd();
                    }
                }
            }
        }
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM