I am attempting to translate the following code in PHP to Java.
private function _aes256_cbc_encrypt($key, $data, $iv) {
if (32 !== strlen($key))
$key = hash('SHA256', $key, true);
if (16 !== strlen($iv))
$iv = hash('MD5', $iv, true);
$padding = 16 - (strlen($data) % 16);
$data .= str_repeat(chr($padding), $padding);
return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, $iv);
}
So far this is what I have in Java.
private String aes256_cbc_encrypt(String key, String data, String iv) throws Exception {
if (32 != key.length())
key = sha256(key);
if (16 != iv.length())
iv = md5(iv);
char padding = (char) (16-(data.length() % 16));
char[] paddingArray = new char[padding];
Arrays.fill(paddingArray, padding);
data = data + new String(paddingArray);
String mcryptData = data;
MCrypt mcrypt = new MCrypt();
String encrypted = mcrypt.bytesToHex(mcrypt.encrypt(mcryptData));
return encrypted;
}
The sha256 and md5 methods are:
public static String md5(String input) throws NoSuchAlgorithmException{
String result = input;
if(input != null) {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(input.getBytes());
BigInteger hash = new BigInteger(1, md.digest());
result = hash.toString(16);
while(result.length() < 32) {
result = "0" + result;
}
}
return result;
}
public static String sha256(String input) throws NoSuchAlgorithmException{
String result = input;
if(input != null) {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(input.getBytes());
BigInteger hash = new BigInteger(1, md.digest());
result = hash.toString(16);
while(result.length() < 32) {
result = "0" + result;
}
}
return result;
}
And the mcrypt class I am using (which I found here https://github.com/serpro/Android-PHP-Encrypt-Decrypt/blob/master/Java/src/com/serpro/library/String/MCrypt.java) is
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
class MCrypt {
static char[] HEX_CHARS = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
private String iv = "{Yr5xwjQp0:\\EC4L";
private IvParameterSpec ivspec;
private SecretKeySpec keyspec;
private Cipher cipher;
private String SecretKey = "m78gLMbPQ";
public MCrypt()
{
ivspec = new IvParameterSpec(iv.getBytes());
keyspec = new SecretKeySpec(SecretKey.getBytes(), "AES");
try {
cipher = Cipher.getInstance("AES/CBC/NoPadding");
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public byte[] encrypt(String text) throws Exception
{
if(text == null || text.length() == 0)
throw new Exception("Empty string");
byte[] encrypted = null;
try {
cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
encrypted = cipher.doFinal(padString(text).getBytes());
} catch (Exception e)
{
throw new Exception("[encrypt] " + e.getMessage());
}
return encrypted;
}
public byte[] decrypt(String code) throws Exception
{
if(code == null || code.length() == 0)
throw new Exception("Empty string");
byte[] decrypted = null;
try {
cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
decrypted = cipher.doFinal(hexToBytes(code));
//Remove trailing zeroes
if( decrypted.length > 0)
{
int trim = 0;
for( int i = decrypted.length - 1; i >= 0; i-- ) if( decrypted[i] == 0 ) trim++;
if( trim > 0 )
{
byte[] newArray = new byte[decrypted.length - trim];
System.arraycopy(decrypted, 0, newArray, 0, decrypted.length - trim);
decrypted = newArray;
}
}
} catch (Exception e)
{
throw new Exception("[decrypt] " + e.getMessage());
}
return decrypted;
}
public static String bytesToHex(byte[] buf)
{
char[] chars = new char[2 * buf.length];
for (int i = 0; i < buf.length; ++i)
{
chars[2 * i] = HEX_CHARS[(buf[i] & 0xF0) >>> 4];
chars[2 * i + 1] = HEX_CHARS[buf[i] & 0x0F];
}
return new String(chars);
}
public static byte[] hexToBytes(String str) {
if (str==null) {
return null;
} else if (str.length() < 2) {
return null;
} else {
int len = str.length() / 2;
byte[] buffer = new byte[len];
for (int i=0; i<len; i++) {
buffer[i] = (byte) Integer.parseInt(str.substring(i*2,i*2+2),16);
}
return buffer;
}
}
private static String padString(String source)
{
char paddingChar = 0;
int size = 16;
int x = source.length() % size;
int padLength = size - x;
for (int i = 0; i < padLength; i++)
{
source += paddingChar;
}
return source;
}
}
However the two codes give me different results. Can anyone please explain why this might be.
I'm using PuTTYgen to generate RSA Keys and export them to the OpenSSH format. They then look like this
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,14EB795C6D5C665D
LIEmjggNbGCPMSR4XaoL3VEEbeG++Lwn/r/GjT0eGJbAaGBqH4jzpZ8ly6ZQ+2Pf
N4uLIsFgK+9a6rfM1cglvQ4IJjsw3740OFcUs4VgX4gOwqZJ6sKrrVIMCFqKfzcd
RsW9q1tFXVKXHO+OYaRY1SjGvQjZMYj1YG22njXFndmZfhC5HPba7UT7oA/p4Whu
XTbQ8r00qNjrdEG6CiK8cX2o6MITe9KITKhF/yc6snrpq222rP7P2CNGhvxLXlCI
K2KjuSHfQ5MBj9ohjEEykcyz+JYZ1E1QSR44JD025k5haC6oOYv/nnBnxAtSB6Vj
J9/ydIJUNpc9XNi3R0dS9D4wBkNUEdXMJTKkBK5zwu145soJ452YWjJ6dI/iYmic
27WRFuMln615QD0CAUUCRMFFQjA4uf/FDR+Im3glHArAWkFRElWOpuk9+ZzbcjYR
2T8ffqB80aTqCYoJ707Qc1l7VwRMBTaAk8ota0Iys2Z9PxqDDh1F+fS27GIbPNLc
2ZV12isUSOSYzyj/5R958vPMJ8ht00lCpDm81t7Yt6M3pBbUDq6QJG7fH9itrl/V
889IA2MZSt39lb/GzvMWRdraoKjw2OFSDu1Cjh7ScvMwzXF/sEI8d3Bf2bkudq/C
5t/h4Cczrlf7PQp85AvsZXL3xyGsFfvdEB0RzTg1andf55boka2ZWggJR9icW+Uh
6l9pAHfDDUaAXCJgVMV9p4IPtZyRp/OAd3vpBo5JrbpxFzoUDIIkkhhSQrANm6Hp
CtZJtQyR5OTHZUT0s3gqi+M9cgbnSEZjDPTuUzEorZWBa1fUX3+FsIMKAI7yNP8j
ALPLStDDtUJZVUtXjNdSZWH5qgsSa3xQq2a1iiaURKOMcDyELQgZbCrr+h78GSBA
XS9b3i5XkAA2g0UcVlmjY0Rm4gMbb2IUqVqGfpgs09ql60O2f6aTypz54ZvVz2f4
XrIGhlxabf+q8u6PFJNUJt33TDMSg1Y1epiu8gVngGqYIwgEJZusWYKSlS0zyzeB
FTVSFJ/7KbCmwRMQnV6gYQA5tIy1ka3Z0SRfbWH8Gu37HTd58RJmQhqXIbvZ+y9e
Irnc/mkkvsi5aaqj64/VyG1rejnssvztFMss7NjHQMr5B9exnfPaw15hU+iXnYGC
2taMxNTlxWOPW+ZTkNpCy2a2fz7URLTVRvk3U2IUqCNi4WqGxZRtJhGphdVnc6Jx
rX7KuvM2SOH0ZhxYiqlb/f0KjTEZ1XOUox/gAVmF015MFHurT66W3G8CB+zP9TVG
7DVQQRwWmSzxZOHC3+3Net2bRMoqpsdqXPRWllLBlby8f0nJMmciSbcyKYZ9lmPp
RNYuGCEKCzPNTtW0sxFKpY57odum8i7n+cW7OJbQ6ItxcEkxSvXybHOkUTXDaZK9
4qkl7Vnaw1YzGyWCW1B47XkrqFhc0Kt1XQHQsbfS9qDvt0Kh72FQLfvaJvTKLnI1
6YoEIP+x0NsbkvjfRvArfs/cdlngg1SMT18UstFqAgbVppnppGRfWti+neHRWsoj
SzvG+oJa72TMCdIekAhp1oLKDFM+jkb7CAZu+ocSpwjST4XJ0rhefA==
-----END RSA PRIVATE KEY-----
which is just an arbitrary SSH2-RSA key with the passphrase "test123" (w/o quotes).
When stored as a file, how can I read this information in Java to use it as
RSAPublicKey
and
RSAPrivateKey
Please try to be as detailed as possible, I know the very basics of cryptography but I'm far from being an expert...
You have to decode the OpenSSH key and the result will be a KeyPair
try {
String PEMPrivateKey = "...";
String password = "";
java.security.KeyPair key = PEMDecoder.decodeKeyPairOpenSSH(PEMPrivateKey, password);
java.security.interfaces.RSAPublicKey pub = (java.security.interfaces.RSAPublicKey)key.getPublic();
} catch (IOException e) {
// key not in new OpenSSH key format
}
Below is the PEM decoder from the trilead SSH project.
import java.io.BufferedReader;
import java.io.CharArrayReader;
import java.io.IOException;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import com.trilead.ssh2.crypto.cipher.AES;
import com.trilead.ssh2.crypto.cipher.BlockCipher;
import com.trilead.ssh2.crypto.cipher.CBCMode;
import com.trilead.ssh2.crypto.cipher.DES;
import com.trilead.ssh2.crypto.cipher.DESede;
import com.trilead.ssh2.crypto.digest.MD5;
import com.trilead.ssh2.signature.DSAPrivateKey;
import com.trilead.ssh2.signature.DidECKeyPair;
import com.trilead.ssh2.signature.ECDSAKeyAlgorithm;
import com.trilead.ssh2.signature.KeyAlgorithm;
import com.trilead.ssh2.signature.KeyAlgorithmManager;
import com.trilead.ssh2.signature.RSAPrivateKey;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.x9.ECNamedCurveTable;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.jcajce.provider.asymmetric.ec.KeyFactorySpi;
import org.bouncycastle.jce.spec.ECPrivateKeySpec;
import org.bouncycastle.jce.spec.ECPublicKeySpec;
import org.bouncycastle.math.ec.ECPoint;
/**
* PEM Support.
*
* #author Christian Plattner, plattner#trilead.com
* #version $Id: PEMDecoder.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
*/
public class PEMDecoder
{
private static final int PEM_RSA_PRIVATE_KEY = 1;
private static final int PEM_DSA_PRIVATE_KEY = 2;
private static final int PEM_EC_PRIVATE_KEY = 3;
private static final int hexToInt(char c)
{
if ((c >= 'a') && (c <= 'f'))
{
return (c - 'a') + 10;
}
if ((c >= 'A') && (c <= 'F'))
{
return (c - 'A') + 10;
}
if ((c >= '0') && (c <= '9'))
{
return (c - '0');
}
throw new IllegalArgumentException("Need hex char");
}
private static byte[] hexToByteArray(String hex)
{
if (hex == null)
throw new IllegalArgumentException("null argument");
if ((hex.length() % 2) != 0)
throw new IllegalArgumentException("Uneven string length in hex encoding.");
byte decoded[] = new byte[hex.length() / 2];
for (int i = 0; i < decoded.length; i++)
{
int hi = hexToInt(hex.charAt(i * 2));
int lo = hexToInt(hex.charAt((i * 2) + 1));
decoded[i] = (byte) (hi * 16 + lo);
}
return decoded;
}
private static byte[] generateKeyFromPasswordSaltWithMD5(byte[] password, byte[] salt, int keyLen)
throws IOException
{
if (salt.length < 8)
throw new IllegalArgumentException("Salt needs to be at least 8 bytes for key generation.");
MD5 md5 = new MD5();
byte[] key = new byte[keyLen];
byte[] tmp = new byte[md5.getDigestLength()];
while (true)
{
md5.update(password, 0, password.length);
md5.update(salt, 0, 8); // ARGH we only use the first 8 bytes of the
// salt in this step.
// This took me two hours until I got AES-xxx running.
int copy = (keyLen < tmp.length) ? keyLen : tmp.length;
md5.digest(tmp, 0);
System.arraycopy(tmp, 0, key, key.length - keyLen, copy);
keyLen -= copy;
if (keyLen == 0)
return key;
md5.update(tmp, 0, tmp.length);
}
}
private static byte[] removePadding(byte[] buff, int blockSize) throws IOException
{
/* Removes RFC 1423/PKCS #7 padding */
int rfc_1423_padding = buff[buff.length - 1] & 0xff;
if ((rfc_1423_padding < 1) || (rfc_1423_padding > blockSize))
throw new IOException("Decrypted PEM has wrong padding, did you specify the correct password?");
for (int i = 2; i <= rfc_1423_padding; i++)
{
if (buff[buff.length - i] != rfc_1423_padding)
throw new IOException("Decrypted PEM has wrong padding, did you specify the correct password?");
}
byte[] tmp = new byte[buff.length - rfc_1423_padding];
System.arraycopy(buff, 0, tmp, 0, buff.length - rfc_1423_padding);
return tmp;
}
private static final PEMStructure parsePEM(char[] pem) throws IOException
{
PEMStructure ps = new PEMStructure();
String line = null;
BufferedReader br = new BufferedReader(new CharArrayReader(pem));
String endLine = null;
while (true)
{
line = br.readLine();
if (line == null)
throw new IOException("Invalid PEM structure, '-----BEGIN...' missing");
line = line.trim();
if (line.startsWith("-----BEGIN DSA PRIVATE KEY-----"))
{
endLine = "-----END DSA PRIVATE KEY-----";
ps.pemType = PEM_DSA_PRIVATE_KEY;
break;
}
if (line.startsWith("-----BEGIN RSA PRIVATE KEY-----"))
{
endLine = "-----END RSA PRIVATE KEY-----";
ps.pemType = PEM_RSA_PRIVATE_KEY;
break;
}
if (line.startsWith("-----BEGIN EC PRIVATE KEY-----"))
{
endLine = "-----END EC PRIVATE KEY-----";
ps.pemType = PEM_EC_PRIVATE_KEY;
break;
}
}
while (true)
{
line = br.readLine();
if (line == null)
throw new IOException("Invalid PEM structure, " + endLine + " missing");
line = line.trim();
int sem_idx = line.indexOf(':');
if (sem_idx == -1)
break;
String name = line.substring(0, sem_idx + 1);
String value = line.substring(sem_idx + 1);
String values[] = value.split(",");
for (int i = 0; i < values.length; i++)
values[i] = values[i].trim();
// Proc-Type: 4,ENCRYPTED
// DEK-Info: DES-EDE3-CBC,579B6BE3E5C60483
if ("Proc-Type:".equals(name))
{
ps.procType = values;
continue;
}
if ("DEK-Info:".equals(name))
{
ps.dekInfo = values;
continue;
}
/* Ignore line */
}
StringBuffer keyData = new StringBuffer();
while (true)
{
if (line == null)
throw new IOException("Invalid PEM structure , " + new String(pem) + endLine + " missing");
line = line.trim();
if (line.startsWith(endLine))
break;
keyData.append(line);
line = br.readLine();
}
char[] pem_chars = new char[keyData.length()];
keyData.getChars(0, pem_chars.length, pem_chars, 0);
ps.data = Base64.decode(pem_chars);
if (ps.data.length == 0)
throw new IOException("Invalid PEM structure, no data available");
return ps;
}
private static PEMStructure parsePEM(char[] pem, CertificateDecoder certificateDecoder) throws IOException
{
PEMStructure ps = new PEMStructure();
String line;
BufferedReader br = new BufferedReader(new CharArrayReader(pem));
String endLine;
while (true)
{
line = br.readLine();
if (line == null)
throw new IOException("Invalid PEM structure, '-----BEGIN...' missing");
line = line.trim();
if (line.startsWith(certificateDecoder.getStartLine()))
{
endLine = certificateDecoder.getEndLine();
break;
}
}
while (true)
{
line = br.readLine();
if (line == null)
throw new IOException("Invalid PEM structure, " + endLine + " missing");
line = line.trim();
int sem_idx = line.indexOf(':');
if (sem_idx == -1)
break;
String name = line.substring(0, sem_idx + 1);
String value = line.substring(sem_idx + 1);
String values[] = value.split(",");
for (int i = 0; i < values.length; i++)
values[i] = values[i].trim();
// Proc-Type: 4,ENCRYPTED
// DEK-Info: DES-EDE3-CBC,579B6BE3E5C60483
if ("Proc-Type:".equals(name))
{
ps.procType = values;
continue;
}
if ("DEK-Info:".equals(name))
{
ps.dekInfo = values;
continue;
}
/* Ignore line */
}
StringBuilder keyData = new StringBuilder();
while (true)
{
if (line == null)
throw new IOException("Invalid PEM structure, " + endLine + " missing");
line = line.trim();
if (line.startsWith(endLine))
break;
keyData.append(line);
line = br.readLine();
}
char[] pem_chars = new char[keyData.length()];
keyData.getChars(0, pem_chars.length, pem_chars, 0);
ps.data = Base64.decode(pem_chars);
if (ps.data.length == 0)
throw new IOException("Invalid PEM structure, no data available");
return ps;
}
private static final void decryptPEM(PEMStructure ps, byte[] pw) throws IOException
{
if (ps.dekInfo == null)
throw new IOException("Broken PEM, no mode and salt given, but encryption enabled");
if (ps.dekInfo.length != 2)
throw new IOException("Broken PEM, DEK-Info is incomplete!");
String algo = ps.dekInfo[0];
byte[] salt = hexToByteArray(ps.dekInfo[1]);
BlockCipher bc = null;
if (algo.equals("DES-EDE3-CBC"))
{
DESede des3 = new DESede();
des3.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 24));
bc = new CBCMode(des3, salt, false);
}
else if (algo.equals("DES-CBC"))
{
DES des = new DES();
des.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 8));
bc = new CBCMode(des, salt, false);
}
else if (algo.equals("AES-128-CBC"))
{
AES aes = new AES();
aes.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 16));
bc = new CBCMode(aes, salt, false);
}
else if (algo.equals("AES-192-CBC"))
{
AES aes = new AES();
aes.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 24));
bc = new CBCMode(aes, salt, false);
}
else if (algo.equals("AES-256-CBC"))
{
AES aes = new AES();
aes.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 32));
bc = new CBCMode(aes, salt, false);
}
else
{
throw new IOException("Cannot decrypt PEM structure, unknown cipher " + algo);
}
if ((ps.data.length % bc.getBlockSize()) != 0)
throw new IOException("Invalid PEM structure, size of encrypted block is not a multiple of "
+ bc.getBlockSize());
/* Now decrypt the content */
byte[] dz = new byte[ps.data.length];
for (int i = 0; i < ps.data.length / bc.getBlockSize(); i++)
{
bc.transformBlock(ps.data, i * bc.getBlockSize(), dz, i * bc.getBlockSize());
}
/* Now check and remove RFC 1423/PKCS #7 padding */
dz = removePadding(dz, bc.getBlockSize());
ps.data = dz;
ps.dekInfo = null;
ps.procType = null;
}
public static final boolean isPEMEncrypted(PEMStructure ps) throws IOException
{
if (ps.procType == null)
return false;
if (ps.procType.length != 2)
throw new IOException("Unknown Proc-Type field.");
if ("4".equals(ps.procType[0]) == false)
throw new IOException("Unknown Proc-Type field (" + ps.procType[0] + ")");
if ("ENCRYPTED".equals(ps.procType[1]))
return true;
return false;
}
public static Object decode(char[] pem, String password) throws IOException
{
PEMStructure ps = parsePEM(pem);
if (isPEMEncrypted(ps))
{
if (password == null)
throw new IOException("PEM is encrypted, but no password was specified");
decryptPEM(ps, password.getBytes("ISO-8859-1"));
}
if (ps.pemType == PEM_DSA_PRIVATE_KEY)
{
SimpleDERReader dr = new SimpleDERReader(ps.data);
byte[] seq = dr.readSequenceAsByteArray();
if (dr.available() != 0)
throw new IOException("Padding in DSA PRIVATE KEY DER stream.");
dr.resetInput(seq);
BigInteger version = dr.readInt();
if (version.compareTo(BigInteger.ZERO) != 0)
throw new IOException("Wrong version (" + version + ") in DSA PRIVATE KEY DER stream.");
BigInteger p = dr.readInt();
BigInteger q = dr.readInt();
BigInteger g = dr.readInt();
BigInteger y = dr.readInt();
BigInteger x = dr.readInt();
if (dr.available() != 0)
throw new IOException("Padding in DSA PRIVATE KEY DER stream.");
return new DSAPrivateKey(p, q, g, y, x);
}
if (ps.pemType == PEM_RSA_PRIVATE_KEY)
{
SimpleDERReader dr = new SimpleDERReader(ps.data);
byte[] seq = dr.readSequenceAsByteArray();
if (dr.available() != 0)
throw new IOException("Padding in RSA PRIVATE KEY DER stream.");
dr.resetInput(seq);
BigInteger version = dr.readInt();
if ((version.compareTo(BigInteger.ZERO) != 0) && (version.compareTo(BigInteger.ONE) != 0))
throw new IOException("Wrong version (" + version + ") in RSA PRIVATE KEY DER stream.");
BigInteger n = dr.readInt();
BigInteger e = dr.readInt();
BigInteger d = dr.readInt();
return new RSAPrivateKey(d, e, n);
}
if (ps.pemType == PEM_EC_PRIVATE_KEY)
{
SimpleDERReader DERderReader = new SimpleDERReader(ps.data);
byte[] sequence = DERderReader.readSequenceAsByteArray();
if (DERderReader.available() != 0) {
throw new IOException("Unexpected padding in EC private key");
}
SimpleDERReader sequenceReader = new SimpleDERReader(sequence);
BigInteger version = sequenceReader.readInt();
if ((version.compareTo(BigInteger.ONE) != 0)) {
throw new IOException("Unexpected version number in EC private key: " + version);
}
byte[] privateBytes = sequenceReader.readOctetString();
String curveOid = null;
byte[] publicBytes = null;
while (sequenceReader.available() > 0) {
int type = sequenceReader.readConstructedType();
SimpleDERReader fieldReader = sequenceReader.readConstructed();
switch (type) {
case 0:
curveOid = fieldReader.readOid();
break;
case 1:
publicBytes = fieldReader.readOctetString();
break;
}
}
ASN1ObjectIdentifier asn1 = new ASN1ObjectIdentifier(curveOid);
X9ECParameters x9 = ECNamedCurveTable.getByOID(asn1);
BigInteger s = new BigInteger(1, privateBytes);
byte[] publicBytesSlice = new byte[publicBytes.length - 1];
System.arraycopy(publicBytes, 1, publicBytesSlice, 0, publicBytesSlice.length);
ECPoint w = ECDSAKeyAlgorithm.decodePoint(publicBytesSlice, x9.getCurve());
didisoft.sftp.bouncycastle.math.ec.ECCurve curve = x9.getCurve();
ECPrivateKeySpec privSpec = new ECPrivateKeySpec(s, new didisoft.sftp.bouncycastle.jce.spec.ECParameterSpec(curve, x9.getG(), x9.getN()));
ECPublicKeySpec pubSpec = new ECPublicKeySpec(w, new didisoft.sftp.bouncycastle.jce.spec.ECParameterSpec(curve, x9.getG(), x9.getN()));
try {
// BC hack because jce-impl KeyFactorySpi.generatePublic doesn't support java.security.spec.ECPublicKeySpec !
// KeyFactory factory = KeyFactory.getInstance("EC");
KeyFactorySpi.ECDSA factory = new KeyFactorySpi.ECDSA();
PublicKey ecPublicKey = factory.engineGeneratePublic(pubSpec);
PrivateKey ecPrivateKey = factory.engineGeneratePrivate(privSpec);
return new DidECKeyPair(ECNamedCurveTable.getName(asn1), ecPublicKey, ecPrivateKey);
} catch (InvalidKeySpecException ex) {
throw new IOException("Could not generate EC key pair: " + ex.getMessage());
}
}
throw new IOException("PEM problem: it is of unknown type");
}
public static Object decodeKeyPairOpenSSH(char[] pem, String password) throws IOException
{
KeyPair key = decodeKeyPair(pem, password);
if (key.getPrivate() instanceof java.security.interfaces.RSAPrivateKey) {
java.security.interfaces.RSAPrivateCrtKey rsa = (java.security.interfaces.RSAPrivateCrtKey)key.getPrivate();
return new RSAPrivateKey(rsa.getPrivateExponent(), rsa.getPublicExponent(), rsa.getModulus());
} else if (key.getPrivate() instanceof java.security.interfaces.DSAPrivateKey) {
java.security.interfaces.DSAPublicKey dsaPub = (java.security.interfaces.DSAPublicKey)key.getPublic();
java.security.interfaces.DSAPrivateKey dsaPriv = (java.security.interfaces.DSAPrivateKey)key.getPrivate();
return new DSAPrivateKey(dsaPub.getParams().getP(),
dsaPub.getParams().getQ(),
dsaPub.getParams().getG(),
dsaPub.getY(),
dsaPriv.getX());
} else {
return null;
}
}
/**
* Decodes keys in the new OpenSSH format
* #param pem
* #param password
* #return
* #throws IOException
*/
public static KeyPair decodeKeyPair(char[] pem, String password) throws IOException
{
for (KeyAlgorithm<PublicKey, PrivateKey> algorithm : KeyAlgorithmManager.getSupportedAlgorithms()) {
for (CertificateDecoder decoder : algorithm.getCertificateDecoders()) {
try {
PEMStructure ps = parsePEM(pem, decoder);
if (isPEMEncrypted(ps)) {
if (password == null)
throw new IOException("PEM is encrypted, but no password was specified");
decryptPEM(ps, password.getBytes("ISO-8859-1"));
}
return decoder.createKeyPair(ps, password);
} catch (IOException ex) {
//LOGGER.log(Level.FINE, "Could not decode PEM Key using current decoder: " + decoder.getClass().getName(), ex);
// we couldn't decode the input, try another decoder
}
}
}
throw new IOException("PEM problem: it is of unknown type");
}
}
I have an encryption/decryption algorithm and I'm trying to encrypt some special characters to send to the server from android, but it is throwing an exception:
java.lang.Exception: [encrypt] error:0607F08A:digital envelope routines:EVP_EncryptFinal_ex:data not multiple of block length
Normal Latin characters are encrypted/decrypted fine, but special characters aren't.
Here is my code:
public class MCrypt {
private String iv = "fedcba9876543210";//Dummy iv (CHANGE IT!)
private IvParameterSpec ivspec;
private SecretKeySpec keyspec;
private Cipher cipher;
private String SecretKey = "0123456789abcdef";//Dummy secretKey (CHANGE IT!)
public MCrypt()
{
ivspec = new IvParameterSpec(iv.getBytes());
keyspec = new SecretKeySpec(SecretKey.getBytes(), "AES");
try {
cipher = Cipher.getInstance("AES/CBC/NoPadding");
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public byte[] encrypt(String text) throws Exception
{
//if(text == null || text.length() == 0)
// throw new Exception("Empty string");
byte[] encrypted = null;
try {
cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
encrypted = cipher.doFinal(padString(text).getBytes());
} catch (Exception e)
{
throw new Exception("[encrypt] " + e.getMessage());
}
return encrypted;
}
public byte[] decrypt(String code) throws Exception
{
if(code == null || code.length() == 0)
throw new Exception("Empty string");
byte[] decrypted = null;
try {
cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
decrypted = cipher.doFinal(hexToBytes(code));
} catch (Exception e)
{
throw new Exception("[decrypt] " + e.getMessage());
}
return decrypted;
}
public static String bytesToHex(byte[] data)
{
if (data==null)
{
return null;
}
int len = data.length;
String str = "";
for (int i=0; i<len; i++) {
if ((data[i]&0xFF)<16)
str = str + "0" + Integer.toHexString(data[i]&0xFF);
else
str = str + Integer.toHexString(data[i]&0xFF);
}
return str;
}
public static byte[] hexToBytes(String str) {
if (str==null) {
return null;
} else if (str.length() < 2) {
return null;
} else {
int len = str.length() / 2;
byte[] buffer = new byte[len];
for (int i=0; i<len; i++) {
buffer[i] = (byte) Integer.parseInt(str.substring(i*2,i*2+2),16);
}
return buffer;
}
}
private static String padString(String source)
{
char paddingChar = ' ';
int size = 32;
int x = source.length() % size;
int padLength = size - x;
for (int i = 0; i < padLength; i++)
{
source += paddingChar;
}
return source;
}
}
Does anyone know what the problem here is?
Thanks!
Change this line:
cipher = Cipher.getInstance("AES/CBC/NoPadding");
To
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
From this Blog
Change this line :
cipher = Cipher.getInstance("AES/CBC/NoPadding"); //this may be not decrypt full plaintext.
To:
cipher = Cipher.getInstance("AES/ECB/PKCS7Padding"); // use this for descrypt full plain text without lost.
In the code below I'm initializing the security key from an external class. Is it necessary to use the security key length of 16? Any possibility to use the smaller length byte?
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class MCrypt {
private String iv = "fedcba9876543210";
private IvParameterSpec ivspec;
private SecretKeySpec keyspec;
private Cipher cipher;
private String SecretKey ;
public MCrypt(String s)
{
SecretKey=s;
ivspec = new IvParameterSpec(iv.getBytes());
keyspec = new SecretKeySpec(SecretKey.getBytes(), "AES");
try {
cipher = Cipher.getInstance("AES/CBC/NoPadding");
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public byte[] encrypt(String text) throws Exception
{
if(text == null || text.length() == 0)
throw new Exception("Empty string");
byte[] encrypted = null;
try {
cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
encrypted = cipher.doFinal(padString(text).getBytes());
} catch (Exception e)
{
throw new Exception("[encrypt] " + e.getMessage());
}
return encrypted;
}
public byte[] decrypt(String code) throws Exception
{
if(code == null || code.length() == 0)
throw new Exception("Empty string");
byte[] decrypted = null;
try {
cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
decrypted = cipher.doFinal(hexToBytes(code));
} catch (Exception e)
{
throw new Exception("[decrypt] " + e.getMessage());
}
return decrypted;
}
public static String bytesToHex(byte[] data)
{
if (data==null)
{
return null;
}
int len = data.length;
String str = "";
for (int i=0; i<len; i++) {
if ((data[i]&0xFF)<16)
str = str + "0" + java.lang.Integer.toHexString(data[i]&0xFF);
else
str = str + java.lang.Integer.toHexString(data[i]&0xFF);
}
return str;
}
public static byte[] hexToBytes(String str) {
if (str==null) {
return null;
} else if (str.length() < 2) {
return null;
} else {
int len = str.length() / 2;
byte[] buffer = new byte[len];
for (int i=0; i<len; i++) {
buffer[i] = (byte) Integer.parseInt(str.substring(i*2,i*2+2),16);
}
return buffer;
}
}
private static String padString(String source)
{
char paddingChar = ' ';
int size = 16;
int x = source.length() % size;
int padLength = size - x;
for (int i = 0; i < padLength; i++)
{
source += paddingChar;
}
return source;
}
}
Your key must match one of the permitted lengths for the AES algorithm: 16, 24 or 32 bytes. You cannot use a key smaller than 16 bytes.
Some other comments on your code:
Use camelCase for all variables (e.g. secretKey).
Avoid using a fixed IV. Generate a random IV each time and store it with the ciphertext.
Consider using a padding mode (e.g. AES/CBC/PKCS5Padding) otherwise your input must always be a multiple of 16 bytes.
You are calling getBytes() on a string without supplying a charset. This could result in different results on different platforms. Always specify a character set.
In addition to Duncan's suggested improvements:
Never ever use a password/String directly as a key!
If you want to create a key based on a password use a "Password based key derivation function" - e.g. the standard is PBKDF2 (see for example this question: PBKDF2 with bouncycastle in Java).
I am using following code block to generate MD5 hashes:
public static String encode(String data) throws Exception {
/* Check the validity of data */
if (data == null || data.isEmpty()) {
throw new IllegalArgumentException("Null value provided for "
+ "MD5 Encoding");
}
/* Get the instances for a given digest scheme MD5 or SHA */
MessageDigest m = MessageDigest.getInstance("MD5");
/* Generate the digest. Pass in the text as bytes, length to the
* bytes(offset) to be hashed; for full string pass 0 to text.length()
*/
m.update(data.getBytes(), 0, data.length());
/* Get the String representation of hash bytes, create a big integer
* out of bytes then convert it into hex value (16 as input to
* toString method)
*/
String digest = new BigInteger(1, m.digest()).toString(16);
return digest;
}
When I run the above code segment with String data as [12, B006GQIIEM, MH-ANT2000], the output is a 31 character hash - 268d43a823933c9dafaa4ac0e756d6a.
Is there any problem with the MD5 hash function or there is some problem in the code above?
The only issue in your code is when MSB is less than Ox10, the result hash string will only have 31 bytes, instead of 32 bytes, missing the leading zero.
Create your md5 string in this way:
byte messageDigest[] = m.digest();
hexString = new StringBuffer();
for (int i=0;i<messageDigest.length;i++) {
String hex=Integer.toHexString(0xFF & messageDigest[i]);
if(hex.length()==1)
hexString.append('0');
hexString.append(hex);
}
You can try this:
...
String digest = String.format("%032x", new BigInteger(1, m.digest()));
Note: it is "%032x", not "%32x".
This is how I use MD5 hash. Calculate MD5 hash from string and return 32-byte hexadecimal representation.
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class MySimpleMD5 {
private static String convertToHex(byte[] data) {
StringBuffer buf = new StringBuffer();
for (int i = 0; i < data.length; i++) {
int halfbyte = (data[i] >>> 4) & 0x0F;
int two_halfs = 0;
do {
if ((0 <= halfbyte) && (halfbyte <= 9))
buf.append((char) ('0' + halfbyte));
else
buf.append((char) ('a' + (halfbyte - 10)));
halfbyte = data[i] & 0x0F;
} while(two_halfs++ < 1);
}
return buf.toString();
}
public static String MD5(String text)
throws NoSuchAlgorithmException, UnsupportedEncodingException {
MessageDigest md;
md = MessageDigest.getInstance("MD5");
byte[] md5hash = new byte[32];
md.update(text.getBytes("iso-8859-1"), 0, text.length());
md5hash = md.digest();
return convertToHex(md5hash);
}
}
You can also try this:
private static String getMd5Hash(String input) throws NoSuchAlgorithmException {
MessageDigest m = MessageDigest.getInstance("MD5");
byte[] data = m.digest(EncodingUtils.getBytes(input, "UTF8"));
StringBuilder sBuilder = new StringBuilder();
for (int i = 0; i < data.length; i++) {
for (byte b : data) {
if(b == 0x00){
sBuilder.append("00");
} else if ((b & 0x0F) == b) {
sBuilder.append("0");
break;
} else {
break;
}
}
BigInteger bigInt = new BigInteger(1, data);
sBuilder.append(bigInt.toString(16));
}
// Return the hexadecimal string.
return sBuilder.toString().substring(0, 32);
}