Decrypting password with PBKDF2 Key - java

I've been playing around with some encryption/decryption with pbkdf/aes128.
I know the master password, iteration count and have the base64 encoded key.
I also have the base64 encoded password which is encrypted with the key. I am trying to decrypt the password. Below is my code so far, but I am fairly stuck with where to go next. Is anyone able to help me out?
public static void main(String... args) throws Exception {
String masterPassword = "password";
String base64EncodedKey = "U2FsdGVkX18QAut2uv9ElKWb2T8QycYS4k0PgjwUtWW2NaBlEmolBwNhPkYDvg986QOYqDs/LVC7mnUUQbrRkbn0sLBbLQ/+oMjvzBpcBRIPqBnQ7bKZjPXZIBDt+15KSRWGSFAnrXVL6Udcecu7t8WNrcUoXy9bgjRpwCzqaMuVRik3sBQNmNbb/4rbf9+NM6tNEZ4KsoHBkcXj4DofzISOBQhmpjfqagjb057vb/8+Pi5cpRl4sO3GPX0Xu8XVTQsLffm3oTTywG9V+2XwXMfvb34SXrJPOkxlHBqjw2GqNx4bhz9buCoLzUFrFfTEeB+gg2yEwFONWhPtV7D/MYAOrZB8P0RaL7oQh1mAnx9ESlFJ5V6VzwbFZGxj7QeaEWG47HFBdSfSuAdibs7N+GtAFv2lQQnEK+li0+rwEYZ+AKeohqe3A6yZDGYpkGvGFr4EDsWEHnYtWl8yL6guLELJxbLXZ8HLLE+W4Sut9l69OS5QeqgLJcuvtw0l1psmrsx8SA/NyFfGNfQE4xi7RPg8qhR3ardEiG6O+IhMMD80PBHFqKZy0jLPRbZA42oApF7isbm4OUQBBrObOpuLMwTCrsMD6DrUUUy1wwd3Ij35mvF7xcJwiYGuE/B3Eqk2UDfDlMXeiWurPJ+px1ie9ZhdfXCTYJOUuRFB25fw0aXFVKO40yrlHoi69B0P2HffO2uH2bzhWQ7KOAqjRyo6mtPn5Xsd+T4rZKWNoWt+fjmLbx+evdDCiF1E/2k8oHAlrvp65DVFhbK69BKFTuMdqr+3sSX9Jl1KPuvJP9fWIk85ncF/K5BbiZ9d47pBt8DFEf+lDhCBSemM0Tjn8fSG6oDlqpdIl7ZXWc/5NhdE6idMQm9bOPbtZWIBM5kJqLpOHLt37vslZFwNv5sq0c2ULwGaBeP1WHfpE0PcaBTTweP3T56ufzo1Larsgw/X3VIXfKM7ZULgbPMQU0SxxBj//jDZJrm+8e782LJJTIMGkcs6B/AOuZCtZVe5gYz1Wlk3JGg+sinBslLyVSP7hFaKHji9KUoBhmyVJajiBYrEVdGLKdyWE7FbGQOZ5rSVNE5kLJ3TBhaejjpHvlJMuGa1WtBmVZTaq2E5ax0wShJixfm+QZMTEhq1ryM3pdsxg0ul1GMLs9x+WRps/F8YHBilwYKEAbqVJxnTRjl8vmqDUS+VrNRYkRHP982DwikzOpTtdlN6+PMta53bw3Q6Qxiiaxhl4SuPYwsV9XcAuDNYu7NcFY+2/IiMJl/ZhLQUXLsbnDT9jUbSi5HVO3W1m65mXX+O/0mFeWmIcf2Ch5aBRhxAtup74V/vKGNVUNY8vU43KrEKFpbD5vc54UY6itQZef3VM+VNqJzQrI9Ka3wKIaY7IWN87KZ/1Odw1ZjdmNQX";
String base64EncodedPassword = "U2FsdGVkX1+H71VA6yn6cA/A/ojEP2lwCUcorrz8CqkffRHSghm/Q0pDsf/p/FTw"; //value is 'password'
int iterations = 100000;
Base64.Decoder base64Decoder = Base64.getDecoder();
byte[] decodedKey = base64Decoder.decode(base64EncodedKey);
byte[] salt = Arrays.copyOfRange(decodedKey, 8, 16);
byte[] keyData = Arrays.copyOfRange(decodedKey, 16, decodedKey.length);
KeySpec spec = new PBEKeySpec(masterPassword.toCharArray(), salt, iterations, 32 * 8);
SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
SecretKey secretKey = f.generateSecret(spec);
byte[] rawKey = secretKey.getEncoded();
byte[] aesRaw = Arrays.copyOfRange(rawKey, 0, 16);
byte[] aesIv = Arrays.copyOfRange(rawKey, 16, 32);
SecretKeySpec aesKey = new SecretKeySpec(aesRaw, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, aesKey, new IvParameterSpec(aesIv));
byte[] decryptedKey = cipher.doFinal(keyData);
byte[] decodedPassword = base64Decoder.decode(base64EncodedPassword);
byte[] passwordSalt = Arrays.copyOfRange(decodedPassword, 8, 16);
byte[] passwordData = Arrays.copyOfRange(decodedPassword, 16,decodedPassword.length);
// hmm...
}
Thanks,

