I have public key generated using java with algorithm RSA and able to reconstruct using following code:
X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(arrBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
publicKey = keyFactory.generatePublic(pubKeySpec);
QUESTION
How to construct PublicKey on dotnet side using csharp?
sample public key would be:, In above code i pass data contained in element encoded
<sun.security.rsa.RSAPublicKeyImpl resolves-to="java.security.KeyRep">
<type>PUBLIC</type>
<algorithm>RSA</algorithm>
<format>X.509</format>
<encoded>MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMf54mcK3EYJn9tT9BhRoTX+8AkqojIyeSfog9ncYEye
0VXyBULGg2lAQsDRt8lZsvPioORZW7eB6IKawshoWUsCAwEAAQ==</encoded>
</sun.security.rsa.RSAPublicKeyImpl>
Unfortunately, C# doesn't provide any simple way to do this. But this will correctly decode an x509 public key (make sure to Base64 decode the x509key parameter first):
public static RSACryptoServiceProvider DecodeX509PublicKey(byte[] x509key)
{
byte[] SeqOID = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 };
MemoryStream ms = new MemoryStream(x509key);
BinaryReader reader = new BinaryReader(ms);
if (reader.ReadByte() == 0x30)
ReadASNLength(reader); //skip the size
else
return null;
int identifierSize = 0; //total length of Object Identifier section
if (reader.ReadByte() == 0x30)
identifierSize = ReadASNLength(reader);
else
return null;
if (reader.ReadByte() == 0x06) //is the next element an object identifier?
{
int oidLength = ReadASNLength(reader);
byte[] oidBytes = new byte[oidLength];
reader.Read(oidBytes, 0, oidBytes.Length);
if (oidBytes.SequenceEqual(SeqOID) == false) //is the object identifier rsaEncryption PKCS#1?
return null;
int remainingBytes = identifierSize - 2 - oidBytes.Length;
reader.ReadBytes(remainingBytes);
}
if (reader.ReadByte() == 0x03) //is the next element a bit string?
{
ReadASNLength(reader); //skip the size
reader.ReadByte(); //skip unused bits indicator
if (reader.ReadByte() == 0x30)
{
ReadASNLength(reader); //skip the size
if (reader.ReadByte() == 0x02) //is it an integer?
{
int modulusSize = ReadASNLength(reader);
byte[] modulus = new byte[modulusSize];
reader.Read(modulus, 0, modulus.Length);
if (modulus[0] == 0x00) //strip off the first byte if it's 0
{
byte[] tempModulus = new byte[modulus.Length - 1];
Array.Copy(modulus, 1, tempModulus, 0, modulus.Length - 1);
modulus = tempModulus;
}
if (reader.ReadByte() == 0x02) //is it an integer?
{
int exponentSize = ReadASNLength(reader);
byte[] exponent = new byte[exponentSize];
reader.Read(exponent, 0, exponent.Length);
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSAParameters RSAKeyInfo = new RSAParameters();
RSAKeyInfo.Modulus = modulus;
RSAKeyInfo.Exponent = exponent;
RSA.ImportParameters(RSAKeyInfo);
return RSA;
}
}
}
}
return null;
}
public static int ReadASNLength(BinaryReader reader)
{
//Note: this method only reads lengths up to 4 bytes long as
//this is satisfactory for the majority of situations.
int length = reader.ReadByte();
if ((length & 0x00000080) == 0x00000080) //is the length greater than 1 byte
{
int count = length & 0x0000000f;
byte[] lengthBytes = new byte[4];
reader.Read(lengthBytes, 4 - count, count);
Array.Reverse(lengthBytes); //
length = BitConverter.ToInt32(lengthBytes, 0);
}
return length;
}
The above code is based off of this question (which only worked for certain key sizes). The above code will work for pretty much any RSA key size though, and has been tested with the key you provided as well as 2048-bit and 4096-bit keys.
An alternative solution would be to generate a certificate using a tool (XCA is a good one), export the cert to a p12 (PKCS12) file and then load the cert in both Java and C# to get at the keys.
In C# you can load a PKCS12 file using the X509Certificate2 class.
X509Certificate2 cert = new X509Certificate2(certificateFile, certificatePassword, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet);
RSACryptoServiceProvider provider1 = (RSACryptoServiceProvider)cert.PublicKey.Key;
RSACryptoServiceProvider provider2 = (RSACryptoServiceProvider)cert.PrivateKey;
In Java you can load a PKCS12 file using the KeyStore class.
KeyStore keystore = KeyStore.getInstance("PKCS12");
keystore.load(new FileInputStream(certificateFile), certificatePassword.toCharArray());
Key key = keystore.getKey(certName, certificatePassword.toCharArray());
Certificate cert = keystore.getCertificate(certName);
PublicKey publicKey = cert.getPublicKey();
KeyPair keys = new KeyPair(publicKey, (PrivateKey) key);
In Java, cast publicKey from PublicKey to RSAPublicKey.
That has getModulus and getExponent will get you BigIntegers, from which you use toByteArray to get the bytes.
I don't know Java keeps leading 0s in the BigInteger class, so check if you have to strip leading null (0x00) bytes from the public exponent.
Encode the byte arrays into base 64 using either the Apache Commons Codec or Java 8's Base64 Encoder.
You may need to check byte order (maybe reverse the modulus, not sure).
Serialize these by constructing this XML: "<RSAKeyValue><Modulus>{your base 64 encoded public modulus here}</Modulus><Exponent>{your base 64 encoded public exponent here}</Exponent></RSAKeyValue>".
In CSharp:
var rsaCsp = new RSACryptoServiceProvider(o.BitLength);
rsaCsp.FromXmlString(xmlRsaKeyValue);
Now you have an RSA CSP loaded with your public key.
The same process can be extended to load a private key by adding P, Q, DP, DQ and InverseQ XML elements.
Hello you can try this way also,
private static string[] GenerateXMLPrivatePublicKeys(){
string[] keys = new string[2];
RSA rsa = new RSACryptoServiceProvider(2048);
string publicKey = rsa.ToXmlString(false);
string privateKey = rsa.ToXmlString(true);
keys[0] = publicKey;
keys[1] = privateKey;
return keys;
}
Related
So here is a question.
I have an old code on a Windows system that just takes a short string and makes CryptEncrypt on it with a public RSA key.
The minimum working example is here (avoiding any checks to make it shorter. avoiding freeing as well)
std::string testData = "12345678";
HCRYPTPROV context;
CryptAcquireContext(&context, nullptr, nullptr, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT);
auto pubK = FromBase64(PublicKey);
CERT_PUBLIC_KEY_INFO *pubKeyInfo = nullptr;
DWORD keyLength = 0;
CryptDecodeObjectEx(X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO,
pubK.data(), pubK.size(),
CRYPT_ENCODE_ALLOC_FLAG, nullptr,
&pubKeyInfo, &keyLength);
HCRYPTKEY key;
CryptImportPublicKeyInfo(context, X509_ASN_ENCODING, pubKeyInfo, &key);
std::vector<std::uint8_t> result(testData.begin(), testData.end());
DWORD size = testData.size();
CryptEncrypt(key, NULL, true, 0, result.data(), &size, result.size());
result.resize(size);
size = testData.size();
CryptEncrypt(key, NULL, true, 0, result.data(), &size, result.size());
std::cout << ToBase64(result) << "\n";
The code works and returns a base64 encoded string. like lTq01sOcgDcgwtDaFFoHH/Qb6xLw0KU+/n/+3t0eEb8l4N69QGcaEWf1qso3a4qn7Y8StlXcfMe8uspNF/KDj6qQOMvCuM+uUl+tkLd5NXiESsjycgjyxAqdCIO71iTSmsYVcsS3fY/gtIbO4UAFnCRPOXoSyqWqpXW7IRtFzL2N3MxgIBlIMErNvNWs5HPA7xAY/XO6UpSMWsQO4ppccdeNLSZDPwOxohKD/BX5oDin81nFn7fvIZgghfH5knF1nezK8IGKl+vtbgrwlUUULp/wJ4POceyIn0HaZoVsaCu6xFJcUJGfBqSvm4GZqkp2MlGxBODku0OSgEfIDEGMTg==.
Then I have to decrypt this string with the private key on another side running java.
I use bouncycastle:
try {
Security.addProvider(new BouncyCastleProvider());
String value = "";
AsymmetricKeyParameter privateKey =
(AsymmetricKeyParameter) PrivateKeyFactory.createKey(Base64.getDecoder().decode(privateKeyValue));
AsymmetricBlockCipher e = new RSAEngine();
e = new org.bouncycastle.crypto.encodings.PKCS1Encoding(e);
e.init(false, privateKey);
byte[] messageBytes = Base64.getDecoder().decode(inputdata);
byte[] hexEncodedCipher = e.processBlock(messageBytes, 0, messageBytes.length);
value = new String(hexEncodedCipher);
System.out.println(value);
return value;
}
catch (Exception e) {
System.out.println(e);
}
And this code shows me the next error:
org.bouncycastle.crypto.DataLengthException: input too large for RSA cipher.
And I believe I'm missing something on the windows side, because if I use the same keys on the Java side and crypt the same data with the same public key, the decryption works as expected.
Here the keys, I generated with openssl for this question (RSA 2048):
Private:
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCeL7K++3+vl52Q
WFC6ThgvNPlhHLlP5ylIMi/7Gy60wcCHtx8R5Hzi2j7Kx/uBXyr7SCbePS7NtqHx
meVK3VhEvYHz2uYaUNf6GqJgNNjfRymnL5ll8K8lq1wym6A7KZ+L3qLHH1oI6GfW
+uf32nUfy1PQvsatPN7aiJ4ymiaJj2LcI6Bp78CINsu56Cx9QBN9uoXqKO/7NjOz
y2GRdYdckiqCkcmGDzA4/5tJROxj21gUbxwoUR+yz2sVFGlcJycsDDJiIXiPjbVN
XgM/YfmRjdAPQQwXXj9AK5w/dp9TPq621WqYES74rIY05cba4v1kihmFbs0DHLrV
fQZB0Pu9AgMBAAECggEAGuD//nO9vpiErYJUNVQPx/W4akf3NRySZzIf9QspZI2H
qYf0P5YTonhzMwHIOrNxGkGoWRsMWOgvnF4KGC6EUSniaw1HDDGwgU8FSFOyhj4R
VddAuZGsMTps8CyBjYwFED9EaZFqOxlCi8UWpYb5X+2s0EuadtVhCMEuIGsRIU53
mfW11182YtbAI7Zqi7wg/w0yRz5rVj4ph8nbSFPgi6qtVWA9bxVxNeRTXyQDWjUU
NFQzGcRG6D/SYTnRzTndVM5TmTwEVt0hvJNA2/pMWF5XMu6B2exOpJIVVVdQ40l/
XHh33SU8+gQYY56rHzFebCa0Nuxwdk0paluv+x3CAQKBgQDKwe8QXfNlMDqrM06n
iq/NwKE7tB+1gs1nKr4qZdbI71IzgQJgIJcxbJwbuE6z6Yk8PoancEELH9mSY7EN
YGeSO3QsO1LF1vyJWgWQU1G3pfC9sh8sY6f9WYg8+JogIeJsgjwf7PXrGaBq8++Q
GBMUATzPAh/ERIqUip0nEQ2IJwKBgQDHuYgPvv77qhLc+ZNOdhaZtibeVsFx50V+
a+qR+INSTJ/CChNkoVtMg803ZQWckJBOYL/TS2Mw+ctNomUg+ImjULGT9GzqMeme
HkPpAj5pNyNVpRfVNZfmS2cwwuRyE1QbFGs3C4p7D5MKKCfl4/8MBQXtJGWrQZFr
owh2NNyHewKBgHsMmSYorlcBnwlZOOnK7AiFWBRgq0G/4SI0OXaHmYMWYp+pMqTe
AoPXMyJLh0/+ce/izlt9b6vtp2AFKmVA1XpUpJtXYVN5tocw3+GH/zbh+SlWmT6a
OFAz7s953CeWCNDrdMu3RkNoqQdfhUrAoYtpeNr0ogy9wBCH0vnrinfPAoGBALNy
U/hpz+lH1qjqKFsPqKC001ljM21msL60sU4zrbHNHKEXsnLwsvodVc3Wm2MfVDjH
nrJ2gomnde2r4hbsl6W/w70+mHkXHWKuqK97D54zJzE1IyOygmctCmr6QIzqJuAp
yWbsnKCSzrcKe0aHQkmHXdrCoAJt5/2AvwKN3jJvAoGAIaLts1F7shIZ365FXyWD
poOFtXsNRXESkcYpJ2Pn+K0fUNtdH4BtR/5Z5bsEWXBf2LbL7s15UZFZbck0KH3K
22BRgPQze7wEhMVFlIMNhF17WrOh2NTGUAVz2CYSthbYh0QSI+5XJk0AEfiQcqYk
+E4bZw/RUTY94V+ITEK6F8g=
public
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAni+yvvt/r5edkFhQuk4Y
LzT5YRy5T+cpSDIv+xsutMHAh7cfEeR84to+ysf7gV8q+0gm3j0uzbah8ZnlSt1Y
RL2B89rmGlDX+hqiYDTY30cppy+ZZfCvJatcMpugOymfi96ixx9aCOhn1vrn99p1
H8tT0L7GrTze2oieMpomiY9i3COgae/AiDbLuegsfUATfbqF6ijv+zYzs8thkXWH
XJIqgpHJhg8wOP+bSUTsY9tYFG8cKFEfss9rFRRpXCcnLAwyYiF4j421TV4DP2H5
kY3QD0EMF14/QCucP3afUz6uttVqmBEu+KyGNOXG2uL9ZIoZhW7NAxy61X0GQdD7
vQIDAQAB
What do I wrong?
CryptEncrypt returns the ciphertext in little-endian format, see CryptEncrypt( Remarks, last section). For the decryption in Java the byte array messageBytes must therefore be inverted, e.g. here.
I am given a Rijndael .Net encrypted file and .Net RSA XML Key and asked to decrypt it in Java.
The key provided to me is 256 bit.
I have parsed the RSA XML file and generated the public Key in Java. I tried to decrypt using the generated key, however I am getting the exception Illegal Key Size, I think I am doing something wrong in my Java code.
Can any one please help to check if anything is wrong with my code?
.Net encryption code:
public static void EncryptFile(string fileIn, string fileOut,
string publicKeyName, string publicKeyFile)
{
try
{
// Read the public key from key file
StreamReader sr = new StreamReader(publicKeyFile);
string strKeyText = sr.ReadToEnd();
sr.Close();
//Initialize Key container and Crypto service provider
RSACryptoServiceProvider rsa;
CspParameters cspp = new CspParameters();
cspp.KeyContainerName = publicKeyName;
rsa = new RSACryptoServiceProvider(cspp);
rsa.FromXmlString(strKeyText);
rsa.PersistKeyInCsp = true;
// Create instance of Rijndael for
// symetric encryption of the data.
RijndaelManaged alg = new RijndaelManaged();
// Key size is set to 256 for strong encryption
alg.KeySize = 256;
alg.BlockSize = 256;
// Cipher Mode is set to CBC to process the file in chunks
alg.Mode = CipherMode.CBC;
// Set padding mode to process the last block of the file
alg.Padding = PaddingMode.ISO10126;
ICryptoTransform transform = alg.CreateEncryptor();
// Use RSACryptoServiceProvider to
// enrypt the Rijndael key.
byte[] KeyEncrypted = rsa.Encrypt(alg.Key, false);
// Create byte arrays to contain
// the length values of the key and IV.
int intKeyLength = KeyEncrypted.Length;
byte[] LenK = BitConverter.GetBytes(intKeyLength);
int intIVLength = alg.IV.Length;
byte[] LenIV = BitConverter.GetBytes(intIVLength);
using (FileStream fsOut = new FileStream(fileOut, FileMode.Create))
{
// Write the following to the FileStream
// for the encrypted file (fsOut):
// - length of the key
// - length of the IV
// - ecrypted key
// - the IV
// - the encrypted cipher content
fsOut.Write(LenK, 0, 4);
fsOut.Write(LenIV, 0, 4);
fsOut.Write(KeyEncrypted, 0, intKeyLength);
fsOut.Write(alg.IV, 0, intIVLength);
// Now write the cipher text using
// a CryptoStream for encrypting.
using (CryptoStream cs = new CryptoStream(fsOut, transform, CryptoStreamMode.Write))
{
// intBlockSizeBytes can be any arbitrary size.
int intBlockSizeBytes = alg.BlockSize / 8;
byte[] DataBytes = new byte[intBlockSizeBytes];
int intBytesRead = 0;
using (FileStream fsIn = new FileStream(fileIn, FileMode.Open))
{
// By encrypting a chunk at
// a time, you can save memory
// and accommodate large files.
int intCount;
int intOffset = 0;
do
{
// if last block size is less than encryption chunk size
// use the last block size and padding character is used
// for remaining bytes
if (intBlockSizeBytes > (fsIn.Length - fsIn.Position))
{
intBlockSizeBytes = ((int)(fsIn.Length - fsIn.Position));
DataBytes = new byte[intBlockSizeBytes];
}
// read data bytes
intCount = fsIn.Read(DataBytes, 0, intBlockSizeBytes);
intOffset += intCount;
// write it into crypto stream
cs.Write(DataBytes, 0, intCount);
intBytesRead += intBlockSizeBytes;
} while (intCount > 0);
// close input file
fsIn.Close();
}
// close crypto stream
cs.FlushFinalBlock();
cs.Close();
}
// close output file
fsOut.Close();
}
}
catch
{
throw;
}
}
Java Code that I wrote to decrypt it:
byte[] expBytes = Base64.decodeBase64(pkey.getExponentEle().trim());
byte[] modBytes = Base64.decodeBase64(pkey.getModulusEle().trim());
byte[] dBytes = Base64.decodeBase64(pkey.getdEle().trim());
BigInteger modules = new BigInteger(1, modBytes);
BigInteger exponent = new BigInteger(1, expBytes);
BigInteger d = new BigInteger(1, dBytes);
KeyFactory factory = KeyFactory.getInstance("RSA");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
RSAPublicKeySpec pubSpec = new RSAPublicKeySpec(modules, exponent);
PublicKey pubKey = factory.generatePublic(pubSpec);
final byte[] keyData = Arrays.copyOf(pubKey.getEncoded(), 256
/ Byte.SIZE);
final byte[] ivBytes = Arrays.copyOf(keyData, cipher.getBlockSize());
AlgorithmParameterSpec paramSpec = new IvParameterSpec(ivBytes);
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyData, "AES"), paramSpec);
byte[] decrypted = cipher.doFinal(encrypted);
System.out.println("decrypted: " + new String(decrypted));
If I change cipher initialization to cipher.init(Cipher.DECRYPT_MODE, pubKey);, then I am getting the error Invalid AES key length: 162 bytes
You are using the public key wrong way. Do you really understad how the C# program works? what parameters is it using?
You are just using the public key bits as the AES key (even I don't realy understand how do you get 162 bytes from it).
This is example of "hybrid encryption" - the data themselves are encrypted by a random AES key (in this you claim it's 256 bit) and the AES key (in this case the IV too) is encrypted by the RSA public key. In Java there are many examples how to do that.
Even to decrypt the AES key you should know parameters used to encrypt it (RSA/ECB/PKCS5Padding, RSA-AOEP, ...), though it should be inside the XML.
Comming to the parameters - you are using PKCS5Padding, but check the .NET code, it's different
I'm trying to get the equivalent of the following C# method :
public byte[] SignHash(byte[] btHash, string SN)
{
string strSignature = string.Empty;
X509Store x509store = null;
x509store = new X509Store(StoreLocation.CurrentUser);
x509store.Open(OpenFlags.ReadOnly);
foreach (X509Certificate2 x509 in x509store.Certificates)
{
if (x509.SerialNumber.ToLower().Contains(SN.ToLower()))
{
byte[] btSignature = null;
using (RSACryptoServiceProvider key = new RSACryptoServiceProvider())
{
key.FromXmlString(x509.PrivateKey.ToXmlString(true));
return key.SignHash(btHash, CryptoConfig.MapNameToOID("SHA256"));
}
break;
}
}
return null;
}
In Java language. Actually, I've come to this :
private static String SignHash(final byte[] btHash, String SerialNumber) throws Exception
{
KeyStore ks = null;
ks = KeyStore.getInstance("Windows-MY");
ks.load(null, null);
Boolean noValidCertificate = true;
Enumeration<String> en = ks.aliases();
ArrayList<String> lstAlias = Collections.list(en);
lstErreurs.add(lstAlias.size() + " certificate(s) found");
for (String aliasKey : lstAlias)
{
X509Certificate cert = (X509Certificate) ks.getCertificate(aliasKey);
Certificat = Base64Coder.encodeBase64String(cert.getEncoded());
Boolean blnCertificateFound = false;
if (SerialNumber != null && !SerialNumber.equals(""))
{
String SerialNumberCert = cert.getSerialNumber().toString(16);
if (SerialNumber.toLowerCase().contains(SerialNumberCert.toLowerCase())
|| SerialNumberCert.toLowerCase().contains(SerialNumber.toLowerCase()))
{
blnCertificateFound = true;
}
}
if (blnCertificateFound == false)
{
continue;
}
Provider p = ks.getProvider();
boolean isHashToSign = false;
for (String strToSign : input.split(";")) {
if(strToSign.length() == 44 && General.isBase64(strToSign)) {
isHashToSign = true;
break;
}
}
String algorithm = "";
if(isHashToSign)
{
algorithm = "RSA";
} else {
algorithm = "SHA256withRSA";
}
Signature sig = Signature.getInstance(algorithm, p);
PrivateKey key = (PrivateKey) ks.getKey(aliasKey, "1234".toCharArray());
if (key != null)
{
noValidCertificate = false;
sig.initSign(key);
String[] TabToSign = input.split(";");
String strResultSignature = "";
String separator = "";
for (String strToSign : TabToSign)
{
byte[] btToSign = null;
if(isHashToSign) {
btToSign = General.Base64_Decode_To_ByteArray(strToSign.getBytes(Charset.forName("UTF-8")));
} else {
btToSign = strToSign.getBytes(Charset.forName("UTF-8"));
}
sig.update(btToSign);
byte[] res = sig.sign();
String resB64 = Base64Coder.encodeBase64String(res);
strResultSignature += separator + resB64;
separator = ";";
}
return strResultSignature;
}
}
return null;
}
But getting the algorithm "RSA" does not work for signature. I finally sign the Hash of a Hash in Java. I would like to sign a SHA256 byte array hash without hashing it again. How can I come to this result ? (for information, I'm using the Windows Certificate Store, so I have to work with Sun MSCAPI provider).
EDIT 1 :
I tried with the algorithm "NONEwithRSA" but the signature result is different from the signature in .NET using SignHash method.
EDIT 2 :
The following thread : Difference between SHA256withRSA and SHA256 then RSA explains it is actually possible to sign a hash, but that method requires BouncyCastle.
I can not operate with BouncyCastle because I need to use sun MSCAPI provider (Windows Certificate Store). I have to find an alternative to BouncyCastle (unless BC allows us to use the Sun MSCAPI provider).
(The answer was completely rewritten. Some less interesting thoughts and snippets can be found in the previous revisions)
A call to SignHash(btHash, CryptoConfig.MapNameToOID("SHA256")) does a PKCS#1 v1.5 signature (RSASSA-PKCS1-v1_5), e.g.:
byte[] btHash = new byte[] { 0x57, 0x91, 0x16, 0xB6, 0x3E, 0x06, 0x58, 0x83, 0x24, 0x8C, 0x07, 0x16, 0xDA, 0x6A, 0x03, 0x4D, 0x23, 0x37, 0x0B, 0x32, 0x1C, 0xA0, 0x80, 0x08, 0x1F, 0x42, 0x03, 0x81, 0x8E, 0x54, 0x3A, 0xC6 };
X509Certificate2 cert = new X509Certificate2("dummy.p12", "1234", X509KeyStorageFlags.Exportable);
using (RSACryptoServiceProvider key = new RSACryptoServiceProvider())
{
key.FromXmlString(cert.PrivateKey.ToXmlString(true));
byte[] ret = key.SignHash(btHash, CryptoConfig.MapNameToOID("SHA256"));
}
Gives a signature:
0A5003E549C4E4310F720076A5A4D785B165C4FE352110F6CA9877EB9F364D0C40B0197110304D6F92E8BD40DFD38DB91F356601CDD2CD34129BC54492C2D7F371D431150288A95C21E47533F01A9FA4977439FF9594C703380BEDF49A47A7B060ECAC26AEB53B8732D93E18FAD3B2D5889B3311C1B0D4F9F6B318169BDEB143D771DEFB56BAFE49B2B59F172757D4273EF369AFCB32490EC954E17599BD66D4E3BDB345B860748DB0C3B5A272ECFA546E65F2D4C87870CC62D91680AB71DB52DE618C006356258A941E8F36A5DCC7A06BA6DACAC3DC35F482B168107B4D7DA6C19A56FEDC247232DD7210CA9DB7273AA9AE6A90A8A4DFEB64BA0FBC830AB922
Which contains a PKCS#1 v1.5 padded DigestInfo and hash (when decrypted using the public key):
0001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF003031300D060960864801650304020105000420579116B63E065883248C0716DA6A034D23370B321CA080081F4203818E543AC6
As you have only the hash (and not the data) to be signed you need to use the NONEwithRSA algorithm in java (which should perform a PKCS#1 v1.5 padded signature of the input data without any hashing) and generate the correct input DigestInfo with the hash OID manually. Like that (with the help of Apache Commons Lang)::
byte[] btHash = new byte[] { ....the same.... };
KeyStore keystore = KeyStore.getInstance("PKCS12");
keystore.load(new FileInputStream("dummy.p12"), "1234".toCharArray());
PrivateKey privKey = (PrivateKey)keystore.getKey("Dummy", "1234".toCharArray());
byte[] asn=ArrayUtils.addAll(new byte[] { (byte)0x30, (byte)0x31, (byte)0x30, (byte)0x0d, (byte)0x06, (byte)0x09, (byte)0x60, (byte)0x86, (byte)0x48, (byte)0x01, (byte)0x65, (byte)0x03, (byte)0x04, (byte)0x02, (byte)0x01, (byte)0x05, (byte)0x00, (byte)0x04, (byte)0x20}, btHash);
Signature signature = Signature.getInstance("NONEwithRSA");
signature.initSign(privKey);
signature.update(asn);
byte[] ret = signature.sign();
Which gives the same signature as the C# code (using the default SunJCE/SunRsaSign providers).
The SunMSCAPI provider supports the NONEWithRSA algorithm with a restriction. Citing sun.security.mscapi.RSASignature javadoc:
NOTE: NONEwithRSA must be supplied with a pre-computed message digest.
Only the following digest algorithms are supported: MD5, SHA-1,
SHA-256, SHA-384, SHA-512 and a special-purpose digest algorithm which
is a concatenation of SHA-1 and MD5 digests.
Which at first sight might work for this scenario. Unfortunately:
Signature mscapiSignatureNoneWithRSA = Signature.getInstance("NONEwithRSA", "SunMSCAPI");
mscapiSignatureNoneWithRSA.initSign(mscapiPrivKey);
mscapiSignatureNoneWithRSA.update(btHash);
byte[] mscapiSignatureNoneWithRSA_btHash = mscapiSignatureNoneWithRSA.sign();
Gives a different signature:
CE26A9F84A85037856D8F910141CE7F68D6CAAB416E5C2D48ACD9677BBACCB46B41500452A79018A22AB1CA866DD878A76B040A343C1BABCDB683AFA8CE1A6CCCA48E3120521E8A7E4F8B62B453565E6A6DC08273084C0748C337724A84929630DC79E2EB1F45F5EEBA2148EC0CA5178F2A232A2AE8A5D22BB06C508659051CD1F5A36951B60F6322C5AEB5D4461FABE4D84E28766501A1583EC2A5D8553C163EC8DB9E80EF972233516BEC50AAC38E54C8B5E3B3DEAE90F37A1D230CD976ABEEE97A4601461E842F0F548DA7A74645AAB42586044B084E6A9F3EFE409EE12D98EB0B16CDC2C0AB75BF5DC9B52EBB7E3303C53A2B1BDF14000B99112B1871A6A
Which contains only a PKCS#1 v1.5 padded value of the hash (there is no ASN.1 DigestInfo sequence /which is wrong in this case/):
0001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00579116B63E065883248C0716DA6A034D23370B321CA080081F4203818E543AC6
Trying to sign the DigestInfo from the SunJCE example gives an exception:
java.security.SignatureException: Message digest is too long
(Here and here are the reasons for this behavior.)
An alternative way to generate the signature using RSA private key encryption which gives the same signature as the C# code with SunJCE provider (using the same asn variable as above):
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, privKey);
byte[] ret = cipher.doFinal(asn);
Does not work with the SunMSCAPI provider:
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "SunMSCAPI");
cipher.init(Cipher.ENCRYPT_MODE, mscapiPrivKey);
byte[] ret = cipher.doFinal(asn1);
As it gives:
4A540DFAD44EBDAE89BF5DD52121DA831E7C394E0586DC9EAEF949C466E649979E19DF81A44801EBD80B8946605B4EFCED53011A844A3A4E023136F0CDEAA57EAAB1EA1FA75400B3B2D5FAB3955BEB13A178AC03DED6AACA0571412B74BCE30E772082A368B58E94D8E20D8F2A116BA5B3881824A014281E9F04BD687C087ACF7164CAF7C74274BA356A671ADA2BB4142504DB2883AFEDA563C6E590BC962725D6697402AB24391409F50D7D16B8BF64A1C0224C379EF9C7B8E493BE889A70674C3AEEC524366DBF9DE36FEE01F186FC00DE2F06096C46CC873D37E194CB217FBFCCF450C1F96C804022E25E1589DF67247927AAD59C66294B027DD5EE991D46
Which decrypted using the public key gives a nonsense:
3A9E0F985D1CF2CFDB45F201A68EF0F241ADBAED2D945FD36451CB4BE77D9B30D977004F95E6EDC208805E62870CD19D87C5E7F4E4C1AC2546F7F9C410299C9D203C47C2B547BAA55DA05C44DACB7E07C3F0DB99AE291E48A67EE089F8DA34EB1AABE352A7F94B082CFB167C0FE90761B79FCE238A0F3D0D917CA51220EEA9A43877703FC06CDC1F13A77CA9904E3660F7AD84DE0C34C877B737C20B1A117E60D69E6766054A30B95E6E88CF2C11AEE5CE30F2DD780C0334BE085302E73E0E5BB096893B7155E7A48CA16DD5EA9FC6F63090F7223E7FBAAA133BDFDA7251F412E395F4D8A462569BC5DCC34C2DF7D996BB3278C3F6B0E1EE9E729925937C8BAE
But (more interestingly) contains a valid PKCS#1 v1.5 padded encrypted plaintext when decrypted with the private key:
000211F7946FAD6BDB18830F8DD1238FD7EFCCFF041D55B88FBABDAAA6B06A5E9FD7556EB33678D9954D26E07B2FCE6D7304386DBDFC352C9932E2BA1794A3A0E0F6D78AA656DEB36CC483171A77ABF34408F4BF60661ECA79852B8E39C1A710976208FFBF6BE0DFB566149E6C5838762316F394B70BDF6D494F8C43C42CB6E527292DEF9204712CB24AC82C572BBC0E70A298D5FB050A27B54AFFA1332EEF37A14E65D379968BCE717BEC37C67A180DE943AAF2FE83560D33BC588E11B85D1C3391CCB13E4A80F57166BAC9003031300D060960864801650304020105000420579116B63E065883248C0716DA6A034D23370B321CA080081F4203818E543AC6
Which means that although given a private key for the encryption operation the SunMSCAPI uses the public key part (I did not dig into the implementation details to find the reason for this behavior).
So (AFAIK) the SunMSCAPI provider can not be directly used in your scenario...
(Please note that you will get a different result for each encryption run as the PKCS#1 v1.5 encryption padding contains a random data)
Fortunately there are some alternative choices:
[A] Abuse the SunMSCAPI internal API to perform the signature like that (again with the help of Apache Commons Lang):
// Obtain the handles
long hCryptKey = (Long)MethodUtils.invokeMethod(mscapiPrivKey, "getHCryptKey");
long hCryptProvider = (Long)MethodUtils.invokeMethod(mscapiPrivKey, "getHCryptProvider");
// Call the internal native method
Class<?> internalClass = Class.forName("sun.security.mscapi.RSASignature");
Method internalSignHashMethod = internalClass.getDeclaredMethod("signHash", boolean.class, byte[].class, int.class, String.class, long.class, long.class);
internalSignHashMethod.setAccessible(true);
byte[] res = (byte[])internalSignHashMethod.invoke(internalClass, false, btHash, btHash.length, "SHA-256", hCryptProvider, hCryptKey);
ArrayUtils.reverse(res); // Make it big endian
Which gives the same result as the C# code.
But is strongly dependand on the underlying SunMSCAPI implementation which can change at any moment
[B] Use JNI/JNA and call the winapi functions directly
Which is a cleaner approach as it depends on a public API
I have found this project, but have not given it a try
Good luck!
Appendix: RSA Private key used in the examples:
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDoZFvkEpdzXwSw
9g6cDxg9n/khCjLIO7E8VQFzu80C0iR0C6K05SHvTFEdssmzZmdCQi092ReSJRPH
yAOQUnlcMuCpi0m62ufZ4yNkZX5sH3fjHkP1FMv5CPtJOIArGFCMS4CufXu2XkXh
dbJuCLPJsUuiRsaoRg0Q6a8QVqWAR1oyVojTNFqzZWTLD46lQQIvINOrIeYvKklU
FUNcmq8PyArwEvxaDeiop4gVyizx7n7v213FjAXMfEG920O4DlnKjObdi1+PhejT
1RUxRUipTmAI2d3JmACpYH6+Il8Ck61wmKQ9IjoTstNeRfKGEkxH9RKP2P4ko5w9
8YfToVDXAgMBAAECggEAI5vNIMNghYMXuu3ZCzyc4ER07gUcBuZun+n+kPdD0JzW
jRmDUuiRLJOrEjvlACI+zD5LpGBxZilcQI57TU/13JTHK/N11rXYNODC+Y07s+GW
gyyOCS2om34u0udfbDsLjJO9If+ER0tmtcdNEeMveUY7aqAhrIMfWWoVMxGzxlXd
0kHWl4blnisjc01xCG4WcXVItyA6F8WZ3kL+BTeR5/3IwM72r9k7fcBkJZPLJZff
oZ2W+whGa9UXAkt6DQ6PlWMvt+AVcu84f8k/4FRRUNbY1OslO7zHbEc1K5qibpjb
6Tlfg2G+bux/1oCJ59bdyRP7FQMmgjLx49H17mwcgQKBgQD1j4HRtwSPnS+INPS4
nVpfEtb+wXGeDLCMAgdesgXYfr5JWJKJCKW65/j2TkJ/xoN8L1z8TeE6o6Q3ZHJ9
QtcM1ifCpNCnjjalbkm9BG4bCPy+5MUIuS5oRtJjwb7mPTxzpq4DIj3G2ooY5F2i
9Nyqde3bEvWn910yqQgI6CjOtwKBgQDyRYkU46gKLhp98gJ0zdm3KNZ/TBE5zsO6
rDnwFPLGxanVNMzkIQX/LXAQOaNK1WD8fmKG+iixDVLyJYtVtuvTQLOHkOOTXw44
QY4BGh+wbS0HrfKd7Qcpt/3HTCKq9eW33+jggyBc+fa+LDIGpdbO16SBCP3Cb7k6
9gtBN5du4QKBgQCKriVO3uGAifESJ3Yd3R/wmZ85+N3FuLzsFSk8XaXXgpzMp2z6
XxvZ1rBPyhrcNqyDMex9wS32A/z2G5BdFaaF5VxHHPWJ61MJUqPqT9ovAoBa/rAY
IR0IXxbqp7y8ItFFL1kPBAOHjlx3emE3arpEup0+IBMEbTsBJV0YSqThOQKBgFRf
syX7QwKIm+FQ71oOdsw7BLjAnR8syy2v3V2nbgWbwVHnWZP5jEUaZfTAngXp2iUV
PusTJCjFIyYBvUzUr7yaw+tqolcou6ML8ZCgsHiZDR2njt9BNUVqNo+6DDjN+nrX
GBtYj2TSCQSiD6oRB4Zxw3DM2NNmZXQLTFAiNDMBAoGBAJOu4+nVB8iUE3JBsrM5
WkwBivqTyo9pusxwEs+GHnkVFsFubFKHda04220JMRVevOuf48DPgvlW6UCTojyr
85dFon9tV0qyi9Ehc0OdXqOjmx0y+wdR6ZqG+x+e6JGiYeReIa4XBrq0cLHlzrNY
8UwL0QLJpuaQZEbqhyOGMNKE
-----END PRIVATE KEY-----
I'm developing an android app and getting this public key from the server of the company that I work with:
PFJTQUtleVZhbHVlPjxNb2R1bHVzPnZOcFhkRWVOTU5pZDhuOTlUekRGMVo4MDNvTEdRSzlqWnNFODlDd2tiS29GV0tGZmt2QTZKODBNWHhPZnhqbFZIYU8vYWM4YUpMc1AxWVR1RFNHVis3VExQL0puVVpyNlJQQTdpbFlmMitVWExiS0U2ZW1RYzBKdXlOaVArL0FTMGZmKzYwSnZQekhYeEdQQnVIbWtTcmRqdEtFV0JCZXJzWWNuQVJyT2ZSYz08L01vZHVsdXM+PEV4cG9uZW50PkFRQUI8L0V4cG9uZW50PjwvUlNBS2V5VmFsdWU+
the server is windows with IIS 7.5.
base64decode it give me this XML
<RSAKeyValue><Modulus>vNpXdEeNMNid8n99TzDF1Z803oLGQK9jZsE89CwkbKoFWKFfkvA6J80MXxOfxjlVHaO/ac8aJLsP1YTuDSGV+7TLP/JnUZr6RPA7ilYf2+UXLbKE6emQc0JuyNiP+/AS0ff+60JvPzHXxGPBuHmkSrdjtKEWBBersYcnARrOfRc=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>
Extracting the Modulu and the Exponent from this xml, base64decode them and making the spec for the public key object:
PublicKey pbKey = null;
XMLParser parser = new XMLParser();
Document doc = parser.getDomElement(publicKeyString);
Element rsakeyvalue = doc.getDocumentElement();
String modulusBase64 = parser.getValue(rsakeyvalue, "Modulus");
byte[] modulus = Base64.decode(modulusBase64, 0);
String exponentBase64 = parser.getValue(rsakeyvalue, "Exponent");
byte[] exponent = Base64.decode(exponentBase64, 0);
BigInteger modBigInteger = new BigInteger(1,modulus);
BigInteger exBigInteger = new BigInteger(1,exponent);
RSAPublicKeySpec spec = new RSAPublicKeySpec(modBigInteger, exBigInteger);
try {
KeyFactory factory = KeyFactory.getInstance("RSA");
pbKey = factory.generatePublic(spec);
} catch (Exception e) {
e.printStackTrace();
}
Creating the cipher and adding the plain text to encrypt with:
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "BC");
byte[] keyBytes = Base64.decode(this.publicKey, 0);
/* the strToPublicKey is the previews code block */
PublicKey publickey = strToPublicKey(new String(keyBytes));
cipher.init( Cipher.ENCRYPT_MODE , publickey );
// Base 64 encode removed.
//byte[] encryptedBytes = Base64.encode( cipher.doFinal(plainText.getBytes()), 0 );
byte[] encryptedBytes = cipher.doFinal(plainText.getBytes());
everything here is working but the server don't accept it,
I'm base64 encode it and sending it as byte array.
the server admin saying it's too long, supposed to be 128 bit but it's 174 bit...
what I'm doing wrong?
How its called this key? RSA XML is correct? or it's got other name?
I can also get PEM string from the server if someone have an idea how to do it with it.
Edit:
i forgot to put here an important part, i'm sending the data to the server as byte array, this is how i make the string of it:
public static int unsignedToBytes(byte b) {
return b & 0xFF;
}
StringBuilder byteArrayString = new StringBuilder();
int bytesLength = bytes.length;
int bytesCounter = 0;
for (byte aByte : bytes) {
bytesCounter++;
byteArrayString.append(unsignedToBytes(aByte));
if(bytesCounter < bytesLength){
byteArrayString.append(",");
}
}
SOLVED! - the solution:
the bytes that the string builder used are signed so i use this function unsignedToBytes() to make them unsigned and i removed the base64Encription on the encrypt() function.
this was the problem, i hope it will help anyone else.
Your variable naming is bad. encryptedBytes contains not the encrypted bytes but the encrypted bytes in base64 encoding.
This is the reason you do not get the expected result.
The length of the encrypted data before applying Base64 encoding is 128 byte.
Afterwards the length is 172 bytes.
I have a rails application that encrypts (with attr_encrypted) 2 fields in one of the models.
Another part of my process, which is not the web-application needs to perform some tasks using this data (plaintext).
I'm trying to read the stored values from the DB and decrypt them but just can't..
my model looks like this:
class SecretData < ActiveRecord::Base
mysecret = "mylittlesecret"
attr_encrypted :data1, :key=>mysecret, :algorithm => "aes-256-cbc"
attr_encrypted :data2, :key=>mysecret, :algorithm => "aes-256-cbc"
...
end
The DB fields (encrypted_data1 and encrypted_data2) are filled with data but when I try to decode the base64 (attr_encrypted does that by default) and decrypt (I tried with openssl from commandline and using Java) I get "bad magic number" (openssl) or various errors about key length (in Java). I spent a lot of time trying to decrypt those strings but just couldn't find the way.
Here is all the data I have:
encrypted + base64 strings (for data1 and data2) are:
cyE3jDkKc99GVB8TiUlBxQ==
sqcbOnBTl6yy3wwjkl0qhA==
I can decode base64 from both of them and get some byte array.
When I try:
echo cyE3jDkKc99GVB8TiUlBxQ== | openssl aes-256-cbc -a -d (and type "mylittlesecret" as the password)
I get: "bad magic number"
When I try the following Java code:
Key key = generateKey();
Cipher c = Cipher.getInstance(ALGO);
c.init(Cipher.DECRYPT_MODE, key);
byte[] decordedValue = new BASE64Decoder().decodeBuffer(encryptedData);
byte[] decValue = c.doFinal(decordedValue);
String decryptedValue = new String(decValue);
I get "java.security.InvalidKeyException: Invalid AES key length: 14 bytes"
I've tried many variations for the Java code, so it might be that this particular one is a complete mistake..
When I try in ruby:
irb(main):069:0> Encryptor.decrypt(Base64.decode64("cyE3jDkKc99GVB8TiUlBxQ=="), ,key=>'mylittlesecret')
=> "data1-value"
I get the correct value decrypted (as you can see).
I've also noticed that when I try to encrypt the same string in Java and encode in Base64 I get a longer string (after base64). Don't know why but it's probably related..
I thought I should also have a salt/iv with the encrypted value, but I don't see it stored anywhere.. I tried to encrypt the same value twice and got the same output string so it's not a random one.
Does anyone know how does attr_encrypted (it's using ruby's Encryptor) encrypts data and how I should decrypt it externally?
Well, thanks to owlstead I was able to solve this. I'm posting the code in ruby and Java in case someone needs it in the future:
The problem, as owlstead mentioned, is indeed in the EVP_BytesToKey (key generation from a password and salt). Ruby from some reason doesn't use the standard one and therefore Java (or openssl) can't decode.
Here is a ruby implementation that uses a standard method:
def self.encrypt(options)
plaintext = options[:value]
return true if plaintext.blank?
cipher = OpenSSL::Cipher::Cipher.new(##cipher_type)
cipher.encrypt
iv = cipher.random_iv
salt = (0 ... ##salt_length).map{65.+(rand(25)).chr}.join # random salt
key = OpenSSL::PKCS5.pbkdf2_hmac_sha1(##password, salt, ##pkbdf_num_iters, cipher.key_len)
cipher.key = key
cipher.iv = iv
enc_data = cipher.update(plaintext)
enc_data << cipher.final
final_data = salt << iv << enc_data
Base64.strict_encode64(final_data)
end
def self.decrypt(options)
ciphertext = options[:value]
return true if ciphertext.blank?
cipher = OpenSSL::Cipher::Cipher.new(##cipher_type)
cipher.decrypt
cipher_data = Base64.decode64(ciphertext)
salt = cipher_data[0 .. ##salt_length-1]
iv = cipher_data[##salt_length .. ##salt_length+cipher.iv_len]
enc_data = cipher_data[##salt_length+cipher.iv_len .. -1] # the rest
key = OpenSSL::PKCS5.pbkdf2_hmac_sha1(##password, salt, ##pkbdf_num_iters, cipher.key_len)
cipher.key = key
cipher.iv = iv
plaintext = cipher.update(enc_data)
plaintext << cipher.final
plaintext
end
I've set the following parameters:
- cipher_type = aes-128-cbc (Java supports only 128 but out of the box. For more than that you need to install some additional packages)
- salt_length = 8
- pkbdf_num_iters = 1024
This is the Java method for decoding:
public String decrypt(String ciphertext) throws Exception {
byte[] crypt = Base64.decodeBase64(ciphertext);
// parse the encrypted data and get salt and IV
byte[] salt = Arrays.copyOfRange(crypt, 0, saltLength);
byte[] iv = Arrays.copyOfRange(crypt, saltLength, saltLength + ivLength);
byte[] encryptedData = Arrays.copyOfRange(crypt, saltLength + ivLength, crypt.length);
// generate key from salt and password
SecretKeyFactory f = SecretKeyFactory.getInstance(secretKeyName);
KeySpec ks = new PBEKeySpec(password.toCharArray(), salt, pbkdfNumIters, keyLength);
SecretKey s = f.generateSecret(ks);
Key keySpec = new SecretKeySpec(s.getEncoded(),"AES");
// initialize the cipher object with the key and IV
Cipher cipher = Cipher.getInstance(cipherAlgo);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
// decrypt
byte[] decBytes = cipher.doFinal(encryptedData);
return new String(decBytes);
}
Worked for me.
Hope it helps (or will, to someone..)
Zach
You will need -nosalt to decrypt the data with OpenSSL. For Java you will need an implementation of the OpenSSL EVP_BytesToKey method. One implementation can be found on the blog of Ola Bini. Thanks for putting this in the public domain, Ola.
public static byte[][] EVP_BytesToKey(int key_len, int iv_len, MessageDigest md,
byte[] salt, byte[] data, int count) {
byte[][] both = new byte[2][];
byte[] key = new byte[key_len];
int key_ix = 0;
byte[] iv = new byte[iv_len];
int iv_ix = 0;
both[0] = key;
both[1] = iv;
byte[] md_buf = null;
int nkey = key_len;
int niv = iv_len;
int i = 0;
if (data == null) {
return both;
}
int addmd = 0;
for (;;) {
md.reset();
if (addmd++ > 0) {
md.update(md_buf);
}
md.update(data);
if (null != salt) {
md.update(salt, 0, 8);
}
md_buf = md.digest();
for (i = 1; i < count; i++) {
md.reset();
md.update(md_buf);
md_buf = md.digest();
}
i = 0;
if (nkey > 0) {
for (;;) {
if (nkey == 0)
break;
if (i == md_buf.length)
break;
key[key_ix++] = md_buf[i];
nkey--;
i++;
}
}
if (niv > 0 && i != md_buf.length) {
for (;;) {
if (niv == 0)
break;
if (i == md_buf.length)
break;
iv[iv_ix++] = md_buf[i];
niv--;
i++;
}
}
if (nkey == 0 && niv == 0) {
break;
}
}
for (i = 0; i < md_buf.length; i++) {
md_buf[i] = 0;
}
return both;
}