It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 10 years ago.
I need to encrypt a string and then be able to decrypt it again.
I implemented the solution here and it works well, but the resulting string is not suitable as it needs to be simple and short enough for a user to use.
I am encrypting incrementing database ID's (from 1) and there won't be more than 500. Ideally I'd like the encrypted string to be not more than 6 characters in length.
Any ideas appreciated..
edit: It's a lengthy form which the user can resume at a later date with this generated string
You can use AES in CTR mode without any padding. In this mode there is a counter that is encrypted and then the result is xor'd with your plaintext which is the number. The result should be small and you will get the encryption of AES which will be better than any substitution cipher you use (which you could probably break by hand). You will have to get the BouncyCastle crypto library however as the Microsoft implementation of Rijndael does not have CTR as an available mode. Below is an example of the AES class you would need to implement. As well as an example of encryption and decryption.
using System;
using System.Text;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
public class AES
{
private readonly Encoding encoding;
private SicBlockCipher mode;
public AES(Encoding encoding)
{
this.encoding = encoding;
this.mode = new SicBlockCipher(new AesFastEngine());
}
public static string ByteArrayToString(byte[] bytes)
{
return BitConverter.ToString(bytes).Replace("-", string.Empty);
}
public static byte[] StringToByteArray(string hex)
{
int numberChars = hex.Length;
byte[] bytes = new byte[numberChars / 2];
for (int i = 0; i < numberChars; i += 2)
{
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
}
return bytes;
}
public string Encrypt(string plain, byte[] key, byte[] iv)
{
byte[] input = this.encoding.GetBytes(plain);
byte[] bytes = this.BouncyCastleCrypto(true, input, key, iv);
string result = ByteArrayToString(bytes);
return result;
}
public string Decrypt(string cipher, byte[] key, byte[] iv)
{
byte[] bytes = this.BouncyCastleCrypto(false, StringToByteArray(cipher), key, iv);
string result = this.encoding.GetString(bytes);
return result;
}
private byte[] BouncyCastleCrypto(bool forEncrypt, byte[] input, byte[] key, byte[] iv)
{
try
{
this.mode.Init(forEncrypt, new ParametersWithIV(new KeyParameter(key), iv));
BufferedBlockCipher cipher = new BufferedBlockCipher(this.mode);
return cipher.DoFinal(input);
}
catch (CryptoException)
{
throw;
}
}
}
Example Usage
string test = "1";
AES aes = new AES(System.Text.Encoding.UTF8);
RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider();
byte[] key = new byte[32];
byte[] iv = new byte[32];
// Generate random key and IV
rngCsp.GetBytes(key);
rngCsp.GetBytes(iv);
string cipher = aes.Encrypt(test, key, iv);
string plaintext = aes.Decrypt(cipher, key, iv);
Response.Write(cipher + "<BR/>");
Response.Write(plaintext);
Output Example
CB
1
//encryption
string output="";
char[] readChar = yourInput.ToCharArray();
for (int i = 0; i < readChar.Length; i++)
{
int no = Convert.ToInt32(readChar[i]) + 10;
string r = Convert.ToChar(no).ToString();
output+=r;
}
//decryption
string output="";
char[] readChar = yourInput.ToCharArray();
for (int i = 0; i < readChar.Length; i++)
{
int no = Convert.ToInt32(readChar[i]) - 10;
string r = Convert.ToChar(no).ToString();
output+=r;
}
if you want it to be simple maybe use a main key of 6 letter
add it to the 6 letters input do a modulo based on the allowed input chars
its like a =1
b =2
c = 3
and then simply add a number a + 13 mod 24 > ...
wont say its secure but its simple as you requested
You might also do some combinations like for the next char is deoded prev char as +xx
Since you said that these are database IDs that are incrementing I'm assuming we're talking about positive integers. Limiting encryption is really not a good idea because the more limited your encryption is the easier it becomes to break. I'm not sure of your goal but it sounds like you may want to simply hide the ID from the end user by "encrypting it". So, if my assumption about these being positive whole integers between 1 and 999 then my suggestions are as follows. One, a simple character substitution. It will be pretty easy to figure out since you'll be limited to 10 characters. Two, convert them to hexidecimal. Won't fool a lot of people but the average end user might not recognize it. Third, convert the number to Base64. This won't be recognized as easily as hexidecimal. Last, come up with some sort of formula you can apply to the number that will always yield a unique result. I really don't advise that however because it's pretty difficult.
Related
I want to migrate following python code into c#.
The entry point is the method encrypted_request
I have no real clue about aes/rsa in python or in c#.
Maybe someone could explain the different code sections and if possible give me a hint how to implement that in c#.
Especially the magic numbers used here and there I do not understand.
modulus = ('00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7'
'b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280'
'104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932'
'575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b'
'3ece0462db0a22b8e7')
nonce = '0CoJUm6Qyw8W8jud'
pubKey = '010001'
def encrypted_request(text):
text = json.dumps(text)
secKey = createSecretKey(16)
encText = aesEncrypt(aesEncrypt(text, nonce), secKey)
encSecKey = rsaEncrypt(secKey, pubKey, modulus)
data = {'params': encText, 'encSecKey': encSecKey}
return data
def aesEncrypt(text, secKey):
pad = 16 - len(text) % 16
text = text + chr(pad) * pad
encryptor = AES.new(secKey, 2, '0102030405060708')
ciphertext = encryptor.encrypt(text)
ciphertext = base64.b64encode(ciphertext).decode('u8')
return ciphertext
def rsaEncrypt(text, pubKey, modulus):
text = text[::-1]
rs = pow(int(binascii.hexlify(text), 16), int(pubKey, 16)) % int(modulus, 16)
return format(rs, 'x').zfill(256)
def createSecretKey(size):
return binascii.hexlify(os.urandom(size))[:16]
Source: https://github.com/darknessomi/musicbox/blob/master/NEMbox/api.py
My current state in c#:
private byte[] hex2Binary(string hex) {
byte[] binaryVal = new byte[hex.Length];
for (int i = 0; i < hex.Length; i++) {
string byteString = hex.Substring(i, 1);
byte b = Convert.ToByte(byteString, 16);
binaryVal[i] = b;
}
return binaryVal;
}
private string aesEncryptBase64(String plainText, string key) {
return aesEncryptBase64(plainText, hex2Binary(key));
}
private string aesEncryptBase64(String plainText, byte[] key) {
//pad = 16 - len(text) % 16
//text = text + chr(pad) * pad
int pad = 16 - plainText.Length % 16;
for (int i=0; i<pad; i++) {
plainText = plainText + ((char)pad);
}
byte[] plainBytes = null;
RijndaelManaged aes = new RijndaelManaged();
//aes.KeySize = 16;
aes.Mode = CipherMode.CBC;
aes.Key = key;
aes.IV = hex2Binary(client.neteaseFix.encryptInfo.iv);
MemoryStream ms = new MemoryStream();
CryptoStream cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write);
cs.Write(plainBytes, 0, plainBytes.Length);
cs.Close();
byte[] encryptedBytes = ms.ToArray();
return Convert.ToBase64String(encryptedBytes); //decode("u8")
}
Here are a couple of things I see right off the bat, but the question is a bit too open-ended:
In aesEncryptBase64 you are manually applying padding. The AES implementation in .NET does that for you. If you prefer to do it yourself you need to set aes.Padding = PaddingMode.None
In aesEncryptBase64 you create a RijndaelManaged object. Don't do that. You want AES, just use AES.Create(), which returns an AES object (not a Rijndael object).
.NET had support for the larger Rijndael algorithm before AES; and Rijndael with a block size of 128 bits is what got selected as AES, but Rijndael supports modes that AES does not, and you shouldn't really use them interchangeably (though many samples do).
In aesEncryptBase64 your aes, ms, and cs objects are all IDisposable, so you should have them in using statements.
The rsaEncrypt method in Python is doing raw RSA, which isn't supported in .NET (nor generally considered a good idea). Unless it's only called by routines which do the padding (and then it's just a pit of side-channel vulnerabilities).
If your rsaEncrypt (in Python) is only being called from routines which do the signature or encryption (or PSS or OAEP) padding then your .NET equivalent would be (using your method naming casing, instead of the normal ones in .NET)
private static rsaEncrypt(string text, string pubKey, string modulus)
{
RSAParameters rsaParams = new RSAParameters
{
Exponent = hex2Binary(pubKey),
Modulus = hex2Binary(modulus),
};
using (RSA rsa = RSA.Create())
{
rsa.ImportParameters(rsaParams);
return rsa.Encrypt(Encoding.ASCII.GetBytes(text), YOUNEEDTOPICKTHEPADDINGMODE);
}
}
It would be worlds better to improve all of the code around this, though, so that it doesn't have to do so much string re-parsing.
I have the following method which takes the plain text and the key text. It is supposed to return a string encrypted with the XOR method as ascii.
public static string encryptXOREng(string plainText,string keyText)
{
StringBuilder chiffreText = new StringBuilder();
byte[] binaryPlainText = System.Text.Encoding.ASCII.GetBytes(plainText);
byte[] binaryKeyText = System.Text.Encoding.ASCII.GetBytes(keyText);
for(int i = 0;i<plainText.Length;i++)
{
int result = binaryPlainText[i] ^ binaryKeyText[i];
chiffreText.Append(Convert.ToChar(result));
}
return chiffreText.ToString();
}
For some characters it runs just fine. But for example if it performs XOR on 'G' & 'M', which is 71 XOR 77 it returns 10. And 10 stands for Line feed. This is then actually not represented by a character in my output. This leads to a plain text of a length being encrypted to a cipher string which is only 2 characters long, in some cases. I suppose this would make a decryption impossible, even with a key? Or are the ascii characters 0 - 31 there but simply not visible?
To avoid non printable chars use Convert.ToBase64String
public static string encryptXOREng(string plainText, string keyText)
{
List<byte> chiffreText = new List<byte>();
byte[] binaryPlainText = System.Text.Encoding.ASCII.GetBytes(plainText);
byte[] binaryKeyText = System.Text.Encoding.ASCII.GetBytes(keyText);
for (int i = 0; i < plainText.Length; i++)
{
int result = binaryPlainText[i] ^ binaryKeyText[i % binaryKeyText.Length];
chiffreText.Add((byte)result);
}
return Convert.ToBase64String(chiffreText.ToArray());
}
PS: In your code you assume keyText is not shorter than plainText, I fixed it also.
As far as i know there are no rules specific to xor-ciphers. Cryptographic functions often output values that are not printable, which makes sense - the result is not supposed to be readable. In stead you may want to use the output bytes directly or a base64 encoded result.
I would do something like:
public static byte[] XORCipher(string plainText, string keyText)
{
byte[] binaryPlainText = System.Text.Encoding.ASCII.GetBytes(plainText);
byte[] binaryKeyText = System.Text.Encoding.ASCII.GetBytes(keyText);
for(int i = 0;i<plainText.Length;i++)
{
binaryPlainText[i] ^= binaryKeyText[i];
}
return binaryPlainText;
}
I am encrypting my data by using RSACryptoServiceProvider() class in c#. I want to decrypt the data in ubuntu, that was encrypted in c#. Can you suggest me which mechanism I need to follow in order to decrypt. Following function is for encryption:
public static void Encrypt(String PublicKey, String plainText, out String cipherText)
{
try
{
int dwKeySize = 1024;
// TODO: Add Proper Exception Handlers
RSACryptoServiceProvider rsaCryptoServiceProvider = new RSACryptoServiceProvider(dwKeySize);
rsaCryptoServiceProvider.FromXmlString(PublicKey);
int keySize = dwKeySize / 8;
byte[] bytes = Encoding.UTF32.GetBytes(plainText);
// The hash function in use by the .NET RSACryptoServiceProvider here is SHA1
// int maxLength = ( keySize ) - 2 - ( 2 * SHA1.Create().ComputeHash( rawBytes ).Length );
int maxLength = keySize - 42;
int dataLength = bytes.Length;
int iterations = dataLength / maxLength;
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i <= iterations; i++)
{
byte[] tempBytes = new byte[(dataLength - maxLength * i > maxLength) ? maxLength : dataLength - maxLength * i];
Buffer.BlockCopy(bytes, maxLength * i, tempBytes, 0, tempBytes.Length);
byte[] encryptedBytes = rsaCryptoServiceProvider.Encrypt(tempBytes, true);
// Be aware the RSACryptoServiceProvider reverses the order
// of encrypted bytes after encryption and before decryption.
// If you do not require compatibility with Microsoft Cryptographic API
// (CAPI) and/or other vendors.
// Comment out the next line and the corresponding one in the
// DecryptString function.
Array.Reverse(encryptedBytes);
// Why convert to base 64?
// Because it is the largest power-of-two base printable using only ASCII characters
stringBuilder.Append(Convert.ToBase64String(encryptedBytes));
}
cipherText = stringBuilder.ToString();
}
catch (Exception e)
{
cipherText = "ERROR_STRING";
Console.WriteLine("Exception in RSA Encrypt: " + e.Message);
//throw new Exception("Exception occured while RSA Encryption" + e.Message);
}
}
Don't use RSA like that. It's not meant to be used that way and it's way too slow.
The right way is to use a symmetric algorithm, e.g. AES, and encrypt the key you used with RSA. See my old blog entry for C# code doing just that.
You need the same mechanisms, but in reverse. Try first, ask later.
I have a project to do for work to do in C#. I have the requirements for a project and this part is just a small piece in the entire project. I was given test data and the result. I need to code it so that I get the correct results. And at the moment I'm not getting the final result.
Please don't question or criticise the requirements, it's what I have and need to sort out and code it.
I was told to take input string "abc" and compute the SHA-1 hash for this. I got this part to work, here is the code:
private string CalculateSHA1Hash(string text, Encoding characterEncoding)
{
byte[] buffer = characterEncoding.GetBytes(text);
SHA1CryptoServiceProvider sha1CryptoServiceProvider = new SHA1CryptoServiceProvider();
byte[] s = sha1CryptoServiceProvider.ComputeHash(buffer);
string hash = BitConverter.ToString(sha1CryptoServiceProvider.ComputeHash(buffer)).Replace("-", "");
return hash;
}
I used UTF8Encoding because none was specified in the requirements doc. The result that I got from this is A9993E364706816ABA3E25717850C26C9CD0D89D.
I was then told to break up this string into 3 string blocks, of 16 characters each, and use just the 1st block. This is what I got:
block1: A9993E364706816A
I was also given 2 keys:
K1: 0123456789ABCDEF
K2: FEDCBA9876543210
Block1 is to be used as input string to a 3DES encryption using the 2 keys.
The result of the cipher text must be 6E5271A3F3F5C418, I am not getting this.
Below is my calculations. Can someone please see if I am doing this correctly and where I am doing this wrong. Chris (on SO) gave me some articles to read but I still can't get the results that I need to get. Is there something that cater for this already, I'm I just totally confused, or what?
public string Encrypt(string message)
{
string result = string.Empty;
// Calculate the SHA1 hash
UTF8Encoding characterEncoding = new UTF8Encoding();
string sha1HashResult = CalculateSHA1Hash(message, characterEncoding);
block1 = sha1HashResult.Substring(0, 16);
byte[] block1ByteArray = characterEncoding.GetBytes(block1);
string key = "0x" + accessKey1 + accessKey2 + accessKey1;
byte[] keyByteArray = StringToByteArray(key).ToArray();
byte[] enc = ComputeTripleDesEncryption(block1ByteArray, keyByteArray);
result = ByteArrayToString(enc);
return result;
}
public byte[] ComputeTripleDesEncryption(byte[] plainText, byte[] key)
{
TripleDESCryptoServiceProvider des = new TripleDESCryptoServiceProvider();
des.Key = key;
des.GenerateIV();
des.Mode = CipherMode.ECB;
des.Padding = PaddingMode.None;
ICryptoTransform ic = des.CreateEncryptor();
byte[] enc = ic.TransformFinalBlock(plainText, 0, plainText.Length);
return enc;
}
private byte[] StringToByteArray(String hex)
{
if (hex.Substring(0, 2) == "0x")
{
hex = hex.Substring(2);
}
int NumberChars = hex.Length;
byte[] bytes = new byte[NumberChars / 2];
for (int i = 0; i < NumberChars; i += 2)
{
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
}
return bytes;
}
private string ByteArrayToString(byte[] ba)
{
string hex = BitConverter.ToString(ba);
return hex.Replace("-", "");
}
I really do not know what to do further.
There's a few things wrong with what you have right now:
You're specifying that you need to use an IV, but you're using ECB (which doesn't use an IV)
You're generating the IV randomly, using GenerateIV(). This will cause the result to be different every time, if you're not using ECB.
You're only performing the transform on the final block, instead of the whole data.
See the following code sample for a decent idea of how to use 3DES in C#:
http://msdn.microsoft.com/en-us/library/system.security.cryptography.tripledescryptoserviceprovider.aspx
I'd guess that since you're specifying an IV, you're actually meant to be using CBC instead of ECB. Try it out and see what you get.
I've been trying to encrypt a password with a public RSA key that is sent to me by the server.
var csp = new CspParameters(1, "Microsoft Strong Cryptographic Provider");
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(1280, csp);
byte[] key = ByteUtils.HexToBytes(client.RSAKey);
RSA.ImportCspBlob(key);
byte[] encrypted = RSA.Encrypt(Encoding.ASCII.GetBytes(password), true);
The hex key is provided in such format:
string key = "30819D300D06092A864886F70D010101050003818B0030818702818100C7BD672D8C634D443840AD809790852770D3A2E99F456D6516329E0205D0645C23FD001D4D070CEE368A20526FEB2402358C915D7E86102B1659AA8651C449C344599F72BE904B8E338E7002E9978453C5BBCCA51AC165AA265069E0EAB1411D11A2FFDD35E5A8296A6A2AF238945874E8206979B0A16E2E4260A161CAB5C905020111";
As the string is 320-bytes long in hex format, I assume the key is 160 bytes (RSA 1280)
Using this method, the provider keeps saying "Bad Version of provider.\r\n".
I've tried several methods, convert it to Base64, simply import it as ASCII / Unicode. Nothing worked so far.
EDIT: My HexToBytes function (which works afaik, it returns me correct 160-b array):
public static byte[] HexToBytes(string pValue)
{
// FIRST. Use StringBuilder.
StringBuilder builder = new StringBuilder();
// SECOND... USE STRINGBUILDER!... and LINQ.
foreach (char c in pValue.Where(IsHexDigit).Select(Char.ToUpper))
{
builder.Append(c);
}
// THIRD. If you have an odd number of characters, something is very wrong.
string hexString = builder.ToString();
if (hexString.Length % 2 == 1)
{
//throw new InvalidOperationException("There is an odd number of hexadecimal digits in this string.");
// I will just add a zero to the end, who cares (0 padding)
Log.WriteLine(LogLevel.Debug, "Hexstring had an odd number of hexadecimal digits.");
hexString += '0';
}
byte[] bytes = new byte[hexString.Length / 2];
// FOURTH. Use the for-loop like a pro :D
for (int i = 0, j = 0; i < bytes.Length; i++, j += 2)
{
string byteString = String.Concat(hexString[j], hexString[j + 1]);
bytes[i] = HexToByte(byteString);
}
return bytes;
}
Your public key is not in the correct format. It is not a CSP blob. It is a DER encoded SubjectPublicKeyInfo structure. You can find source code to parse it or you can write your own. Here is one example of such code.