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;
}
Related
This C# code:
private static string CreateHashKey(object myString)
{
byte[] buffer = JsonSerializer.SerializeToUtf8Bytes(myString);
var cryptoTransform = MD5.Create();
return BitConverter.ToString(cryptoTransform.ComputeHash(buffer));
}
When given the string "Bangalore", produces this hashed value: "92-E7-92-78-E7-D9-37-C1-AF-AC-D7-E6-B2-CD-B6-5E".
However, I cannot reproduce this in R, playing around a bit:
library(digest)
digest::digest(serialize("Bangalore", connection = NULL),"md5")
"e85798ec7dd5003d8d464f6c5d8de5c5"
digest::digest(serialize("Bangalore", connection = NULL,ascii = TRUE),"md5")
"5377a11b9792c774ddd726361c56d8f2"
digest::digest(charToRaw('Bangalore'),"md5")
"bf19f50fed3db016cb78cdb029db3034"
digest::digest(serialize(charToRaw('Bangalore'),connection = NULL,ascii = TRUE),"md5")
"be7696c777f9418e8e853084d6ddf0ae"
library(openssl)
md5("Bangalore") # "1bc99cb2f4153c2d0d8025ee5575b2a0"
This post suggests converting to bytes would fix the problem, but it hasn't so far...
Why do I get an different hash when running with python and C# the same key and message?
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);
I tried to sign and valid my signed data using myCert.pfx file private and public key. But while signing the data I am getting " Invalid algorithm specified." exception
.Net framework we are using is 4.5 and the code is as below
public static void CallMainMethod()
{
string str = "Sign and verify the data";
X509Certificate2 certificate = LoadPrivateKey();
byte[] hashBytes = GetDataHash(str);
byte[] signature = GetDigitalSignature(hashBytes);
}
private static X509Certificate2 LoadPrivateKey()
{
return new X509Certificate2(#"d:\Keys\myCert.pfx", "Pass##123");
}
private static byte[] GetDataHash(string sampleData)
{
//choose any hash algorithm
SHA256Managed managedHash = new SHA256Managed();
return managedHash.ComputeHash(Encoding.Unicode.GetBytes(sampleData));
}
private static byte[] GetDigitalSignature(byte[] data)
{
X509Certificate2 certificate = LoadPrivateKey();
RSACryptoServiceProvider provider = (RSACryptoServiceProvider)certificate.PrivateKey;
return provider.SignHash(data, "SHA256");
}
I believe that legacy RSACryptoServiceProvider doesn't support SHA2 algorithms. Rewrite last method as follows:
private static byte[] GetDigitalSignature(byte[] data)
{
X509Certificate2 certificate = LoadPrivateKey();
RSA provider = certificate.GetRSAPrivateKey();
return provider.SignHash(data, "SHA256", RSASignaturePadding.Pkcs1);
}
This style is preferred as of .NET Framework 4.6 and above (#bartonjs, please correct me if I'm wrong in regards to .NET version).
While #Crypt32 gave the best answer (upgrade to .NET Framework 4.6 or better and use GetRSAPrivateKey() and the better version of SignData -- it's been out over 4 years at this point), if you really need to stay on net45, your easiest answer is to open the PFX with X509KeyStorageFlags.Exportable and copy the key into a better provider.
return new X509Certificate2(#"d:\Keys\myCert.pfx", "Pass##123", X509KeyStorageFlags.Exportable);
...
RSA legacyProv = (RSA)certificate.PrivateKey;
RSACryptoServiceProvider provider = new RSACryptoServiceProvider();
provider.ImportParameters(legacyProv.ExportParameters(true));
return provider.SignHash(data, "SHA256");
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
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.