Related

What type of encryption that used in this?

I'm new on Mobile Programming especially encryption, is anyone know what type of encryption that used in this code below?
static String encrypt(String text) throws Exception
{
String password = AppData.ENCRYPT_SERVER;
final byte[] pass = password.getBytes(US_ASCII);
final byte[] salt = (new SecureRandom()).generateSeed(8);
final byte[] inBytes = text.getBytes(UTF_8);
final byte[] passAndSalt = array_concat(pass, salt);
byte[] hash = new byte[0];
byte[] keyAndIv = new byte[0];
for (int i = 0; i < 3 && keyAndIv.length < 48; i++) {
final byte[] hashData = array_concat(hash, passAndSalt);
final MessageDigest md = MessageDigest.getInstance("MD5");
hash = md.digest(hashData);
keyAndIv = array_concat(keyAndIv, hash);
}
final byte[] keyValue = Arrays.copyOfRange(keyAndIv, 0, 32);
final byte[] iv = Arrays.copyOfRange(keyAndIv, 32, 48);
final SecretKeySpec key = new SecretKeySpec(keyValue, "AES");
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
byte[] data = cipher.doFinal(inBytes);
data = array_concat(array_concat(SALTED_MAGIC, salt), data);
return Base64.encodeToString(data, Base64.DEFAULT);
}
You have the answer to your question in this line:
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
AES
Advanced Encryption Standard - this is your encryption.
CBC
Cipher block chaining - this is the cipher encryption mode.
PKCS5
Public Key Cryptography Standards #5 - This is the padding applied.

Decrypt an encrypted private key file with passphrase - java [duplicate]

