How can I reverse this output from this method? (So that I get to see the plaintext)
I don't see any keys being used.
public String encrypt(String plaintext) {
MessageDigest md = null;
try {
md = MessageDigest.getInstance("SHA");
} catch (NoSuchAlgorithmException e) {
}
try {
md.update(plaintext.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
}
byte raw[] = md.digest();
String hash = (new BASE64Encoder()).encode(raw);
return hash;
}
You can't. The SHA family are hash algorithms; that is, they are one-way cryptographic algorithms producing a hash or message digest. There is no way back. A hash is a fixed length result from a message of any length, with the design aim that it is computationally infeasible to find two messages that produce the same hash value; thus, to all intents, the hash can be seen as identifying the message. But you can't go back and retrieve the message from it.
Hashes are unkeyed. There are keyed constructs that can be made with them, for example a message authentication code or MAC.
The method is actually poorly named - this is not "encryption" per se.
Related
This question already has answers here:
Converting Secret Key into a String and Vice Versa
(6 answers)
Converted SecretKey into bytes, how to convert it back to a SecretKey?
(2 answers)
Closed 2 years ago.
What I am trying to do is encrypt a string into a byte[] with javax.crypto, send it through a DatagramSocket, then on the receiving side, decrypt it.
public static final String UNICODE = "UTF-8";
private SecretKey key;
private Cipher cipher;
public StringHandler() {
try {
key = generateKey("AES");
cipher = Cipher.getInstance("AES");
} catch (Exception e) {
e.printStackTrace();
}
}
private SecretKey generateKey(String type) throws Exception {
KeyGenerator gen = KeyGenerator.getInstance(type);
SecretKey key = gen.generateKey();
return key;
}
public byte[] encrypt(String msg) {
try {
byte[] data = msg.getBytes(UNICODE);
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(data);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public String decrypt(byte[] data) {
try {
cipher.init(Cipher.DECRYPT_MODE, key);
return new String(cipher.doFinal(data), UNICODE);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
I read on a website how to encrypt and decrypt data, and I wrote this class. From what I can tell, the security key has to be the same on both sides for it to decrypt properly. Is there any way to convert it to a string or something, then get it from a string on the server side? Currently I have no idea how decrypt it on a different program.
You can turn a key represented as an array of bytes into a form that can be sent as "text". Base64 encoding is a common way to do that.
But that doesn't solve your real problem:
Is there any way to convert it to a string or something, then get it from a string on the server side?
The real problem is how to send the string that represents to secret key to the server securely; i.e. without someone else being able to steal the key while it is in transit.
And the answer is that you can't ... without using another encryption mechanism:
One possibility to encrypt the secret key with a different secret key that both the client and server already know.
A second possibility is to use public key encryption.
A third possibility is to use a secure communication system to transmit the secret key. For example a network based on quantum cryptography.
This is far too large a topic to cover here. If you want to understand the "key distribution problem" and its solutions, find a good textbook. Or start with these Wikipedia articles:
https://en.wikipedia.org/wiki/Cryptography
https://en.wikipedia.org/wiki/Public-key_cryptography
https://en.wikipedia.org/wiki/Quantum_key_distribution
Thanks the comments from everyone.
However, I have to explain this question.
I know that we shouldn't compare the result of encryption by using a fix random generator due to it may reduce the secure. However, I just want to do it in testing and I'll use a original mechanism of random in real running.
The case is that I need to login a server with account/password by the following steps:
Get info from server: a.b.com/get_cipher.cgi.
receive the response, parse it and get some info to create a cipher.
use the cipher to encrypt account/password and then compose the following URL a.b.com/login.cgi?encrypted={encrypted_account_password}
It's a complex process and I can't request the server to change the protocal. I want to test the whole login process. Thus, I tried to provide fake account/password and check if the generated url is correct without deciphering the result (if deciphering the result, it means that, in this test case, I need to decipher the the encrypted result, parse the url, and extract the related info, there are too much not related to the testing. Moreover, if I do some change in the login process, I may need to modify the deciphering and parsing process in the test cases.)
That means a hash function is not proper for me. (The original login process don't use any hash so that I don't want to test it in a test cases. Moreover, even thought I check the hash result is correct, it doesn't prove the login process is correct.)
=== The original question is as the following ===
I have a program which needs login. To void transfer the password in plain text on the network, I need to encipher it. In other word, the login process contains a ciphering phase.
Then, I want to write a test case for the whole login process. I think the encryption result of be the same if it use the same account and password.
Since it may use SecureRandom in the encryption process, I write a fake SecureRandom by Mockito as the following code:
private static final long RANDOM_SEED = 3670875202692512518L;
private Random generateRandomWithFixSeed() {
Random random = new Random(RANDOM_SEED);
return random;
}
private SecureRandom generateSecureRandomWithFixSeed() {
final Random random = generateRandomWithFixSeed();
final SecureRandom secureRandom = new SecureRandom();
final SecureRandom spySecureRandom = Mockito.spy(secureRandom);
Mockito.doAnswer(new Answer<Object>() {
#Override
public Object answer(InvocationOnMock invocation) throws Throwable {
Object[] args = invocation.getArguments();
byte[] bytes = (byte[]) args[0];
random.nextBytes(bytes);
return bytes;
}
})
.when(spySecureRandom)
.nextBytes(Matchers.<byte[]>anyObject());
return spySecureRandom;
}
#Test
public void test_SecureRandom_WithFixSeed() {
final SecureRandom secureRandom1 = generateSecureRandomWithFixSeed();
final SecureRandom secureRandom2 = generateSecureRandomWithFixSeed();
final byte[] bytes1 = new byte[20];
final byte[] bytes2 = new byte[20];
secureRandom1.nextBytes(bytes1);
secureRandom2.nextBytes(bytes2);
boolean isTheSameSeries = true;
for(int i = 0; i < 20; i++) {
isTheSameSeries &= (bytes1[i]==bytes2[i]);
}
assertThat(isTheSameSeries, is(true));
}
The generateRandomWithFixSeed() will new a Random with the same key, so that it will generate the same result. The generateSecureRandomWithFixSeed() uses Makito to detect the function call nextBytes() and always answer the result of the random. the test test_SecureRandom_WithFixSeed() also show two different SecureRandom instances generate the same results.
However, if I use generateSecureRandomWithFixSeed() in the cipher as the following, it can't always return the same result.
#Test
public void test_cipher() {
final SecureRandom secureRandomWithFixSeed = generateSecureRandomWithFixSeed();
final String pkcs = "MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAv7n+/uWHHVC7229QLEObeH0vUcOagavDukf/gkveqgZsszzGkZQaXfsrjdPiCnvjozCy1tbnLu5EInDy4w8B+a9gtK8KqsvlsfuaT9kRSMUS8CfgpWj8JcJwijmeZhjR52k0UBpWLfn3JmRYW8xjZW6dlOSnS0yqwREPU7myyqUzhk1vyyUG7wLpk7uK9Bxmup0tnbnD4MeqDboAXlQYnIFVV+CXywlAQfHHCfQRsGhsEtu4excZVw7FD1rjnro9bcWFY9cm/KdDBxZCYQoT/UW0OBbipoINycrmfMKt1r4mGE9/MdVoIEMBc54aI6sb2g5J2GtNCYfEu+1/gA99xY0+5B3ydH74cbqfHYOZIvu11Q7GnpZ6l8zTLlMuF/pvlSily76I45H0YZ3HcdQnf/GoKC942P6fNsynHEX01zASYM8dzyMxHQpNEx7fcXGi+uiBUD/Xdm2jwzr9ZEP5eEVlrpcAvr8c9S5ylE50lwR+Mp3vaZxPoLdSGZrfyXy4v97UZSnYARQBacmn6KgsIHIOKhYOxNgUG0jwCO/zrPvlbjiYTHQYLOCcldTULvXOdn51enJFGVjscGoZfRj6vZgyHVCUW4iip4iSbSKPcPbf0GMZuniS9qJ3Wybve0/xpppdOv1c40ez0NKQyQkEZRb+V0qfesatJKZd/hUGr+MCAwEAAQ==";
final byte bytePKCS[] = Base64.base64ToByteArray(pkcs);
final X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(bytePKCS);
PublicKey pubKey = null;
try {
pubKey = KeyFactory.getInstance("RSA").generatePublic(pubKeySpec);
} catch (InvalidKeySpecException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
final String targetResultText = "NZqTzuNli92vuXEQNchGeF6faN/NBHykhfqBFcWzBHZhbgljZaWAcAzasFSm/odZZ6vBD2jK7J4oi5BmDjxNdEjkXyv3OZ2sOTLCfudgPwXcXmmhOwWHDLY02OX0X3RwBHzqWczqAd4dwslo59Gp5CT59GWXenJPL8wvG90WH2XAKOmHg5uEZj55ZvErRQ6StPVzLkiNCMPOhga7FZWK/rSEpT6BHDy3CibDZ0PNRtAW4wiYAr0Cw6brqiqkN301Bz6DzrV5380KDHkw26GjM8URSTFekwvZ7FISQ72UaNHhjnh1WgMIPf/QDbrEh5b+rmdZjzc5bdjyONrQuoj0rzrWLN4z8lsrBnKFVo+zVyUeqr0IxqD2aHDLyz5OE4fb5IZJHEMfYr/R79Zfe8IuQ2tusA02ZlFzGRGBhAkb0VygXxJxPXkjbkPaLbZQZOsKLUoIDkcbNoUTxeS9+4LWVg1j5q3HR9OSvmsF5I/SszvVrnXdNaz1IKCfVYkwpIBQ+Y+xI/K360dWIHR/vn7TU4UsGmWtwVciq0jWLuBN/qRE6MV47TDRQu63GzeV00yAM/xUM33qWNXCV1tbGXNZw8jHpakgflTY0hcjOFHPpq2UfJCyxiSBtZ0b7hw9Rvhi8VwYc243jXO9CvGq+J6JYvchvKHjq2+YKn1UB2+gs20=";
final String plainText = "a";
String resultText = "";
try {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pubKey, secureRandomWithFixSeed);
final byte[] result = cipher.doFinal(plainText.getBytes());
resultText = Base64.byteArrayToBase64(result);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
}
assertThat(resultText, is(targetResultText));
}
aa
You shouldn't do what you're trying to do. That is not the point of encryption to be able to compare two encrypted values to determine if they're the same. I'm sure you CAN get this to work, but you'd effectively be disabling features and making everything you encrypt less secure in order to make them show up the same.
If you want to be able to compare the two values without decrypting the password, what you really want is a hash function. Specifically, take a look at using SHA1 at least, or SHA-256 (Better).
How to hash some string with sha256 in Java?
By design, a hash is one-way (e.g. You can't reverse the password without something like a Rainbow Table). However, its designed to be used exactly how you're describing, comparing the new value with the old.
If you really want to work with encrypted values, you should be decrypting the password value and comparing it against the plain-text. However, hashing follows best practices.
rather simple answer to your explanations, with only hash functions (no cipher needed):
1 share a secret between server and client. this must be done before. Any string does the job (you can see it as a password).
Client has P, Server has P
1bis better security: hash the P, at both sides: Client and Server have Ph
2 connection:
2a server create random R, and send to client
2b client make Ph x R (bit at bit for example) and hash it => (Ph x R)h
and sends it
2c server can do the same thing: (Ph x R)h and compares it
One weakness : if you get Ph at the server, you can spoof the client. To avoid that, you should use other functions.
Other option (more secure if you dont trust the server): use an asymetric key and RSA as in your original code.
I've found the reason.
I found that if I run the test case on Android 4.4, it can't work but it works on 4.1.2.
By more survey, it seems that Android use OpenSSL for encryption/description in newer version (AndroidOpenSSL).
In the file, external/conscrypt/src/main/java/org/conscrypt/OpenSSLCipherRSA.java
#Override
protected void engineInit(int opmode, Key key, SecureRandom random) throws InvalidKeyException {
engineInitInternal(opmode, key);
}
It shows that it use native OpenSSL library to encipher and don't use the given SecureRandom.
The solution is to provider the Provider when Cipher.getInstance().
#Test
public void test_cipher() {
private static final String PROVIDER_NAME = "BC";
final Provider provider = Security.getProvider(PROVIDER_NAME);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", provider);
}
I need to calculate the SHA 256 for my password.
i already know that I can user the common codec from apache but this is not allowed in where i am working
I tried to make a simple function to return the sha 256 from a plain text, which is:
public static String getSHA1(String plainText) {
MessageDigest md;
try {
md = MessageDigest.getInstance("SHA-256");
md.update(plainText.getBytes());
StringBuffer hexString = new StringBuffer();
for (int i = 0; i < md.digest().length; i++) {
hexString.append(Integer.toHexString(0xFF & md.digest()[i]));
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
my problem is whatever the input is, the result is the same. i always got this result
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
I can calculate the sha 256 online using this website http://onlinemd5.com/
but i need to calculate it from my code.
your help is appreciated and lovely.
From the Javadoc for digest():
Completes the hash computation by performing final operations such as padding. The digest is reset after this call is made.
Call digest() once and put the results into a variable.
(By the way, had you searched for the digest, which is always a good idea whenever you get a fixed result, you would have seen that it's the SHA-256 digest for the empty string.)
I'm trying to create a very simple key exchange in Java. After the code and output there are questions:
public class Blergh {
public static KeyPair genKeyPair512() {
try {
AlgorithmParameterGenerator paramGen = AlgorithmParameterGenerator
.getInstance("DH");
paramGen.init(512);
BigInteger g = new BigInteger(
"7961C6D7913FDF8A034593294FA52D6F8354E9EDFE3EDC8EF082D36662D69DFE8CA7DC7480121C98B9774DFF915FB710D79E1BCBA68C0D429CD6B9AD73C0EF20",
16);
BigInteger p = new BigInteger(
"00AC86AB9A1F921B251027BD10B93D0A8D9A260364974648E2543E8CD5C48DB4FFBEF0C3843465BA8DE20FFA36FFAF840B8CF26C9EB865BA184642A5F84606AEC5",
16);
final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DH");
final DHParameterSpec dhSpec = new DHParameterSpec(p, g, 511);
keyGen.initialize(dhSpec);
return keyGen.generateKeyPair();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
return null;
}
public static byte[] genSharedSecretKey(KeyPair keyPair,
byte[] bytesPeerPublicKey) {
PrivateKey privateKey = keyPair.getPrivate();
try {
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(
bytesPeerPublicKey);
KeyFactory keyFact = KeyFactory.getInstance("DH");
PublicKey peerPublicKey = keyFact.generatePublic(x509KeySpec);
KeyAgreement ka;
ka = KeyAgreement.getInstance("DH");
ka.init(privateKey);
ka.doPhase(peerPublicKey, true);
String algorithm = "AES";
SecretKey secretKey = ka.generateSecret(algorithm);
return secretKey.getEncoded();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
}
return null;
}
public static void main(String argv[]) {
KeyPair akp = genKeyPair512();
KeyPair bkp = genKeyPair512();
System.out.println("Ali pub key: "
+ toRawHex(akp.getPublic().getEncoded()));
System.out.println("Bob pub key: "
+ toRawHex(bkp.getPublic().getEncoded()));
System.out.println("Ali pri key: "
+ toRawHex(akp.getPrivate().getEncoded()));
System.out.println("Bob pri key: "
+ toRawHex(bkp.getPrivate().getEncoded()));
byte[] apk = akp.getPublic().getEncoded();
byte[] bpk = bkp.getPublic().getEncoded();
byte[] as = genSharedSecretKey(akp, bpk);
byte[] bs = genSharedSecretKey(bkp, apk);
}
}
It generates, for example, the output:
Ali pub key: 3081DF30819706092A864886F70D010301308189024100AC86AB9A1F921B251027BD10B93D0A8D9A260364974648E2543E8CD5C48DB4FFBEF0C3843465BA8DE20FFA36FFAF840B8CF26C9EB865BA184642A5F84606AEC502407961C6D7913FDF8A034593294FA52D6F8354E9EDFE3EDC8EF082D36662D69DFE8CA7DC7480121C98B9774DFF915FB710D79E1BCBA68C0D429CD6B9AD73C0EF20020201FF03430002403BBCBF4052CD1CEF7A580A919AF75186CE0A624BC93AA47922C3822CE60A8CD10CE98550ABCA2D39DA2F09903C3D761B9A1C4AED185934FE5D08AD0CD097AA86
Bob pub key: 3081DF30819706092A864886F70D010301308189024100AC86AB9A1F921B251027BD10B93D0A8D9A260364974648E2543E8CD5C48DB4FFBEF0C3843465BA8DE20FFA36FFAF840B8CF26C9EB865BA184642A5F84606AEC502407961C6D7913FDF8A034593294FA52D6F8354E9EDFE3EDC8EF082D36662D69DFE8CA7DC7480121C98B9774DFF915FB710D79E1BCBA68C0D429CD6B9AD73C0EF20020201FF03430002400F119BC06E53F8C33D3F7C16473D1F9E001FABF4D619930C34945AA2C6D0A00CB9B332CEAF2C0C2FB61D3F568B9263B69A152410237F4D793F8B571C34AB37B7
Ali pri key: 3081E102010030819706092A864886F70D010301308189024100AC86AB9A1F921B251027BD10B93D0A8D9A260364974648E2543E8CD5C48DB4FFBEF0C3843465BA8DE20FFA36FFAF840B8CF26C9EB865BA184642A5F84606AEC502407961C6D7913FDF8A034593294FA52D6F8354E9EDFE3EDC8EF082D36662D69DFE8CA7DC7480121C98B9774DFF915FB710D79E1BCBA68C0D429CD6B9AD73C0EF20020201FF0442024043BA0B3C73EB7482B80DE98FA81A7E50B0DC2F5786CA62285655BD36CE012C056545DE5EED65736D9135EC9CD5148F8D68FF3C7B5CC62B2A1F7649698B26D1BE
Bob pri key: 3081E102010030819706092A864886F70D010301308189024100AC86AB9A1F921B251027BD10B93D0A8D9A260364974648E2543E8CD5C48DB4FFBEF0C3843465BA8DE20FFA36FFAF840B8CF26C9EB865BA184642A5F84606AEC502407961C6D7913FDF8A034593294FA52D6F8354E9EDFE3EDC8EF082D36662D69DFE8CA7DC7480121C98B9774DFF915FB710D79E1BCBA68C0D429CD6B9AD73C0EF20020201FF04420240485DDD7F5BDECA92FEE30D9D15211D274BC0FF7838B8B51E7894263CA65DB4E394033CE3E2146C0CD0CA74E2DB0EF95D01EE0DC4011A3EC6A8EC61CC2FDC5A44
So, I have a main question and two additional ones:
Why more than half of the bytes of the keys (both private and public) between Alice and Bob are equal? For instance, Alice's private starts with 3081E102010030819706092A... and Bob's private with 3081E102010030819706092A... as well.
Why is DHParameterSpec created with 511 and not 512 (at least in most examples on the web)?
Assuming there's nothing wrong with the generated keys, is there something else that I'm missing here, or this code should be safe when adpated to exchange the public keys through the internet?
Any help is welcome. Thanks in advance.
EDIT: The third question applies to the genSharedSecretKey() as well (that is, the whole code), though I'm not calling/showing output because it would be irrelevant.
Why more than half of the bytes of the keys (both private and public) between Alice and Bob are equal? For instance, Alice's private starts with 3081E102010030819706092A... and Bob's private with 3081E102010030819706092A... as well.
When you call getEncoded() you receive a copy of the key data in ASN.1 format. Depending upon the objects being represented, there is often a certain amount of duplicate data present in any two structures. Somewhere within the structure will be the key data, which will be different for Alice and Bob.
Indeed, as CodesInChaos stated in the comments, the first part of the ASN.1 structure contains the group parameter which is identical between both parties.
The reason why you receive keys with much duplicate that is - as Duncan suggested - that it is encoded in an ASN.1 format. And that does explain the amount of duplicate data as the contains the parameters p and g you've specified yourself - look for the values in the hexadecimal string. If you want to take a look at the contents, simply paste the hexadecimal output into an online ASN.1 decoder, such as the one found here.
There is a lot to be said about DH and (key) sizes. Some interesting discussions can be found on security.stackexchange.com and crypto.stackexchange.com. But in the end, we don't know; you typed in the code, so we should ask you how and why the Diffie-Hellman parameters were chosen.
As for the third question; to distribute public keys on the internet you need to establish trust. You need to trust the other party. Diffie Hellman key agreement by itself does not establish trust; only a shared secret with some other party. So you need a secure protocol (such as TLS) with trusted certificates or keys that contains an authentication component. A lot of knowledge is required to create such a protocol however; if you do not fully understand the code you just showed us, you will fail to create such a protocol yourself.
Note that if both parties have agreed upon a set of parameters, that it may not be necessary to send the whole encoded public key; you may just have to send the values that the other party does not know so the other party can reconstruct the public key.
I am writing android app that makes AES encryption/decryption of files. I want to be able to detect if incorrect password is specified and thus not matching key is derived for decryption.
I am using AES/CBC/PKCS7Padding with 256 bit key.
If I do cipher.doFinal() I can try/catch the BadPaddingException and it tells me that something is wrong and probably key was incorrect. But if I use CipherInputStream to read encrypted file, I get no feedback on correctness of padding. So if I deliberately specify incorrect password it decrypts file, then reports that everything is ok, however decrypted file is a total junk.
So my question is how to detect bad padding when using CipherInputStream?
Prepend some known header to your data. Decrypt it first and if it doesn't match what you expected, stop and return error.
Try and use GCM mode instead (Java 7 or Bouncy Castle provider). The trick with padding is that sometimes it is correct after the message has been altered (once in 256 times, approximately). GCM mode will add intergrity protection, so any alteration will result in an exception derived from BadPaddingException.
One thing though: you should prepend a (random) nonce when encrypting with GCM (actually a rule for CBC mode too, but the implications of using a non-random IV in CBC are less severe).
Note that you need to perform the final calculation to get a badpaddingexception, so don't forget to close or end the underlying stream. This is probably your current issue.
[EDIT]: this is not the answer, but the input could be used to generate a better CipherInputStream, see my other answer on this question.
I had the same issue, how to know if the key used to encrypt is the same that used to decrypt, because in my case I could decrypt the strings but it returned some garbage, and i need to know the encrypted string(its random) to get the correct value.
So what i have done is;
Decrypt the encrypted string.
Encrypt the String again using the correct key.
Decrypt the previous encrypted string.
Match if the original decrypted key equals the new decrypted key.
Cipher c = Cipher.getInstance(algorithm);
c.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
byte[] decValue = c.doFinal(encryptedData.getBytes());
decryptedValue = new String(decValue,"UTF-8");
//now i have the string decrypted in decryptedValue
byte[] encryptAgain = encrypt(decryptedValue);
String encryptAgaindecripted = new String(c.doFinal(encryptAgain),"UTF-8");
//if keys match then it uses the same key and string is valid
if (decryptedValue.equals(encryptAgaindecripted)){
//return valid
}
hope this helps someone.
I think bad padding is caught for some reason, this is from the CipherInputStream source:
private int getMoreData() throws IOException {
if (done) return -1;
int readin = input.read(ibuffer);
if (readin == -1) {
done = true;
try {
obuffer = cipher.doFinal();
}
catch (IllegalBlockSizeException e) {obuffer = null;}
catch (BadPaddingException e) {obuffer = null;}
if (obuffer == null)
return -1;
else {
ostart = 0;
ofinish = obuffer.length;
return ofinish;
}
}
try {
obuffer = cipher.update(ibuffer, 0, readin);
} catch (IllegalStateException e) {obuffer = null;};
ostart = 0;
if (obuffer == null)
ofinish = 0;
else ofinish = obuffer.length;
return ofinish;
}
Here is modified version of getMoreData() method in CipherInputStream, it maybe useful for someone who faced my problem:
private int getMoreData() throws IOException {
if (done) return -1;
int readin = input.read(ibuffer);
if (readin == -1) {
done = true;
try {
obuffer = cipher.doFinal();
}
catch (IllegalBlockSizeException e) {
throw new IOException(e);
}
catch (BadPaddingException e) {
throw new IOException(e);
}
if (obuffer == null)
return -1;
else {
ostart = 0;
ofinish = obuffer.length;
return ofinish;
}
}
try {
obuffer = cipher.update(ibuffer, 0, readin);
} catch (IllegalStateException e) {obuffer = null;};
ostart = 0;
if (obuffer == null)
ofinish = 0;
else ofinish = obuffer.length;
return ofinish;
}
I ran into this as well. I had a test that reliably fails a couple of times out of a thousand runs with AES/CBC/PKCS5Padding that is supported in Java.
To fix, you can do as suggested above and use bouncy castle.
However, I did a different fix and simply added a md5 content hash to the plain text before encrypting that I verify on decrypt. Simply append the content to the md5 hash and on decrypt grab the first 22 characters of the md5 hash and verify that the rest of the string has the same hash and throw an exception if it doesn't or return the plaintext (without md5 hash) if it does match. This will work regardless of the encryption algorithm. Probably with GCM mode this is indeed not needed though. Anyway, this way you can avoid extra dependencies on bouncy castle.