Approaches to encrypt a string inside asp.net mvc - c#

I am working on an ASP.NET MVC 5 web application inside VS 2012 and I am using IIS 8 to deploy the web application.
I have a security token which I am using to call a third party WebAPI. Currently inside my controller class, I define and use the token as follows:
string token = "D12356";
string url = currentURL + "resources?AUTHTOKEN=" + token;
Is there is a way to encrypt this value, so if anyone accesses the code inside VS or anyone reverse engineers the .dll files on IIS they won't see the actual token value, but will instead see the encrypted value?

Is there is a way to encrypt this value, so if anyone accesses the code inside VS or anyone reverse engineers the .dll files on IIS they won't see the actual token value, but will instead see the encrypted value?
Well, yes, you can embed an encrypted value in the code, but the problem is that whoever decompiles the library will also see how you decrypt it.
Since you're talking about ASP.NET, your web.config is just as vulnerable as your source code, so there's no added security there.
The solution is to either store the value somewhere secure outside of your web app (secured database?), or use some external value as part of your decryption process, like a certificate or other private key value.

The following class has the encryption and decryption process, through which one can encrypt or decrypt its data with the provision of some values i.e.
Key = string / byte[] to encrypt or decrypt the input
Input = the user required field on which he wants to apply cryptography
Please write this class as follows:
namespace SomeNameSpace
{
public enum CryptType { ENCRYPT, DECRYPT }
public enum CryptTechnique { AES, RC2, RIJ, DES, TDES }
public class Cryptography
{
public object Crypt(CryptType EncryptOrDecrypt, CryptTechnique CryptographicTechnique, object Input, string Key)
{
try
{
SymmetricAlgorithm SymAlgo; //This class is parent of all classes in CryptTechnique enums
switch (CryptographicTechnique)
{
case CryptTechnique.AES:
SymAlgo = new AesManaged();
break;
case CryptTechnique.RC2:
SymAlgo = new RC2CryptoServiceProvider();
break;
case CryptTechnique.RIJ:
SymAlgo = new RijndaelManaged();
break;
case CryptTechnique.DES:
SymAlgo = new DESCryptoServiceProvider();
break;
case CryptTechnique.TDES:
SymAlgo = new TripleDESCryptoServiceProvider();
break;
default:
return false;
}
SymAlgo.Key = UTF8Encoding.UTF8.GetBytes(Key);
SymAlgo.Padding = PaddingMode.PKCS7;
SymAlgo.Mode = CipherMode.ECB;
ICryptoTransform ICT = null;
byte[] resultArray;
if(EncryptOrDecrypt == CryptType.ENCRYPT)
{
ICT = SymAlgo.CreateEncryptor();
}
else if(EncryptOrDecrypt == CryptType.DECRYPT)
{
ICT = SymAlgo.CreateDecryptor();
}
if (Input is string)
{
byte[] inputArray = UTF8Encoding.UTF8.GetBytes(Input as string);
resultArray = ICT.TransformFinalBlock(inputArray, 0, inputArray.Length);
SymAlgo.Clear();
return Convert.ToBase64String(resultArray, 0, resultArray.Length);
}
else if (Input is byte[])
{
resultArray = ICT.TransformFinalBlock(Input as byte[], 0, (Input as byte[]).Length);
SymAlgo.Clear();
return resultArray;
}
return false;
}catch(Exception ex)
{
return ex.Message;
}
}
}
}
and in some controller where you want to encrypt or decrypt data, write there as
public ActionResult SomeAction()
{
string Key = "1234567890abcdef"; //key must have 16 chars, other wise you may get error "key size in not valid".
Password = "Secret";
Cryptography Crypt = new Cryptography();
EncryptedPassword = (string)Crypt.Crypt(CryptType.ENCRYPT, CryptTechnique.RIJ, Password, Key);
}
Here you will get the encrypted password in EncryptedPassword variable

Related

Can't extract private key from PCKS#12 encoded certificate file in .NET 6