I have an encrypted private key and I know the password.
I need to decrypt it using a Java library.
I'd prefer not to use BouncyCastle though, unless there is no other option. Based on previous experience, there is too much change and not enough documentation.
The private key is in this form:
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,56F3A98D9CFFA77A
X5h7SUDStF1tL16lRM+AfZb1UBDQ0D1YbQ6vmIlXiK....
.....
/KK5CZmIGw==
-----END RSA PRIVATE KEY-----
I believe the key data is Base64 encoded since I see \r\n after 64 characters.
I tried the following to decrypt the key:
import java.security.Key;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import javax.crypto.EncryptedPrivateKeyInfo;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
public String decrypt(String keyDataStr, String passwordStr){
// This key data start from "X5... to =="
char [] password=passwordStr.toCharArray();
byte [] keyDataBytes=com.sun.jersey.core.util.Base64.decode(keyDataStr);
PBEKeySpec pbeSpec = new PBEKeySpec(password);
EncryptedPrivateKeyInfo pkinfo = new EncryptedPrivateKeyInfo(keyDataBytes);
SecretKeyFactory skf = SecretKeyFactory.getInstance(pkinfo.getAlgName());
Key secret = skf.generateSecret(pbeSpec);
PKCS8EncodedKeySpec keySpec = pkinfo.getKeySpec(secret);
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey pk=kf.generatePrivate(keySpec);
return pk.toString();
}
I get this Exception
java.io.IOException: DerInputStream.getLength(): lengthTag=50, too big.
at sun.security.util.DerInputStream.getLength(DerInputStream.java:561)
at sun.security.util.DerValue.init(DerValue.java:365)
at sun.security.util.DerValue.<init>(DerValue.java:294)
at javax.crypto.EncryptedPrivateKeyInfo.<init> (EncryptedPrivateKeyInfo.java:84)
Am I passing the right parameter to EncryptedPrivateKeyInfo constructor?
How can I make this work?
I tried what Ericsonn suggested, with one small change since I am working Java 7, I could not use Base64.getMimeCoder() instead I used Base64.decode and I am getting this error
I am getting an error like this Input length must be multiple of 8 when decrypting with padded cipher at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:750)
static RSAPrivateKey decrypt(String keyDataStr, String ivHex, String password)
throws GeneralSecurityException, UnsupportedEncodingException
{
byte[] pw = password.getBytes(StandardCharsets.UTF_8);
byte[] iv = h2b(ivHex);
SecretKey secret = opensslKDF(pw, iv);
Cipher cipher = Cipher.getInstance("DESede/CBC/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
byte [] keyBytes=Base64.decode(keyDataStr.getBytes("UTF-8"));
byte[] pkcs1 = cipher.doFinal(keyBytes);
/* See note for definition of "decodeRSAPrivatePKCS1" */
RSAPrivateCrtKeySpec spec = decodeRSAPrivatePKCS1(pkcs1);
KeyFactory rsa = KeyFactory.getInstance("RSA");
return (RSAPrivateKey) rsa.generatePrivate(spec);
}
private static SecretKey opensslKDF(byte[] pw, byte[] iv)
throws NoSuchAlgorithmException
{
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.update(pw);
md5.update(iv);
byte[] d0 = md5.digest();
md5.update(d0);
md5.update(pw);
md5.update(iv);
byte[] d1 = md5.digest();
byte[] key = new byte[24];
System.arraycopy(d0, 0, key, 0, 16);
System.arraycopy(d1, 0, key, 16, 8);
return new SecretKeySpec(key, "DESede");
}
private static byte[] h2b(CharSequence s)
{
int len = s.length();
byte[] b = new byte[len / 2];
for (int src = 0, dst = 0; src < len; ++dst) {
int hi = Character.digit(s.charAt(src++), 16);
int lo = Character.digit(s.charAt(src++), 16);
b[dst] = (byte) (hi << 4 | lo);
}
return b;
}
static RSAPrivateCrtKeySpec decodeRSAPrivatePKCS1(byte[] encoded)
{
ByteBuffer input = ByteBuffer.wrap(encoded);
if (der(input, 0x30) != input.remaining())
throw new IllegalArgumentException("Excess data");
if (!BigInteger.ZERO.equals(derint(input)))
throw new IllegalArgumentException("Unsupported version");
BigInteger n = derint(input);
BigInteger e = derint(input);
BigInteger d = derint(input);
BigInteger p = derint(input);
BigInteger q = derint(input);
BigInteger ep = derint(input);
BigInteger eq = derint(input);
BigInteger c = derint(input);
return new RSAPrivateCrtKeySpec(n, e, d, p, q, ep, eq, c);
}
private static BigInteger derint(ByteBuffer input)
{
byte[] value = new byte[der(input, 0x02)];
input.get(value);
return new BigInteger(+1, value);
}
private static int der(ByteBuffer input, int exp)
{
int tag = input.get() & 0xFF;
if (tag != exp)
throw new IllegalArgumentException("Unexpected tag");
int n = input.get() & 0xFF;
if (n < 128)
return n;
n &= 0x7F;
if ((n < 1) || (n > 2))
throw new IllegalArgumentException("Invalid length");
int len = 0;
while (n-- > 0) {
len <<= 8;
len |= input.get() & 0xFF;
}
return len;
}
1640 is keyDataStr.length() and 1228 is keyBytes.length
You need to use a non-standard, OpenSSL method for deriving the decryption key. Then use that to decrypt the PKCS-#1–encoded key—what you are working with is not a PKCS #8 envelope. You'll also need the IV from the header as input to these processes.
It looks something like this:
static RSAPrivateKey decrypt(String keyDataStr, String ivHex, String password)
throws GeneralSecurityException
{
byte[] pw = password.getBytes(StandardCharsets.UTF_8);
byte[] iv = h2b(ivHex);
SecretKey secret = opensslKDF(pw, iv);
Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
byte[] pkcs1 = cipher.doFinal(Base64.getMimeDecoder().decode(keyDataStr));
/* See note for definition of "decodeRSAPrivatePKCS1" */
RSAPrivateCrtKeySpec spec = decodeRSAPrivatePKCS1(pkcs1);
KeyFactory rsa = KeyFactory.getInstance("RSA");
return (RSAPrivateKey) rsa.generatePrivate(spec);
}
private static SecretKey opensslKDF(byte[] pw, byte[] iv)
throws NoSuchAlgorithmException
{
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.update(pw);
md5.update(iv);
byte[] d0 = md5.digest();
md5.update(d0);
md5.update(pw);
md5.update(iv);
byte[] d1 = md5.digest();
byte[] key = new byte[24];
System.arraycopy(d0, 0, key, 0, 16);
System.arraycopy(d1, 0, key, 16, 8);
return new SecretKeySpec(key, "DESede");
}
private static byte[] h2b(CharSequence s)
{
int len = s.length();
byte[] b = new byte[len / 2];
for (int src = 0, dst = 0; src < len; ++dst) {
int hi = Character.digit(s.charAt(src++), 16);
int lo = Character.digit(s.charAt(src++), 16);
b[dst] = (byte) (hi << 4 | lo);
}
return b;
}
This is already a lot of code, so I will link to another answer for the definition of the decodeRSAPrivatePKCS1() method.
Java code example below shows how to construct the decryption key to obtain the underlying RSA key from an encrypted private key created using the openssl 1.0.x genrsa command; specifically from the following genrsa options that may have been leveraged:
-des encrypt the generated key with DES in cbc mode
-des3 encrypt the generated key with DES in ede cbc mode (168 bit key)
-aes128, -aes192, -aes256 encrypt PEM output with cbc aes
Above options result in encrypted RSA private key of the form ...
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AAA,BBB
...
Where AAA would be one of:
DES-CBC, DES-EDE3-CBC, AES-128-CBC, AES-192-CBC, AES-256-CBC
AND BBB is the hex-encoded IV value
KeyFactory factory = KeyFactory.getInstance("RSA");
KeySpec keySpec = null;
RSAPrivateKey privateKey = null;
Matcher matcher = OPENSSL_ENCRYPTED_RSA_PRIVATEKEY_PATTERN.matcher(pemContents);
if (matcher.matches())
{
String encryptionDetails = matcher.group(1).trim(); // e.g. AES-256-CBC,XXXXXXX
String encryptedKey = matcher.group(2).replaceAll("\\s", ""); // remove tabs / spaces / newlines / carriage return etc
System.out.println("PEM appears to be OpenSSL Encrypted RSA Private Key; Encryption details : "
+ encryptionDetails + "; Key : " + encryptedKey);
byte[] encryptedBinaryKey = java.util.Base64.getDecoder().decode(encryptedKey);
String[] encryptionDetailsParts = encryptionDetails.split(",");
if (encryptionDetailsParts.length == 2)
{
String encryptionAlgorithm = encryptionDetailsParts[0];
String encryptedAlgorithmParams = encryptionDetailsParts[1]; // i.e. the initialization vector in hex
byte[] pw = new String(password).getBytes(StandardCharsets.UTF_8);
byte[] iv = fromHex(encryptedAlgorithmParams);
MessageDigest digest = MessageDigest.getInstance("MD5");
// we need to come up with the encryption key
// first round digest based on password and first 8-bytes of IV ..
digest.update(pw);
digest.update(iv, 0, 8);
byte[] round1Digest = digest.digest(); // The digest is reset after this call is made.
// second round digest based on first round digest, password, and first 8-bytes of IV ...
digest.update(round1Digest);
digest.update(pw);
digest.update(iv, 0, 8);
byte[] round2Digest = digest.digest();
Cipher cipher = null;
SecretKey secretKey = null;
byte[] key = null;
byte[] pkcs1 = null;
if ("AES-256-CBC".equals(encryptionAlgorithm))
{
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
key = new byte[32]; // 256 bit key (block size still 128-bit)
System.arraycopy(round1Digest, 0, key, 0, 16);
System.arraycopy(round2Digest, 0, key, 16, 16);
secretKey = new SecretKeySpec(key, "AES");
}
else if ("AES-192-CBC".equals(encryptionAlgorithm))
{
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
key = new byte[24]; // key size of 24 bytes
System.arraycopy(round1Digest, 0, key, 0, 16);
System.arraycopy(round2Digest, 0, key, 16, 8);
secretKey = new SecretKeySpec(key, "AES");
}
else if ("AES-128-CBC".equals(encryptionAlgorithm))
{
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
key = new byte[16]; // 128 bit key
System.arraycopy(round1Digest, 0, key, 0, 16);
secretKey = new SecretKeySpec(key, "AES");
}
else if ("DES-EDE3-CBC".equals(encryptionAlgorithm))
{
cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
key = new byte[24]; // key size of 24 bytes
System.arraycopy(round1Digest, 0, key, 0, 16);
System.arraycopy(round2Digest, 0, key, 16, 8);
secretKey = new SecretKeySpec(key, "DESede");
}
else if ("DES-CBC".equals(encryptionAlgorithm))
{
cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
key = new byte[8]; // key size of 8 bytes
System.arraycopy(round1Digest, 0, key, 0, 8);
secretKey = new SecretKeySpec(key, "DES");
}
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv));
pkcs1 = cipher.doFinal(encryptedBinaryKey);
keySpec = pkcs1ParsePrivateKey(pkcs1);
privateKey = (RSAPrivateKey) factory.generatePrivate(keySpec);
}
}
The regular expression ...
static final String OPENSSL_ENCRYPTED_RSA_PRIVATEKEY_REGEX = "\\s*"
+ "-----BEGIN RSA PUBLIC KEY-----" + "\\s*"
+ "Proc-Type: 4,ENCRYPTED" + "\\s*"
+ "DEK-Info:" + "\\s*([^\\s]+)" + "\\s+"
+ "([\\s\\S]*)"
+ "-----END RSA PUBLIC KEY-----" + "\\s*";
static final Pattern OPENSSL_ENCRYPTED_RSA_PRIVATEKEY_PATTERN = Pattern.compile(OPENSSL_ENCRYPTED_RSA_PRIVATEKEY_REGEX);
the fromHex(...) method ...
public static byte[] fromHex(String hexString)
{
byte[] bytes = new byte[hexString.length() / 2];
for (int i = 0; i < hexString.length(); i += 2)
{
bytes[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4)
+ Character.digit(hexString.charAt(i + 1), 16));
}
return bytes;
}

