I’ve recently been asked to write some code to decrypt some data using C# which was originally encrypted in Java. While I managed to get this to work, I had to piece information together from many different sources on the web. Therefore I have decided to simplify the procedure for the next person who comes looking by putting this into one place.
I was provided with the following piece of information by the Java developer (obviously the values here are created to be used for demo purposes):
저는 최근 Java로 암호화 된 것을 C# 으로 사용하여 일부 데이터를 복호화하는 코드를 개발하라는 요청을 받았습니다. 개발을 하는 동안 인터넷 검색을 통해 여러 다른 많은 소스 정보를 수집했습니다. 따라서 이것을 한 곳에 두어 찾는 사람을 위해 이런 복잡한 절차를 단순화하기로 결정했습니다. Java 개발자에 의해 다음 정보를 제공 받았습니다 (분명히 여기에 있는 값은 데모용으로 사용하기 위해 생성되었습니다).
Cyphered data (base64) | r/w1xZb85PokABCycED5Tw== |
Deciphered data | I am Zeb |
Encryption key (hex) | 142eb4a7ab52dbfb971e18daed7056488446b4b2167cf61187f4bbc60fc9d96d |
Initialisation Vector (hex) | 26744a68b53dd87bb395584c00f7290a |
Cipher method | AES/CBC/PKCS5Padding |
Though what needed to be done was straight forward, I faced the following difficulties when discovering how to code my solution:
In our scenario, we will not be working to encrypt passwords. For that, I would recommend the use of Rfc2898DeriveBytes. There are plenty of examples of this on the web.
수행해야 할 작업은 간단했지만 솔루션을 코딩하는 방법을 발견 할 때 다음과 같은 어려움에 직면했습니다.
1. 내가 접한 거의 모든 예제는 Rfc2898DeriveBytes 클래스를 사용하여 암호 보안에 매우 특정한 암호를 salt 하는 방법에 대해 논의했습니다.
2. Base32 인코딩, 16 진수 문자열 및 Base64 문자열의 차이점을 설명하는 샘플이 없습니다.
3. 암호 해독 작업을 할 때 결과는 옳거나 틀릴 것입니다. 알고리즘이 작동하지 않으면 정확히 무엇이 잘못되었는지 알 수 없습니다. 작업중인 데이터가 유효한지 확인하는 온라인 도구가 없는 것 같습니다.
4. 데이터가 Java에서 왔기 때문에 Java가 사용 된 암호 방법에 대해 약간 다른 알고리즘을 사용하여 디코딩에 실패했는지 여부를 확신 할 수 없었습니다. (분명히 이것은 의심의 여지가 있습니다). 이 시나리오에서는 암호를 암호화하지 않습니다. 이를 위해 Rfc2898DeriveBytes 사용을 권장합니다. 웹상 검색하면 이에 대한 많은 예제가 있습니다.
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace CypherExample
{
public sealed class MyCryptoClass
{
protected RijndaelManaged myRijndael;
private static string encryptionKey = "142eb4a7ab52dbfb971e18daed7056488446b4b2167cf61187f4bbc60fc9d96d";
private static string initialisationVector = "26744a68b53dd87bb395584c00f7290a";
// Singleton pattern used here with ensured thread safety
protected static readonly MyCryptoClass _instance = new MyCryptoClass();
public static MyCryptoClass Instance
{
get { return _instance; }
}
public MyCryptoClass()
{
}
public string DecryptText(string encryptedString)
{
using (myRijndael = new RijndaelManaged())
{
myRijndael.Key = HexStringToByte(encryptionKey);
myRijndael.IV = HexStringToByte(initialisationVector);
myRijndael.Mode = CipherMode.CBC;
myRijndael.Padding = PaddingMode.PKCS7;
Byte[] ourEnc = Convert.FromBase64String(encryptedString);
string ourDec = DecryptStringFromBytes(ourEnc, myRijndael.Key, myRijndael.IV);
return ourDec;
}
}
public string EncryptText(string plainText)
{
using (myRijndael = new RijndaelManaged())
{
myRijndael.Key = HexStringToByte(encryptionKey);
myRijndael.IV = HexStringToByte(initialisationVector);
myRijndael.Mode = CipherMode.CBC;
myRijndael.Padding = PaddingMode.PKCS7;
byte[] encrypted = EncryptStringToBytes(plainText, myRijndael.Key, myRijndael.IV);
string encString = Convert.ToBase64String(encrypted);
return encString;
}
}
protected byte[] EncryptStringToBytes(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("Key");
byte[] encrypted;
// Create an RijndaelManaged object
// with the specified key and IV.
using (RijndaelManaged rijAlg = new RijndaelManaged())
{
rijAlg.Key = Key;
rijAlg.IV = IV;
// Create a decrytor to perform the stream transform.
ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);
// 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;
}
protected string DecryptStringFromBytes(byte[] cipherText, byte[] Key, byte[] IV)
{
// Check arguments.
if (cipherText == null || cipherText.Length <= 0)
throw new ArgumentNullException("cipherText");
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("Key");
// Declare the string used to hold
// the decrypted text.
string plaintext = null;
// Create an RijndaelManaged object
// with the specified key and IV.
using (RijndaelManaged rijAlg = new RijndaelManaged())
{
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(cipherText))
{
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();
}
}
}
}
return plaintext;
}
public static void GenerateKeyAndIV()
{
// This code is only here for an example
RijndaelManaged myRijndaelManaged = new RijndaelManaged();
myRijndaelManaged.Mode = CipherMode.CBC;
myRijndaelManaged.Padding = PaddingMode.PKCS7;
myRijndaelManaged.GenerateIV();
myRijndaelManaged.GenerateKey();
string newKey = ByteArrayToHexString(myRijndaelManaged.Key);
string newinitVector = ByteArrayToHexString(myRijndaelManaged.IV);
}
protected static byte[] HexStringToByte(string hexString)
{
try
{
int bytesCount = (hexString.Length) / 2;
byte[] bytes = new byte[bytesCount];
for (int x = 0; x < bytesCount; ++x)
{
bytes[x] = Convert.ToByte(hexString.Substring(x * 2, 2), 16);
}
return bytes;
}
catch
{
throw;
}
}
public static string ByteArrayToHexString(byte[] ba)
{
StringBuilder hex = new StringBuilder(ba.Length * 2);
foreach (byte b in ba)
hex.AppendFormat("{0:x2}", b);
return hex.ToString();
}
}
}
To be able to test our Cipher, the following is a Console Application which would output the results of our Encryption / Decryption.
다음은 암호화 / 복호화 결과를 출력하는 콘솔 애플리케이션입니다.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CypherExample
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("********************Encryption Example******************");
MyCryptoClass crypto = MyCryptoClass.Instance;
string inputText = "I am Zeb";
Console.WriteLine("Plain text is: '{0}'", inputText);
string encryptedText = crypto.EncryptText(inputText);
Console.WriteLine("Encrypted text is '{0}'", encryptedText);
Console.WriteLine();
Console.WriteLine();
Console.WriteLine("********************Decryption Example******************");
string inputEncryptedText = "r/w1xZb85PokABCycED5Tw==";
Console.WriteLine("Input Encrypted text is '{0}'", inputEncryptedText);
string decryptedText = crypto.DecryptText(inputEncryptedText);
Console.WriteLine("Decrypted text is: '{0}'", decryptedText);
Console.ReadLine();
}
}
}
********************Encryption Example******************
Plain text is: 'I am Zeb'
Encrypted text is 'r/w1xZb85PokABCycED5Tw=='
********************Decryption Example******************
Input Encrypted text is 'r/w1xZb85PokABCycED5Tw=='
Decrypted text is: 'I am Zeb'
Key notes about the code above:
1. cypher 데이터는 Base64 문자열입니다. 그래서 Convert.FromBase64String () 메서드를 사용하는 이유입니다.
2. HexStringToByte () 메서드는 16 진수 문자열을 바이트로 변환합니다. 이러한 문자열은 Base64 문자열과 다릅니다.
3. ByteArrayToHexString () 메서드는 HexStringToByte ()의 reverse 한 프로세스입니다.
4. MyCryptoClass.cs 의 33-36 행에 유의하십시오. 이것이 Cipher 메서드를 정의하는 곳입니다.
5. 암호화 된 데이터는 base64이고 key 및 initialisation vector 는 16 진수 값입니다.
6. PaddingMode.PKCS7 은 PKCS5Padding 을 제공합니다.
만약에 key 나 iv 값이 변동되어 오류가 string 값을 byte 로 변환하는 메소드에 오류나면 아래와 같이 변경하시면 됩니다.
protected static byte[] HexStringToByte(string hexString)
{
try
{
//int bytesCount = (hexString.Length) / 2;
//byte[] bytes = new byte[bytesCount];
//for (int x = 0; x < bytesCount; ++x)
//{
// bytes[x] = Convert.ToByte(hexString.Substring(x * 2, 2), 16);
//}
byte[] bytes = Encoding.UTF8.GetBytes(hexString);
return bytes;
}
catch
{
throw;
}
}
c# 으로 telegram 텔레그램 chat bot 메시지 전송하기 (2) | 2021.04.26 |
---|---|
C# : Firebase API 를 통해 C# Wrapper 로 CRUD 및 Access-token 적용하기 (0) | 2021.04.19 |
C# Dapper - model 을 만들지 않고 곧바로 dynamic 키워드 사용 방법 (0) | 2020.12.22 |
json string 문자열을 object 로 변환하기 ( json to object c# ) (6) | 2020.11.25 |
c# System.IO.StreamWriter 로 한글 euc-kr 로 인코딩 하기 ✔ (0) | 2020.10.30 |