This might be a possible duplicate but I am unable to fix it.
Below is my code in C# for tripleDES:
using System;
using System.Security.Cryptography;
using System.IO;
using System.Text;
class MainClass {
public static void Main (string[] args) {
String encrypt="5241110000602040";
SymmetricAlgorithm sa= SymmetricAlgorithm.Create("TripleDES");
sa.Key= Convert.FromBase64String("FRSF1P3b6fHiW/DXrK8ZJks5KAiyNpP0");
sa.IV=Convert.FromBase64String("YFKA0QlomKY=");
byte[] iba=Encoding.ASCII.GetBytes(encrypt);
MemoryStream mS=new MemoryStream();
ICryptoTransform trans=sa.CreateEncryptor();
byte[] buf= new byte[2049];
CryptoStream cs=new CryptoStream(mS,trans,CryptoStreamMode.Write);
cs.Write(iba,0,iba.Length);
cs.FlushFinalBlock();
Console.WriteLine(Convert.ToBase64String(mS.ToArray()));
}
}
Encrypted value is
Nj7GeyrbJB93HZLplFZwq5HRjxnvZSvU
I want to achieve the same thing with crypto-js library of nodejs. Here is nodejs code of what I tried:
var CryptoJS = require("crypto-js");
var text = "5241110000602040";
var key = "FRSF1P3b6fHiW/DXrK8ZJks5KAiyNpP0";
var options = {
// mode: CryptoJS.mode.ECB,
// padding: CryptoJS.pad.Pkcs7,
iv: CryptoJS.enc.Hex.parse("YFKA0QlomKY=")
};
var textWordArray = CryptoJS.enc.Utf8.parse(text);
var keyHex = CryptoJS.enc.Hex.parse(key);
var encrypted = CryptoJS.TripleDES.encrypt(textWordArray, keyHex, options);
var base64String = encrypted.toString();
console.log('encrypted val: ' + base64String);
Expected output
Nj7GeyrbJB93HZLplFZwq5HRjxnvZSvU
Actual Output
NXSBe9YEiGs5p6VHkzezfdcb5o08bALB
Encrypted value in nodejs is different than C#. What am I doing wrong?
You differently decode key and iv.
In c# you use base64:
sa.Key= Convert.FromBase64String("FRSF1P3b6fHiW/DXrK8ZJks5KAiyNpP0");
sa.IV=Convert.FromBase64String("YFKA0QlomKY=");
in node.js hex:
iv: CryptoJS.enc.Hex.parse("YFKA0QlomKY=")
var key = "FRSF1P3b6fHiW/DXrK8ZJks5KAiyNpP0";
var keyHex = CryptoJS.enc.Hex.parse(key);
Try to use base64 in both cases.
Related
Consider the following code (you also may check in sandbox):
using System;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
class EncryptionIVTest
{
private static readonly string Data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
private static readonly byte[] Password = Guid.NewGuid().ToByteArray().Take(32).ToArray();
static void Main()
{
var iv = Guid.NewGuid().ToByteArray().Take(16).ToArray(); // random initialization vector
var iv2 = new byte[16]; // just another zero-filled initialization vector
var encrypted = Encrypt(iv);
Console.WriteLine($"Original: {Data}");
Console.WriteLine($"Encrypted: {encrypted}");
Console.WriteLine($"Decrypted: {Decrypt(encrypted, iv)}");
Console.WriteLine($"Decrypted with another IV: {Decrypt(encrypted, iv2)}"); // It should throw exception or output completely mangled string
}
private static string Encrypt(byte[] iv)
{
var cipher = CreateCipher(iv);
var buf = Encoding.UTF8.GetBytes(Data);
using var ms = new MemoryStream();
using (var stream = new CryptoStream(ms, cipher.CreateEncryptor(), CryptoStreamMode.Write))
stream.Write(buf, 0, buf.Length);
return Convert.ToBase64String(ms.ToArray());
}
private static string Decrypt(string encrypted, byte[] iv)
{
var cipher = CreateCipher(iv);
using var ms = new MemoryStream(Convert.FromBase64String(encrypted));
using var result = new MemoryStream();
using (var stream = new CryptoStream(ms, cipher.CreateDecryptor(), CryptoStreamMode.Read))
stream.CopyTo(result);
return Encoding.UTF8.GetString(result.GetBuffer(), 0, (int)result.Length);
}
private static Aes CreateCipher(byte[] iv)
{
var cipher = Aes.Create();
cipher.Key = Password;
cipher.IV = iv;
cipher.Mode = CipherMode.CBC;
return cipher;
}
}
It outputs:
Original: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Encrypted: EROKh8lVgREvTqzBYXjEm7EbTIT883uR9wsD82lRM14KtiOYr+/+ZpAwz/UfprqSP5mIQ7Du/d43Y88hAPjvkA==
Decrypted: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Decrypted with another IV: ???#?n? ??7║??Paaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
The fourth string is not fully mangled, it contains an untouched trailing. It seems like encryptor only mangle first 16 bytes (size of an initialization vector) and leaves other untouched. By default, encryptor uses CBC CipherMode and it should mangle all data if I understand correctly.
Is it possible to mangle all data, not only the first part?
The purpose of the IV is not to "mangle" data further or to serve as a second encryption key - that would just make it redundant of the actual key.
The purpose is to provide additional entropy so that two sets of plaintext data encrypted with the same key but with different IVs will appear completely different when encrypted. This makes it harder for an attacker to infer anything about the data. For example, without the IV, sophisticated attackers could run statistical analyses based on language patterns and potentially figure out what certain encrypted packets actually are based on how frequently they occur.
So what you're seeing should not be surprising or concerning. The IV is doing its job.
By the way, using a Guid as a key is NOT secure. First of all it's only 16-bytes not 32 so you only have a 128-bit key basically. See https://learn.microsoft.com/en-us/dotnet/standard/security/generating-keys-for-encryption-and-decryption#symmetric-keys for the right apis to use to generate keys and IVs
I want to run my C# code to login a web site, so I need to implement the RSA Encryption method in the web site. Below is my C# and JavaScript test code to encrypt "test", but they display the different results.How to modify the C# code to get the same result as the JavaScript code?
JavaScript:Save the code to a html file and opened by web browser will see the result.
Or run the code online:https://onlinegdb.com/fzKiCrbGf
It is the JavaScript RSA library with documentation comments:http://www.ohdave.com/rsa/RSA.js
<script src="http://www.ohdave.com/rsa/RSA.js"></script>
<script src="http://www.ohdave.com/rsa/Barrett.js"></script>
<script src="http://www.ohdave.com/rsa/BigInt.js"></script>
<script>
setMaxDigits(130);
var key = new RSAKeyPair("010001","","906C793510FB049452764740B21B97A51DAEA794AB6E43836269D5E6317D49226C12362BA22DAB5EC3BC79553A8A098B01F3C4D81A87B3EE5BD2F4F1431CC495EE2FE54688B212145BB32D56EEEEE1430CE26234331B291CFC53C9B84FAFFDF0B44371A032880C3D567F588D2CD5FCE28D9CDD2923CB547DAD219A6A1B8B5D3D");
var result=encryptedString(key,"test")
document.write(result);
</script>
C#:It is the code of the C# Consloe Program.Run the code will see the result output to the consloe.
Or run the code online:https://onlinegdb.com/B1wG_5rXu
class Program
{
static void Main(string[] args)
{
System.Security.Cryptography.RSAParameters rsaParams = new System.Security.Cryptography.RSAParameters
{
Modulus = HexToByteArray("906C793510FB049452764740B21B97A51DAEA794AB6E43836269D5E6317D49226C12362BA22DAB5EC3BC79553A8A098B01F3C4D81A87B3EE5BD2F4F1431CC495EE2FE54688B212145BB32D56EEEEE1430CE26234331B291CFC53C9B84FAFFDF0B44371A032880C3D567F588D2CD5FCE28D9CDD2923CB547DAD219A6A1B8B5D3D"),
Exponent = HexToByteArray("010001"),
};
System.Security.Cryptography.RSACryptoServiceProvider rsa = new System.Security.Cryptography.RSACryptoServiceProvider();
rsa.ImportParameters(rsaParams);
byte[] result = rsa.Encrypt(System.Text.Encoding.UTF8.GetBytes("test"), false);
System.Console.Write(ByteArrayToHex(result));
System.Console.ReadKey();
}
public static byte[] HexToByteArray(string hex)
{
return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
}
public static string ByteArrayToHex(byte[] ba)
{
System.Text.StringBuilder hex = new System.Text.StringBuilder(ba.Length * 2);
foreach (byte b in ba)
hex.AppendFormat("{0:x2}", b);
return hex.ToString();
}
}
The JavaScript library uses textbook rsa by default, i.e. none of the usual (non-deterministic) paddings (PKCS#1 v1.5 or OAEP) are applied (note that the JavaScript library also supports PKCS#1 v1.5 padding).
textbook RSA is insecure and should therefore not be used in practice! As noted in the comment, .NET does not support this insecure variant out-of-the-box, so a third-party library must be applied, e.g. BouncyCastle.
Furthermore, the JavaScript library internally reverses the order of the plaintext, which must therefore be done explicitly in the C# code.
The JavaScript code below:
setMaxDigits(130);
var key = new RSAKeyPair("010001","","906C793510FB049452764740B21B97A51DAEA794AB6E43836269D5E6317D49226C12362BA22DAB5EC3BC79553A8A098B01F3C4D81A87B3EE5BD2F4F1431CC495EE2FE54688B212145BB32D56EEEEE1430CE26234331B291CFC53C9B84FAFFDF0B44371A032880C3D567F588D2CD5FCE28D9CDD2923CB547DAD219A6A1B8B5D3D");
var result=encryptedString(key,"test")
document.write(result);
<script src="http://www.ohdave.com/rsa/BigInt.js"></script>
<script src="http://www.ohdave.com/rsa/Barrett.js"></script>
<script src="http://www.ohdave.com/rsa/RSA.js"></script>
produces the following ciphertext:
4331ef280f5fd4f4c53fc2367c90fceb5cc65eca7b343cb5e67c120e4a47202e5343f9b9952f885542053d7c408495a2a3f53da9d13839fcd5b0fc044543ffccd44e8057015534c4ff0f1b849619cf0e5b2c86751c6f6effbc4555158c5000876cc0bb5915abdfbcf211be8a195a97b3fb1662c71a20d8183c589da5a5549b55
A possible implementation in C# that provides the result of the JavaScript code is:
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Security;
using System;
using System.Text;
...
// Encode plaintext and reverse order
string plaintext = "test";
var dataToEncrypt = Encoding.UTF8.GetBytes(plaintext);
Array.Reverse(dataToEncrypt);
// Import key via modulus and public exponent
string modulus = "906C793510FB049452764740B21B97A51DAEA794AB6E43836269D5E6317D49226C12362BA22DAB5EC3BC79553A8A098B01F3C4D81A87B3EE5BD2F4F1431CC495EE2FE54688B212145BB32D56EEEEE1430CE26234331B291CFC53C9B84FAFFDF0B44371A032880C3D567F588D2CD5FCE28D9CDD2923CB547DAD219A6A1B8B5D3D";
string exponent = "010001";
BigInteger rsaPubMod = new BigInteger(modulus, 16);
BigInteger rsaPubExp = new BigInteger(exponent, 16);
RsaKeyParameters rsaPublic = new RsaKeyParameters(false, rsaPubMod, rsaPubExp);
// Encrypt with NoPadding (= textbook RSA) - Be aware that this is insecure!!!
var cipher = CipherUtilities.GetCipher("RSA/NONE/NoPadding");
cipher.Init(true, rsaPublic);
var encryptedData = cipher.DoFinal(dataToEncrypt);
// Hex encode the data
var encryptedDataHex = BitConverter.ToString(encryptedData).Replace("-", "").ToLower();
Console.WriteLine(encryptedDataHex); // 4331ef280f5fd4f4c53fc2367c90fceb5cc65eca7b343cb5e67c120e4a47202e5343f9b9952f885542053d7c408495a2a3f53da9d13839fcd5b0fc044543ffccd44e8057015534c4ff0f1b849619cf0e5b2c86751c6f6effbc4555158c5000876cc0bb5915abdfbcf211be8a195a97b3fb1662c71a20d8183c589da5a5549b55
First some background, in case I'm taking the wrong approach. I have two requirements:
I want to encrypt the data written and read from AnonymousPipeServerStream and AnonymousPipeClientStream
I must use a FIPS-compliant NIST-accredited cryptographic module.
I'm actually using StreamJsonRpc to read and write the pipes, so I have no control over how many bytes are read and written at once. What I'm looking for is an encrypting/decrypting Stream that I can use to wrap unencrypted streams.
I'm trying to use the FIPS-compliant Bouncy Castle .Net library to do this using AES in CTR mode, which I understand is a reasonable way to encrypt a stream.
I can't work out how to increment the counter when encrypting a long data stream, so I get an exception "counter in CTR mode out of range".
Below is a compilable console app which demonstrates what I have so far. It's strongly based on the sample code in section 3.2.2 of the Bouncy Castle BC-FNA user guide.
The code below works, but if you increase the amount of data written to the stream by changing const int COPIES = 50; to const int COPIES = 60;, it will throw an exception.
My questions:
Is this a reasonable approach?
If so, how can I handle large streams?
The code:
using System;
using System.IO;
using System.Text;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Fips;
using Org.BouncyCastle.Utilities.Encoders;
namespace Demo
{
class Program
{
static void Main()
{
const int COPIES = 50;
CryptoServicesRegistrar.SetApprovedOnlyMode(true);
var key = new FipsAes.Key(Hex.Decode("aafd12f659cae63489b479e5076ddec2"));
IBlockCipherService provider = CryptoServicesRegistrar.CreateService(key);
// define IV – note 15 bytes, so max message length 255 * 16 bytes
byte[] iv = Hex.Decode("000102030405060708090a0b0c0d0e");
// define data to be encrypted.
string text = "'Twas brillig, and the slithy toves did gyre and gymble in the wabe.\n";
byte[] toBeEncrypted = Encoding.UTF8.GetBytes(text);
// encrypt the data.
var bOut = new MemoryStream();
var encryptorBldr = provider.CreateEncryptorBuilder(FipsAes.Ctr.WithIV(iv));
var encryptor = encryptorBldr.BuildCipher(bOut);
using (Stream encryptingStream = encryptor.Stream)
{
// Write several copies a byte at a time to prove it works.
writeMultipleCopiesOneByteAtOnce(toBeEncrypted, COPIES, encryptingStream); // Change copies to 60 and it breaks.
}
byte[] cipherText = bOut.ToArray();
// decrypt the resulting cipher text
var decryptorBldr = provider.CreateDecryptorBuilder(FipsAes.Ctr.WithIV(iv));
var decryptor = decryptorBldr.BuildCipher(new MemoryStream(cipherText));
using var decIn = decryptor.Stream;
var bytes = readAllOneByteAtOnce(decIn); // Prove that we can read the decryptor stream a byte at a time.
string result = Encoding.UTF8.GetString(bytes); // Print decrypted text.
Console.WriteLine(result);
}
// Just for test purposes. I wouldn't normally write bytes like this.
static void writeMultipleCopiesOneByteAtOnce(byte[] data, int copies, Stream output)
{
for (int i = 0; i < copies; ++i)
{
foreach (byte b in data)
output.WriteByte(b);
}
}
// Just for test purposes. I wouldn't normally read bytes like this.
static byte[] readAllOneByteAtOnce(Stream stream)
{
using var memStream = new MemoryStream();
while (true)
{
int b = stream.ReadByte();
if (b < 0)
return memStream.ToArray();
memStream.WriteByte((byte)b);
}
}
}
}
so i using this code to encrypt my file
as you can see iam using public PGP
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1.2.6 (GNU/Linux)
masdSFVkRBADYxPZYC+nu9nhSVkxcVkVJ5axZKzCRuygqUxka
kZIBy2CAQVKz5dBkRaUkaaksbcyautks7asaov26Fc9cT25Rvnh7
wYIJhcRoIl4cxashdgutasd0qfcOnVB5JVCQDhXclBW7kwCgkoUW
....
...
...
-----END PGP PUBLIC KEY BLOCK-----
the code works fine but i think the data of the encrepted file is corrupted
because it doesnt comes out in this format (like the key)
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1.2.6 (GNU/Linux)
masdSFVkRBADYxPZYC+nu9nhSVkxcVkVJ5axZKzCRuygqUxka
kZIBy2CAQVKz5dBkRaUkaaksbcyautks7asaov26Fc9cT25Rvnh7
wYIJhcRoIl4cxashdgutasd0qfcOnVB5JVCQDhXclBW7kwCgkoUW
....
...
...
-----END PGP PUBLIC KEY BLOCK-----
am i wrong?
dont the output should be in the same format ?
using System;
using System.Xml;
using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.Xml;
using System.Text;
using Org.BouncyCastle.Bcpg.OpenPgp;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities.IO;
using Org.BouncyCastle.Utilities.Encoders;
using Org.BouncyCastle.Bcpg;
class CPGPencrypt
{
private static PgpPublicKey ReadPublicKey(Stream inputStream)
{
inputStream = PgpUtilities.GetDecoderStream(inputStream);
PgpPublicKeyRingBundle pgpPub = new PgpPublicKeyRingBundle(inputStream);
//
// we just loop through the collection till we find a key suitable for encryption, in the real
// world you would probably want to be a bit smarter about this.
//
//
// iterate through the key rings.
//
foreach (PgpPublicKeyRing kRing in pgpPub.GetKeyRings())
{
foreach (PgpPublicKey k in kRing.GetPublicKeys())
{
if (k.IsEncryptionKey)
{
return k;
}
}
}
throw new ArgumentException("Can't find encryption key in key ring.");
}
private static byte[] EncryptFile(byte[] clearData, string fileName, PgpPublicKey encKey, bool withIntegrityCheck)
{
MemoryStream bOut = new MemoryStream();
PgpCompressedDataGenerator comData = new PgpCompressedDataGenerator(
CompressionAlgorithmTag.Zip);
Stream cos = comData.Open(bOut); // open it with the final destination
PgpLiteralDataGenerator lData = new PgpLiteralDataGenerator();
// we want to Generate compressed data. This might be a user option later,
// in which case we would pass in bOut.
Stream pOut = lData.Open(
cos, // the compressed output stream
PgpLiteralData.Binary,
fileName, // "filename" to store
clearData.Length, // length of clear data
DateTime.UtcNow // current time
);
pOut.Write(clearData, 0, clearData.Length);
lData.Close();
comData.Close();
PgpEncryptedDataGenerator cPk = new PgpEncryptedDataGenerator(SymmetricKeyAlgorithmTag.Cast5, new SecureRandom());
cPk.AddMethod(encKey);
byte[] bytes = bOut.ToArray();
MemoryStream encOut = new MemoryStream();
Stream os = encOut;
Stream cOut = cPk.Open(os, bytes.Length);
cOut.Write(bytes, 0, bytes.Length); // obtain the actual bytes from the compressed stream
cOut.Close();
encOut.Close();
return encOut.ToArray();
}
public static string Encrypt(string file_name,string file_to_read)
{
try
{
byte[] dataBytes = File.ReadAllBytes(file_to_read);
Stream keyIn = File.OpenRead("pgpdata-public.asc");
Stream outStream = File.Create(#"myfolder\"+file_name);
byte[] encrypted = EncryptFile(dataBytes, #"myfolder\"+file_name, ReadPublicKey(keyIn), false);
outStream.Write(encrypted, 0, encrypted.Length);
keyIn.Close();
outStream.Close();
}
catch (Exception e)
{
return e.Message;
}
return file_name;
}
}
There are different encoding schemes in OpenPGP, namely
binary data and
ASCII armored data.
Especially for key exchange, normally the ASCII armored format is preferred as it is more robust and easy to recognize. For mail exchange, it is mandatory (for 7 bit compatibility). The binary version also has advantages, especially regarding performance and storage (bandwith) requirements.
For example, GnuPG will use the binary encoding by default, unless you request the ASCII armored version using the option --ascii or abbreviated -a.
It look like your code is outputting the binary encoding, but works all fine.
You can easily test by trying to decrypt (eg. using GnuPG: gpg --decrypt file.pgp). Alternatively, you can dump the OpenPGP packets the file contains by using gpg --list-packets file.pgp or using the more verbose utility pgpdump, which is available in most (unix) package repositories: pgpdump file.pgp. Unlike gpg --list-packets, it also resolves packet and algorithm identifiers to human readable strings (where gpg --list-packets just dumps their numeric IDs).
I'm busy trying to port Java code that looks like this
Cipher rsa = Cipher.getInstance("RSA/ECB/nopadding");
rsa.init(Cipher.DECRYPT_MODE, RSAPrivateKey);
decryptedData = rsa.doFinal(data, 0, 128);
to C#, but as it seems the RSACryptoServiceProvider, forces you to either use OEAP or PKCS1 padding. I know no padding isn't secure, but in this case Im working with a closed source client, so I can't do anything about that. Is there any way around this padding issue?
You might want to get the code from BouncyCastle, http://www.bouncycastle.org/csharp/, and modify the code from the link below, and ensure that it can use the encryption that you list above.
http://www.java2s.com/Code/Java/Security/Whatisinbouncycastlebouncycastle.htm
BouncyCastle will help us to make nopadding RSA encryption.
public string RsaEncryptWithPublic(string clearText, string publicKey)
{
// analogue of Java:
// Cipher rsa = Cipher.getInstance("RSA/ECB/nopadding");
try
{
var bytesToEncrypt = Encoding.ASCII.GetBytes(clearText);
var encryptEngine = new RsaEngine(); // new Pkcs1Encoding (new RsaEngine());
using (var txtreader = new StringReader("-----BEGIN PUBLIC KEY-----\n" + publicKey+ "\n-----END PUBLIC KEY-----"))
{
var keyParameter = (AsymmetricKeyParameter)new PemReader(txtreader).ReadObject();
encryptEngine.Init(true, keyParameter);
}
var encrypted = Convert.ToBase64String(encryptEngine.ProcessBlock(bytesToEncrypt, 0, bytesToEncrypt.Length));
return encrypted;
}
catch
{
return "";
}
}
also dont forget to put it at top:
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.OpenSsl;