Triple Des codes returning cipher with different lengths

I have got this code from Server guys:
public string Encryption(string PlainText)
{
string key = "twelve_digit_key";
TripleDES des = CreateDES(key);
ICryptoTransform ct = des.CreateEncryptor();
byte[] input = Encoding.Unicode.GetBytes(PlainText);
byte[] buffer = ct.TransformFinalBlock(input, 0, input.Length);
return Convert.ToBase64String(buffer);
}
static TripleDES CreateDES(string key)
{
MD5 md5 = new MD5CryptoServiceProvider();
TripleDES des = new TripleDESCryptoServiceProvider();
des.Key = md5.ComputeHash(Encoding.Unicode.GetBytes(key));
des.IV = new byte[des.BlockSize / 8];
return des;
}
This is my code against above :
public String encryptDES(String message) throws Exception {
final MessageDigest md = MessageDigest.getInstance("md5");
final byte[] digestOfPassword = md.digest(getNativeKey3().getBytes("utf-8"));
final byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
for (int j = 0, k = 16; j < 8; ) {
keyBytes[k++] = keyBytes[j++];
}
final SecretKey key = new SecretKeySpec(digestOfPassword, "DESede");
final IvParameterSpec iv = new IvParameterSpec(new byte[8]);
final Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS7Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
final byte[] plainTextBytes = message.getBytes("utf-8");
final byte[] cipherText = cipher.doFinal(plainTextBytes);
return Base64.encodeToString(cipherText, Base64.DEFAULT)
.replace("\n", "")
.replace("\r", "");
}
Problem :
First Code gives below result :
Encrypted Text for 121212 is VvRQkSUj5SQ69mGXsL+h6w==
But Second Code returns this :
Encrypted Text for 121212 is 2STVJSd1mnw=
Observations :
When I increase the plainttext to 10 digits I am getting 24 digit cipher text
Can any one help me in this:
Thanks in Advance
You've been fooled by the badly named Unicode class, which actually specifies UTF-16LE rather than UTF-8.
You can use StandardCharsets.UTF_16LE for specifying the encoding rather than the string; this saves you from one exception to handle.
If there are still issues with the length (test!) then you may have to deal with the Byte Order Mark or BOM - but I don't think so.