I'm writing a .NET 6 application for Windows that is intended to extract the private key from a PFX file containing an RSA cert/key bundle.
public static Boolean ToCertAndKey(String pfxFilePath, String? unlockPassword, String certFilePath, String keyFilePath, String? keyPassword, out String error) {
try {
error = String.Empty;
using var bundle = new X509Certificate2(pfxFilePath, unlockPassword);
RSA key = bundle.GetRSAPrivateKey();
Byte[] publicKeyBytes = key.ExportSubjectPublicKeyInfo();
Byte[] privateKeyBytes;
//We fail here.
if (String.IsNullOrEmpty(keyPassword)) {
privateKeyBytes = key.ExportPkcs8PrivateKey();
} else {
privateKeyBytes = key.ExportEncryptedPkcs8PrivateKey(keyPassword,
new PbeParameters(
PbeEncryptionAlgorithm.Aes256Cbc,
HashAlgorithmName.SHA256,
iterationCount: 1));
}
String encodedCert = new(PemEncoding.Write("PUBLIC KEY", publicKeyBytes));
File.WriteAllText(certFilePath, encodedCert);
String encodedKey = new(PemEncoding.Write("PRIVATE KEY", privateKeyBytes));
File.WriteAllText(keyFilePath, encodedKey);
return true;
} catch (Exception ex) {
error = $"An exception occurred: '{ex.Message}'\r\n\r\nStack Trace:\r\n{ex.StackTrace}";
return false;
}
}
It fails at both ExportPkcs8PrivateKey (When I don't specify a password to encrypt the key) and ExportEncryptedPkcs8PrivateKey (when I do) with the same exception text:
WindowsCryptographicException: The requested operation is not supported
I came across this answer however, I'm still receiving the same exception at RSA.ExportEncryptedPkcs8PrivateKey.
There doesn't appear to be anything wrong with the PFX files I've been testing with; I'm able to import them into my certstore via the UI or PowerShell with no issues.
Hoping someone else has run into this issue.
You need to mark the keys as exportable.
Change
using var bundle = new X509Certificate2(pfxFilePath, unlockPassword);
to
using var bundle = new X509Certificate2(pfxFilePath, unlockPassword, X509KeyStorageFlags.Exportable);

What is the Java equilant of this C# method RSACryptoServiceProvider.ExportCspBlob()

We wrote a c# server app that muliple clients use a couple of years ago. Some use C++ and others use c# and others use python (They can all connect). A client is trying to use Java and just can't seem to get it to work.
The issue is the PublicKey representation.
In c# I don't send the raw byte [] publicKey, I convert it to a blob using RSACryptoServiceProvider.ExportCspBlob(). So in the server app I simply use rsaCSP.ImportCspBlob(publicKeyBlob);
QUESTION
How can I use Java to create a RSACryptoServiceProvider.ExportCspBlob() byte[] representation of the publicKey
C# SERVER CODE
public static bool VerifySignature(byte[] hash, byte[] signature, byte[] publicKeyBlob)
{
RSACryptoServiceProvider rsaCSP = new RSACryptoServiceProvider();
try
{
rsaCSP.ImportCspBlob(publicKeyBlob);
bool res = rsaCSP.VerifyHash(hash, CryptoConfig.MapNameToOID("SHA1"), signature);
return res;
}
catch
{
return false;
}
finally
{
if (rsaCSP != null)
rsaCSP = null;
}
}
C# Client Code for public key that works
public static byte[] getPublicKeyBlob()
{
RSACryptoServiceProvider rsaCspPublic = (RSACryptoServiceProvider)getCertificate()
.PublicKey.Key;
return rsaCspPublic.ExportCspBlob(false);
}
Java Client Code - In Progress (Currently gets the raw byte [] public key
public static byte[] getPublicKeyBlob(){
try
{
byte[] ba = keystore.getCertificate("le-f0b649ee-4e25-4973-a185-efd5bd587c54")
.getPublicKey().getEncoded();
return ba;
}catch(Exception e){
}
return null;
}
If anyone can assist me it would be greatly appreciated.
It seems this is the way to do it (Only small issue is that Java puts one extra 00 byte which I just strip off in the server code)
public static byte[] getPublicKey(){
try
{
RSAPublicKey key = (RSAPublicKey)keystore.getCertificate("alias").getPublicKey();
return key.getModulus().toByteArray();
}catch(Exception e){
}
return null;
}

RSA Encryption issue between C# and Java using Bouncy Castle

I'm facing a couple of issues with a Java app and another in C#. Here is the thing. There's a server application that receives request through HTTP, process it and sends
back the response. This server is writting in Java with Bouncy Castle and we use PKI to encrypt sensitive data in the request. We have many operations that the server recognizes
and one of the them is used to generate the the public and private keys used to exchange with the clients. Each client has a unique ID, so when this operation named
GetEncryptionKey is executed, it generates the private key and save it locally in the server and generates the public key which is sent back in PEM format, like this:
encryptionKey=-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAon1WDHdarN7yq0UOevzW
5PiFsSC8bEkTUOZ6X3RIth+RCU42pUj/Z8fp9T8rbWp8CqbhlFDxU4c+YucpGljC
7A10nkrPoBT0lpHEuXJiSgx+9qqsyo9q6GddhOpdMa+Z6VCfI+JCM3kdJNMH3r+o
i+WLPHLB8lxnfT2CHyZVQGhkzrH9fk1XhdenXxjtPGpwYBOsUZUwRt8EeW6JUwSI
mKXiXag0IViEcyAa2BvProkxklbQB3BczLHdXjIDwnE6u1aMA7pYPSkBtY6tuQ0F
5sNWXHsaKWON33MnbhlM7sieYDi9L4dWksala/m/mdIeHIXzX4ZCYdOhayWWKZ1N
HwIDAQAB
-----END PUBLIC KEY-----
This is working ok. The code used to generate the key is the following one:
private void getEncryptedKey() throws GWTranException {
PEMWriter pemWriter;
Security.addProvider(new BouncyCastleProvider());
KeyPair keyPair = null;
KeyPairGenerator keyPairGenerator = null;
try {
keyPairGenerator = KeyPairGenerator.getInstance("RSA");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
keyPairGenerator.initialize(2048);
keyPair = keyPairGenerator.generateKeyPair();
StringWriter writer = new StringWriter();
try {
pemWriter = new PEMWriter(new PrintWriter(writer));
pemWriter.writeObject(keyPair.getPublic());
pemWriter.flush();
String pem = writer.toString();
savePrivateKey(keyPair, pem); // save the private key locally
} catch (IOException e) {
e.printStackTrace();
}
}
Now I have a client written in C# that connects to the server, retrieves the Public Key sent in the response and use it to encrypt a string that will travel encrypted.
This is the .NET code (very simple, as I'm just testing the functionality):
private string EncryptDataWithRSA(string data) {
string cryptedData = string.Empty;
RsaKeyParameters rsaParams;
using (var reader = new StringReader(txtKey.Text)) {
rsaParams = (RsaKeyParameters)new PemReader(reader).ReadObject();
reader.Close();
}
IAsymmetricBlockCipher eng = new RsaEngine();
eng = new OaepEncoding(eng);
eng.Init(true, rsaParams);
var dataBytes = Encoding.UTF8.GetBytes(data);
var cryptedDataBytes = eng.ProcessBlock(dataBytes, 0, dataBytes.Length);
cryptedData = Convert.ToBase64String(cryptedDataBytes);
return cryptedData;
}
Everything looks nice but the problem is that when the server (Java app) tries to decrypt the data I get an exception:
javax.crypto.BadPaddingException: data hash wrong
at org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi.engineDoFinal(Unknown Source)
at javax.crypto.Cipher.doFinal(DashoA13*..)
at com.verifone.gateway.security.RSAEncryptUtil.decrypt(RSAEncryptUtil.java:127)
at com.verifone.gateway.security.RSAEncryptUtil.decrypt(RSAEncryptUtil.java:152)
at com.verifone.gateway.preonline.PreOnlineJob.decryptData(PreOnlineJob.java:1661)
at com.verifone.gateway.preonline.PreOnlineJob.extractCardData(PreOnlineJob.java:989)
at com.verifone.gateway.preonline.PreOnlineJob.run(PreOnlineJob.java:288)
at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.util.concurrent.FutureTask$Sync.innerRun(Unknown Source)
at java.util.concurrent.FutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
This is working fine with another client writting in iOS but I don't have access to the source code. I'm writting a simulator in .NET but I'm not being able to
get the information decrypted correctly. On the server side this is part of the code used to decrypt the data:
public static byte[] decrypt(byte[] text, PrivateKey key) throws Exception
{
byte[] dectyptedText = null;
try {
// decrypt the text using the private key
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPPadding");
cipher.init(Cipher.DECRYPT_MODE, key);
dectyptedText = cipher.doFinal(text);
}
catch (Exception e) {
//_log.error(e, e);
throw e;
}
return dectyptedText;
}
I'm out of ideas, I've tried everything. Do you see something I don't?
Thanks for your help.

AES encrypt in .NET and decrypt with Node.js crypto?

I'm trying to encrypt some data in Mono C#, send it to a NodeJS server and decrypt it there. I'm trying to figure out what algorithms to use to match the two.
I send the encrypted string encoded with base64. So I do something like this in Javascript, where I know the key which was used to encrypt the data in my C# application:
var decipher = crypto.createDecipher('aes192',binkey, biniv);
var dec = decipher.update(crypted,'base64','utf8');
dec += decipher.final('utf8');
console.log("dec", dec);
In Mono I create my Cypher with:
using System.Security.Cryptography;
using (Aes aesAlg = Aes.Create("aes192"))
I need to pass the correct string to Aes.Create() in order to have it use the same algorithm, but I can't find what it should be. "aes192" is not correct it seems.
I don't need aes192 this was just a tryout. Suggest a different encryption flavor if it makes sense. Security is not much of an issue.
Here are links to .NET and Nodejs docs:
http://msdn.microsoft.com/en-us/library/system.security.cryptography.aes.aspx
http://nodejs.org/api/crypto.html
This code works for my Node.js side, but please replace the static iv, otherwhise aes encryption would be useless.
var crypto = require('crypto');
function encrypt(data, key) {
key = key || new Buffer(Core.config.crypto.cryptokey, 'binary'),
cipher = crypto.createCipheriv('aes-256-cbc', key.toString('binary'), str_repeat('\0', 16));
cipher.update(data.toString(), 'utf8', 'base64');
return cipher.final('base64');
}
function decipher(data, key) {
key = key || new Buffer(Core.config.crypto.cryptokey, 'binary'),
decipher = crypto.createDecipheriv('aes-256-cbc', key.toString('binary'), str_repeat('\0', 16));
decipher.update(data, 'base64', 'utf8');
return decipher.final('utf8');
}
function str_repeat(input, multiplier) {
var y = '';
while (true) {
if (multiplier & 1) {
y += input;
}
multiplier >>= 1;
if (multiplier) {
input += input;
} else {
break;
}
}
return y;
}
I hope this helps You.
NOTE: You need to deliver an 265bit aka 32 character key for this algorithm to work.
POSSIBLE .NET SOLUTION: This may help you Example
You should simply write new AesManaged().
You don't need to call Create().
You then need to set Key and IV, then call CreateDecryptor() and put it in a CryptoStream.
It turned out to be a stupid mistake. I thought the create function in Node.js could take a variable argument count. Turns out you need to call the createDecipheriv() instead.
Just for the record, you can easily check the padding and mode by looking at those properties in the Aes object. The defaults are CBC and PKCS7. That padding is also used in nodejs crypto. So a for a 128 key size my code to decrypt a base64 encoded string would be:
var crypto = require('crypto');
var binkey = new Buffer(key, 'base64');
var biniv = new Buffer(iv, 'base64');
var decipher = crypto.createDecipheriv('aes-128-cbc', binkey, biniv);
var decrypted = decipher.update(crypted,'base64','utf8');
decrypted += decipher.final('utf8');
console.log("decrypted", decrypted);

C# HMAC to Java

I'm making the equivalent java code for the code below. But I can make something that returns the same result for encodedString. What Java class can I use for achieve the same result?
//Set the Hash method to SHA1
HMAC hash;
switch (validation)
{
case MachineKeyValidation.MD5:
hash = new HMACMD5();
break;
case MachineKeyValidation.SHA1:
default:
hash = new HMACSHA1();
break;
}
//Get the hash validation key as an array of bytes
hash.Key = HexToByte(validationKey);
//Encode the password based on the hash key and
//converts the encrypted value into a string
encodedString = Convert.ToBase64String(hash.ComputeHash(Encoding.Unicode.GetBytes(password)));
Thanks in advance!
:)
I found a solution for the translation code.
There was two main problem. When a request a HMACSHA1 I'm not talking about a SHA1 algorithm, but a HmacSHA1. And there is a difference between the encoding from Java and C#. I was using the correct key, and the correct algorithm, but the encoding was differente.
SecretKeySpec signingKey = new SecretKeySpec(key, "HmacSHA1");
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(signingKey);
// The big problem is difference between C# and Java encoding
byte[] rawHmac = mac.doFinal(data.getBytes("UTF-16LE"));
result = new String(Base64.encode(rawHmac));
See this question about computing hash functions in Java.
And look at the javadoc for java.security.MessageDigest.getInstance(String algorithm).
Edited to add:
Try running the following app to see what providers you have registered.
import java.security.Provider;
import java.security.Security;
public class SecurityTest {
public static void main(String[] args) {
Provider[] providers = Security.getProviders();
for (Provider p : providers) {
System.out.println(p.toString());
}
}
}
You should have at least a few Sun providers listed. If not, you may need to download some security libraries.

Categories

Resources