Decrypting an OpenSSL PEM Encoded RSA private key with Java?

I have an encrypted private key and I know the password.
I need to decrypt it using a Java library.
I'd prefer not to use BouncyCastle though, unless there is no other option. Based on previous experience, there is too much change and not enough documentation.
The private key is in this form:
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,56F3A98D9CFFA77A
X5h7SUDStF1tL16lRM+AfZb1UBDQ0D1YbQ6vmIlXiK....
.....
/KK5CZmIGw==
-----END RSA PRIVATE KEY-----
I believe the key data is Base64 encoded since I see \r\n after 64 characters.
I tried the following to decrypt the key:
import java.security.Key;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import javax.crypto.EncryptedPrivateKeyInfo;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
public String decrypt(String keyDataStr, String passwordStr){
// This key data start from "X5... to =="
char [] password=passwordStr.toCharArray();
byte [] keyDataBytes=com.sun.jersey.core.util.Base64.decode(keyDataStr);
PBEKeySpec pbeSpec = new PBEKeySpec(password);
EncryptedPrivateKeyInfo pkinfo = new EncryptedPrivateKeyInfo(keyDataBytes);
SecretKeyFactory skf = SecretKeyFactory.getInstance(pkinfo.getAlgName());
Key secret = skf.generateSecret(pbeSpec);
PKCS8EncodedKeySpec keySpec = pkinfo.getKeySpec(secret);
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey pk=kf.generatePrivate(keySpec);
return pk.toString();
}
I get this Exception
java.io.IOException: DerInputStream.getLength(): lengthTag=50, too big.
at sun.security.util.DerInputStream.getLength(DerInputStream.java:561)
at sun.security.util.DerValue.init(DerValue.java:365)
at sun.security.util.DerValue.<init>(DerValue.java:294)
at javax.crypto.EncryptedPrivateKeyInfo.<init> (EncryptedPrivateKeyInfo.java:84)
Am I passing the right parameter to EncryptedPrivateKeyInfo constructor?
How can I make this work?
I tried what Ericsonn suggested, with one small change since I am working Java 7, I could not use Base64.getMimeCoder() instead I used Base64.decode and I am getting this error
I am getting an error like this Input length must be multiple of 8 when decrypting with padded cipher at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:750)
static RSAPrivateKey decrypt(String keyDataStr, String ivHex, String password)
throws GeneralSecurityException, UnsupportedEncodingException
{
byte[] pw = password.getBytes(StandardCharsets.UTF_8);
byte[] iv = h2b(ivHex);
SecretKey secret = opensslKDF(pw, iv);
Cipher cipher = Cipher.getInstance("DESede/CBC/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
byte [] keyBytes=Base64.decode(keyDataStr.getBytes("UTF-8"));
byte[] pkcs1 = cipher.doFinal(keyBytes);
/* See note for definition of "decodeRSAPrivatePKCS1" */
RSAPrivateCrtKeySpec spec = decodeRSAPrivatePKCS1(pkcs1);
KeyFactory rsa = KeyFactory.getInstance("RSA");
return (RSAPrivateKey) rsa.generatePrivate(spec);
}
private static SecretKey opensslKDF(byte[] pw, byte[] iv)
throws NoSuchAlgorithmException
{
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.update(pw);
md5.update(iv);
byte[] d0 = md5.digest();
md5.update(d0);
md5.update(pw);
md5.update(iv);
byte[] d1 = md5.digest();
byte[] key = new byte[24];
System.arraycopy(d0, 0, key, 0, 16);
System.arraycopy(d1, 0, key, 16, 8);
return new SecretKeySpec(key, "DESede");
}
private static byte[] h2b(CharSequence s)
{
int len = s.length();
byte[] b = new byte[len / 2];
for (int src = 0, dst = 0; src < len; ++dst) {
int hi = Character.digit(s.charAt(src++), 16);
int lo = Character.digit(s.charAt(src++), 16);
b[dst] = (byte) (hi << 4 | lo);
}
return b;
}
static RSAPrivateCrtKeySpec decodeRSAPrivatePKCS1(byte[] encoded)
{
ByteBuffer input = ByteBuffer.wrap(encoded);
if (der(input, 0x30) != input.remaining())
throw new IllegalArgumentException("Excess data");
if (!BigInteger.ZERO.equals(derint(input)))
throw new IllegalArgumentException("Unsupported version");
BigInteger n = derint(input);
BigInteger e = derint(input);
BigInteger d = derint(input);
BigInteger p = derint(input);
BigInteger q = derint(input);
BigInteger ep = derint(input);
BigInteger eq = derint(input);
BigInteger c = derint(input);
return new RSAPrivateCrtKeySpec(n, e, d, p, q, ep, eq, c);
}
private static BigInteger derint(ByteBuffer input)
{
byte[] value = new byte[der(input, 0x02)];
input.get(value);
return new BigInteger(+1, value);
}
private static int der(ByteBuffer input, int exp)
{
int tag = input.get() & 0xFF;
if (tag != exp)
throw new IllegalArgumentException("Unexpected tag");
int n = input.get() & 0xFF;
if (n < 128)
return n;
n &= 0x7F;
if ((n < 1) || (n > 2))
throw new IllegalArgumentException("Invalid length");
int len = 0;
while (n-- > 0) {
len <<= 8;
len |= input.get() & 0xFF;
}
return len;
}
1640 is keyDataStr.length() and 1228 is keyBytes.length
You need to use a non-standard, OpenSSL method for deriving the decryption key. Then use that to decrypt the PKCS-#1–encoded key—what you are working with is not a PKCS #8 envelope. You'll also need the IV from the header as input to these processes.
It looks something like this:
static RSAPrivateKey decrypt(String keyDataStr, String ivHex, String password)
throws GeneralSecurityException
{
byte[] pw = password.getBytes(StandardCharsets.UTF_8);
byte[] iv = h2b(ivHex);
SecretKey secret = opensslKDF(pw, iv);
Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
byte[] pkcs1 = cipher.doFinal(Base64.getMimeDecoder().decode(keyDataStr));
/* See note for definition of "decodeRSAPrivatePKCS1" */
RSAPrivateCrtKeySpec spec = decodeRSAPrivatePKCS1(pkcs1);
KeyFactory rsa = KeyFactory.getInstance("RSA");
return (RSAPrivateKey) rsa.generatePrivate(spec);
}
private static SecretKey opensslKDF(byte[] pw, byte[] iv)
throws NoSuchAlgorithmException
{
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.update(pw);
md5.update(iv);
byte[] d0 = md5.digest();
md5.update(d0);
md5.update(pw);
md5.update(iv);
byte[] d1 = md5.digest();
byte[] key = new byte[24];
System.arraycopy(d0, 0, key, 0, 16);
System.arraycopy(d1, 0, key, 16, 8);
return new SecretKeySpec(key, "DESede");
}
private static byte[] h2b(CharSequence s)
{
int len = s.length();
byte[] b = new byte[len / 2];
for (int src = 0, dst = 0; src < len; ++dst) {
int hi = Character.digit(s.charAt(src++), 16);
int lo = Character.digit(s.charAt(src++), 16);
b[dst] = (byte) (hi << 4 | lo);
}
return b;
}
This is already a lot of code, so I will link to another answer for the definition of the decodeRSAPrivatePKCS1() method.
Java code example below shows how to construct the decryption key to obtain the underlying RSA key from an encrypted private key created using the openssl 1.0.x genrsa command; specifically from the following genrsa options that may have been leveraged:
-des encrypt the generated key with DES in cbc mode
-des3 encrypt the generated key with DES in ede cbc mode (168 bit key)
-aes128, -aes192, -aes256 encrypt PEM output with cbc aes
Above options result in encrypted RSA private key of the form ...
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AAA,BBB
...
Where AAA would be one of:
DES-CBC, DES-EDE3-CBC, AES-128-CBC, AES-192-CBC, AES-256-CBC
AND BBB is the hex-encoded IV value
KeyFactory factory = KeyFactory.getInstance("RSA");
KeySpec keySpec = null;
RSAPrivateKey privateKey = null;
Matcher matcher = OPENSSL_ENCRYPTED_RSA_PRIVATEKEY_PATTERN.matcher(pemContents);
if (matcher.matches())
{
String encryptionDetails = matcher.group(1).trim(); // e.g. AES-256-CBC,XXXXXXX
String encryptedKey = matcher.group(2).replaceAll("\\s", ""); // remove tabs / spaces / newlines / carriage return etc
System.out.println("PEM appears to be OpenSSL Encrypted RSA Private Key; Encryption details : "
+ encryptionDetails + "; Key : " + encryptedKey);
byte[] encryptedBinaryKey = java.util.Base64.getDecoder().decode(encryptedKey);
String[] encryptionDetailsParts = encryptionDetails.split(",");
if (encryptionDetailsParts.length == 2)
{
String encryptionAlgorithm = encryptionDetailsParts[0];
String encryptedAlgorithmParams = encryptionDetailsParts[1]; // i.e. the initialization vector in hex
byte[] pw = new String(password).getBytes(StandardCharsets.UTF_8);
byte[] iv = fromHex(encryptedAlgorithmParams);
MessageDigest digest = MessageDigest.getInstance("MD5");
// we need to come up with the encryption key
// first round digest based on password and first 8-bytes of IV ..
digest.update(pw);
digest.update(iv, 0, 8);
byte[] round1Digest = digest.digest(); // The digest is reset after this call is made.
// second round digest based on first round digest, password, and first 8-bytes of IV ...
digest.update(round1Digest);
digest.update(pw);
digest.update(iv, 0, 8);
byte[] round2Digest = digest.digest();
Cipher cipher = null;
SecretKey secretKey = null;
byte[] key = null;
byte[] pkcs1 = null;
if ("AES-256-CBC".equals(encryptionAlgorithm))
{
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
key = new byte[32]; // 256 bit key (block size still 128-bit)
System.arraycopy(round1Digest, 0, key, 0, 16);
System.arraycopy(round2Digest, 0, key, 16, 16);
secretKey = new SecretKeySpec(key, "AES");
}
else if ("AES-192-CBC".equals(encryptionAlgorithm))
{
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
key = new byte[24]; // key size of 24 bytes
System.arraycopy(round1Digest, 0, key, 0, 16);
System.arraycopy(round2Digest, 0, key, 16, 8);
secretKey = new SecretKeySpec(key, "AES");
}
else if ("AES-128-CBC".equals(encryptionAlgorithm))
{
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
key = new byte[16]; // 128 bit key
System.arraycopy(round1Digest, 0, key, 0, 16);
secretKey = new SecretKeySpec(key, "AES");
}
else if ("DES-EDE3-CBC".equals(encryptionAlgorithm))
{
cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
key = new byte[24]; // key size of 24 bytes
System.arraycopy(round1Digest, 0, key, 0, 16);
System.arraycopy(round2Digest, 0, key, 16, 8);
secretKey = new SecretKeySpec(key, "DESede");
}
else if ("DES-CBC".equals(encryptionAlgorithm))
{
cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
key = new byte[8]; // key size of 8 bytes
System.arraycopy(round1Digest, 0, key, 0, 8);
secretKey = new SecretKeySpec(key, "DES");
}
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv));
pkcs1 = cipher.doFinal(encryptedBinaryKey);
keySpec = pkcs1ParsePrivateKey(pkcs1);
privateKey = (RSAPrivateKey) factory.generatePrivate(keySpec);
}
}
The regular expression ...
static final String OPENSSL_ENCRYPTED_RSA_PRIVATEKEY_REGEX = "\\s*"
+ "-----BEGIN RSA PUBLIC KEY-----" + "\\s*"
+ "Proc-Type: 4,ENCRYPTED" + "\\s*"
+ "DEK-Info:" + "\\s*([^\\s]+)" + "\\s+"
+ "([\\s\\S]*)"
+ "-----END RSA PUBLIC KEY-----" + "\\s*";
static final Pattern OPENSSL_ENCRYPTED_RSA_PRIVATEKEY_PATTERN = Pattern.compile(OPENSSL_ENCRYPTED_RSA_PRIVATEKEY_REGEX);
the fromHex(...) method ...
public static byte[] fromHex(String hexString)
{
byte[] bytes = new byte[hexString.length() / 2];
for (int i = 0; i < hexString.length(); i += 2)
{
bytes[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4)
+ Character.digit(hexString.charAt(i + 1), 16));
}
return bytes;
}

Java CBC decrypting works, but CTR is failing

I setup a program for a class I am taking on crypto. I will follow this with my code and another section for my variable differences. My goal is to decrypt the text for our homework. I do not want someone to decrypt this for me, but would like some help as to what is causing this within my code. When I decrypt CBC I get the correct output with no problem, though it does have some extra chars in it (this may be an issue with padding? I am not sure)
Then when I use the CTR with the correct changes it returns a bunch of garbage. Any help would be greatly appreciated.
Thank you,
CBC:
CBC key: 140b41b22a29beb4061bda66b6747e14
CBC Ciphertext 1:
4ca00ff4c898d61e1edbf1800618fb2828a226d160dad07883d04e008a7897ee2e4b7465d5290d0c0e6c6822236e1daafb94ffe0c5da05d9476be028ad7c1d81
CTR:
CTR key: 36f18357be4dbd77f050515c73fcf9f2
CTR Ciphertext 1:
69dda8455c7dd4254bf353b773304eec0ec7702330098ce7f7520d1cbbb20fc388d1b0adb5054dbd7370849dbf0b88d393f252e764f1f5f7ad97ef79d59ce29f5f51eeca32eabedd9afa9329
CBC Variables
String algorithm = "AES";
String mode = "CBC";
String padding = "PKCS5Padding";
byte[] ciphertextBytes = StringToByte("4ca00ff4c898d61e1edbf1800618fb2828a226d160dad07883d04e008a7897ee2e4b7465d5290d0c0e6c6822236e1daafb94ffe0c5da05d9476be028ad7c1d81");
byte[] keyBytes = StringToByte("140b41b22a29beb4061bda66b6747e14");
CTR Variables
String algorithm = "AES";
String mode = "CTR";
String padding = "NoPadding";
byte[] ciphertextBytes = StringToByte("770b80259ec33beb2561358a9f2dc617e46218c0a53cbeca695ae45faa8952aa0e311bde9d4e01726d3184c34451");
byte[] keyBytes = StringToByte("36f18357be4dbd77f050515c73fcf9f2");
Decrypt Main
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
import static java.lang.Character.digit;
public class CryptoClass {
public static void main(String[] args) throws Exception {
byte[] decryptByte = Decrypt();
String hexString = ByteToHex(decryptByte);
StringBuilder decryptedString = HexToString(hexString);
System.out.println(decryptedString);
}
public static byte[] Decrypt() throws Exception {
//
String algorithm = "AES";
String mode = "CTR";
String padding = "NoPadding";
byte[] ciphertextBytes = StringToByte("770b80259ec33beb2561358a9f2dc617e46218c0a53cbeca695ae45faa8952aa0e311bde9d4e01726d3184c34451");
byte[] keyBytes = StringToByte("36f18357be4dbd77f050515c73fcf9f2");
IvParameterSpec ivParamSpec = null;
int ivSize = 16;
byte[] iv = new byte[ivSize];
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
secureRandom.nextBytes(iv);
ivParamSpec = new IvParameterSpec(iv);
SecretKey aesKey = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance(algorithm + "/" + mode + "/" + padding, "JsafeJCE");
cipher.init(Cipher.DECRYPT_MODE, aesKey, ivParamSpec);
byte[] result = cipher.doFinal(ciphertextBytes);
return result;
}
//convert ByteArray to Hex String
public static String ByteToHex(byte[] byteArray) {
StringBuilder sb = new StringBuilder();
for (byte b : byteArray)
{
sb.append(String.format("%02X", b));
}
return sb.toString();
}
//convert String to ByteArray
private static byte[] StringToByte(String input) {
int length = input.length();
byte[] output = new byte[length / 2];
for (int i = 0; i < length; i += 2) {
output[i / 2] = (byte) ((digit(input.charAt(i), 16) << 4) | digit(input.charAt(i+1), 16));
}
return output;
}
//changes a hex string into plain text
public static StringBuilder HexToString(String hex) throws Exception {
StringBuilder output = new StringBuilder();
for (int i = 0; i < hex.length(); i+=2) {
String str = hex.substring(i, i+2);
output.append((char)Integer.parseInt(str, 16));
}
return output;
}
}
*Edit method for solution - instead of a random IV I pulled the IV from the first 16 bits of the ciphertext. In the assignment it stated that this was the case, for some reason I glossed over it when I looked through it the first time.
public static byte[] Decrypt() throws Exception {
String algorithm = "AES";
String mode = "CTR";
String padding = "NoPadding";
byte[] ciphertextBytes = StringToByte("0ec7702330098ce7f7520d1cbbb20fc388d1b0adb5054dbd7370849dbf0b88d393f252e764f1f5f7ad97ef79d59ce29f5f51eeca32eabedd9afa9329");
byte[] keyBytes = StringToByte("36f18357be4dbd77f050515c73fcf9f2");
//int ivSize = 16;
//byte[] iv = new byte[ivSize];
//SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
//secureRandom.nextBytes(iv);
byte[] ivParamSpecTMP = StringToByte("69dda8455c7dd4254bf353b773304eec");
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivParamSpecTMP);
SecretKey aesKey = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance(algorithm + "/" + mode + "/" + padding, "JsafeJCE");
cipher.init(Cipher.DECRYPT_MODE, aesKey, ivParameterSpec);
byte[] result = cipher.doFinal(ciphertextBytes);
return result;
The trick is that you must send the IV (in plain text) to the receiver. If you randomly generate the IV before decryption you will get garbage by definition. Random IV's should only be generated before encryption.
Standard practice is for the sender to prefix the IV to the ciphertext. The receiver uses the first 16 bytes as an IV and the rest as the actual ciphertext.

Categories

Resources