CryptoJS AES and Java AES encrypted value mismatch - java

I am trying to encrypt in client and decrypt in sever using AES,
so using cryptojs to encrypt in client side with CBC mode and nopadding
in server side also using Cipher class with same mode and nopadding
function call()
{
var key = CryptoJS.enc.Hex.parse('roshanmathew1989');
var iv = CryptoJS.enc.Hex.parse('roshanmathew1989');
var encrypted = CryptoJS.AES.encrypt("roshanmathew1989",key,{ iv: iv},
{padding:CryptoJS.pad.NoPadding});
alert(encrypted.ciphertext.toString(CryptoJS.enc.Base64));
alert(encrypted.iv.toString());
}
Server side code
public class Crypto
{
private static byte[] key = null;
public void setKey(String key){this.key=key.getBytes();}
public String encrypt(String strToEncrypt)
{
String encryptedString =null;
try
{
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
final SecretKeySpec secretKey = new SecretKeySpec(key,"AES");
System.out.println("sdfsdf = "+key.toString());
IvParameterSpec ips = new IvParameterSpec(key);
cipher.init(Cipher.ENCRYPT_MODE, secretKey,ips);
encryptedString = Base64.encodeBase64String(cipher.doFinal(strToEncrypt.getBytes()));
}
catch(Exception e)
{
System.out.println(" ERROR : "+e.getMessage());
}
return encryptedString;
} other method omitted ....
implementation
Crypto cry=new Crypto();
cry.setKey("roshanmathew1989");
String s=cry.encrypt("roshanmathew1989");
Results
Browser side value = O64X/bKNBu7R2Tuq2lUbXeFlQ7wD2YnFasyyhsVUryw=
Server side value of s = RrNcVIER/75fzdjHr884sw==
Can anybody point out the mistake?

There are a few things wrong with the code:
you are using hexadecimal decoding of the key in JavaScript, and String.getBytes() - character encoding without specifying the character set - in Java
your key is 16 characters (it should be 16, 24 or 32 randomized bytes), but it is not in hexadecimals
you are encrypting instead of decrypting on the "server side", although that one is probably on purpose
Take another good look on how to perform encoding and character-encoding, they are essential for good crypto and often performed incorrectly (it's probably the most common issue on Stackoverflow regarding encryption)

Related

Sending IV with cipher text

I am encrypting my message with a constant secret key and random IV using AES/CTR/NoPadding in Java.
I plan to prepend the IV to the cipher text and then Base64 encode it.
Now, there is one problem that is bothering me. There can be multiple IV + Cipher text combinations that can result in my original message. Isn't that a problem? Also, is it safe to send IV as such (i.e. prepend/append to the cipher text) or there is some procedure that I should follow?
I am relatively new to cryptography, so pardon me if it's very simple question. I couldn't find any satisfying answer to this.
EDIT:
public static String encrypt(String message) {
try {
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
byte[] iv = generateRandomIV();
cipher.init(Cipher.ENCRYPT_MODE, SECRET_KEY, new IvParameterSpec(iv));
byte[] cipherText = cipher.doFinal(message.getBytes("utf-8"));
return DatatypeConverter.printBase64Binary(concat(iv, cipherText));
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
public static String decrypt(String encryptedMessage) {
try {
byte[] bytes = DatatypeConverter.parseBase64Binary(encryptedMessage);
byte[] iv = getIV(bytes);
byte[] cipherText = getCipherText(bytes);
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, SECRET_KEY, new IvParameterSpec(iv));
return new String(cipher.doFinal(cipherText));
} catch (Exception ex) {
ex.printStackTrace();
return encryptedMessage;
}
}
private static byte[] getIV(byte[] bytes) {
return Arrays.copyOfRange(bytes, 0, 16);
}
private static byte[] getCipherText(byte[] bytes) {
return Arrays.copyOfRange(bytes, 16, bytes.length);
}
and then
public static void main(String[] args) {
System.out.println(decrypt("wVroKV1UnL2NXiImS83hLKpLLJKk"));
System.out.println(decrypt("Q0tWAMZDhqMo0LbtEY7lF9D8Dkor"));
}
Both these produce same output -- "goody"
There can be multiple IV + Cipher text combinations that can result in my original message. Isn't that a problem?
The reason that you're using different IV's is that you can send the same message twice using the same key, with different ciphertext.
If it would generate the same ciphertext an adversary would know that the same message was send, leaking information about the message. So the idea of the IV is that it generates a different ciphertext and in most cases that is beneficial rather than a problem.
If it is a problem depends on your protocol. Note that the ciphertext length may still show information about the plaintext (i.e. "Affirmative, Sergeant" will of course be different than the encryption of "No").
You'll need an authentication tag / MAC value to protect against change of the message. Furthermore, you may want to include something like a message sequence number to make sure that replay attacks don't happen.
However, the deeper you go the more complex encryption becomes. If you require secure transport then in the end it is infinitely easier to use TLS or SSH (or any other applicable transport layer security).
Also, is it safe to send IV as such (i.e. prepend/append to the cipher text) or there is some procedure that I should follow?
Prepending it ("prepend" is not a word in many dictionaries, you could use "prefix" as well) is a common way of handling the IV, yes. The IV may be sent anyway and it doesn't need to be kept confidential. In the case of CBC however it must be random, not a sequence number.

Error : javax.crypto.BadPaddingException: pad block corrupted while Decryption

I have done Encryption with ,
public static String encrypt(String plainText) {
try {
byte[] keyData = secret_key.getBytes();
SecretKeySpec secretKey = new SecretKeySpec(keyData, "AES/ECB/PKCS7Padding");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] cipherText = cipher.doFinal(plainText.getBytes("UTF-8"));
String encryptedString = Base64.encodeToString(cipherText, Base64.NO_WRAP);
return encryptedString;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
it's working well.
but part of Decryption gives Error like,
W/System.err: javax.crypto.BadPaddingException: pad block corrupted
W/System.err: at com.android.org.bouncycastle.jce.provider.JCEBlockCipher.engineDoFinal(JCEBlockCipher.java:701)
W/System.err: at javax.crypto.Cipher.doFinal(Cipher.java:1111)
decrypt Code like,
public static String decrypt(String encryptedText) {
try {
byte[] keyData = secret_key.getBytes();
SecretKeySpec secretKey = new SecretKeySpec(keyData, "AES/ECB/PKCS7Padding");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] cipherText = Base64.decode(encryptedText,Base64.NO_WRAP);
String decryptedString = new String(cipher.doFinal(cipherText),"UTF-8");
return decryptedString;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
here what is the problem? How can i solve this Issue?
It is likely that your secret_key value contains bytes which are not well represented in the ambiguous encoding you're using. When you call String#getBytes() without specifying an encoding, you get the system default, which can vary.
You should use hexadecimal encoding whenever you represent your key as a String. This will be consistent across serialization/deserialization on every platform. There are many standard implementations of this encoding/decoding process available (i.e. org.bouncycastle.util.encoders.Hex.decode("0123456789ABCDEFFEDCBA9876543210"); or org.apache.commons.codec.binary.Hex.decodeHex("0123456789ABCDEFFEDCBA9876543210".toCharArray()); which both return the raw byte[]).
Some side notes:
You are using ECB mode of operation, which is extremely susceptible to frequency analysis for cryptanalysis and is effectively deprecated aside from toy crypto demonstrations. I suggest you use CBC, CTR, or GCM.
You do not provide an initialization vector (IV), so the same message encrypted with the same key will always yield identical cipher text. Use a unique and non-predictable IV for every encryption operation by generating 16 bytes from SecureRandom and populating it into an IvParameterSpec. You can prepend the IV bytes to the cipher text and transport/store it in the clear.
Your cipher text is not authenticated, allowing for malicious users to both manipulate encrypted data and to attempt decryption via padding oracle/CCA attacks. Use an authenticated encryption with associated data (AEAD) mode like GCM, or use an HMAC/SHA-256 message authentication code (MAC) over the cipher text, and verify it using a constant-time equals method before attempting any decryption.
You do not need to provide the mode of operation or padding scheme when instantiating a key. SecretKey key = new SecretKeySpec(keyData, "AES"); is sufficient.

RSA encryption in Android and Java

I would like to encrypt a String with RSA encryption. My public/private keys were generated and stored in DB. In android, I use this code:
public static String encryptRSAToString(String text, String strPublicKey) {
byte[] cipherText = null;
String strEncryInfoData="";
try {
KeyFactory keyFac = KeyFactory.getInstance("RSA");
KeySpec keySpec = new X509EncodedKeySpec(Base64.decode(strPublicKey.trim().getBytes(), Base64.DEFAULT));
Key publicKey = keyFac.generatePublic(keySpec);
// get an RSA cipher object and print the provider
final Cipher cipher = Cipher.getInstance("RSA");
// encrypt the plain text using the public key
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
cipherText = cipher.doFinal(text.getBytes());
strEncryInfoData = new String(Base64.encode(cipherText,Base64.DEFAULT));
} catch (Exception e) {
e.printStackTrace();
}
return strEncryInfoData.replaceAll("(\\r|\\n)", "");
}
For debug purpose, I try to call 2 times this method with the same parameters and String result were similar (as expected).
I want to generate the same encrypted String in java. However, "android.util.Base64" class is not available in Java, so I've tried with the default Base64 class:
public static String encryptRSAToString(String text, String strPublicKey) {
byte[] cipherText = null;
String strEncryInfoData="";
try {
KeyFactory keyFac = KeyFactory.getInstance("RSA");
KeySpec keySpec = new X509EncodedKeySpec(Base64.decodeBase64(strPublicKey.trim().getBytes()));
Key publicKey = keyFac.generatePublic(keySpec);
// get an RSA cipher object and print the provider
final Cipher cipher = Cipher.getInstance("RSA");
// encrypt the plain text using the public key
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
cipherText = cipher.doFinal(text.getBytes());
strEncryInfoData = new String(Base64.encodeBase64(cipherText));
} catch (Exception e) {
e.printStackTrace();
}
return strEncryInfoData.replaceAll("(\\r|\\n)", "");
}
But the String generated in Android and the one in java are different.
Generated in Android side :
Ky2T4j1JdI081ZESVJgxZXEf/xmtpehfv/EwpVvKQxUu1JI8lwXP2Rc66jHZRc0P846ZYuF3C9YEmWoKbXGXk2MBuT5KVxa2yoTbwZlMmhVOX3X3Efq0VyaO5zZ4qavIq036cA3MzvQbUAb678UdbALW/CjRCsOdeH+hSCzNQ+0=
Generated in JAVA side :
XhSLxfiJUUdZW5kWh0MEPSrqoROBBhNC/krfTx+sdnXML3WegYbMzSvNnPgB8+8Z9joEUBMmoeBI1OhTF6qPFL1EEixkFYAkGaryEFxvN/aFI75kEUj71OHNzAHAuvS+h+9Nssx9psSZ5gc2OoLQH0QtbGDyXB4p+qUGFCde4tY=
Does someone know how to solve my issue ?
thank you
It looks like you've been undone by relying on defaults. Never do that if you hope for interoperability.
Here are the two examples of mistakenly relying on defaults in your code that I've found.
final Cipher cipher = Cipher.getInstance("RSA");
The tranformation string is supposed to be of the form "algorithm/mode/padding" but you've left off the mode and padding specifications. As a result you got default values for those. The defaults are evidently different on Android and Oracle Java. You should always fully specify the transformation, for example:
final Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING");
Another bad example is
cipherText = cipher.doFinal(text.getBytes());
In text.getBytes() you are relying on the no-args getBytes() method which uses the default charset for the platform. But this default charset differs on different platforms, and thus this is not portable. In almost all cases I've run across you should specify the UTF-8 charset. So the correct line would thus be
cipherText = cipher.doFinal(text.getBytes("UTF-8"));
and the correct string constructor to use to recreate the original string in the decrypt method is the String(byte [] data, String charsetName).
I canĀ“t comment yet so I answer.
It is possible that different default configurations are being used. Check this question: Is there any difference between Apache's Base64.encodeBase64 and Android's Base64.encode with Base64.Default flag?
There are deviations of different cipher and hash implementations. I would suggest using OpenSSL as a common implementation.

How to encrypt a message at client side using crypto-js library and decrypt it at the Java server

Background:
the application that I am working on is supposed to work offline. I have an HTML5 page and the data keyed in by the user is encrypted using crypto-js library.
And I want the encrypted message sent to java webserver and then decrypt it at the server side.
What am doing
I am able to encrypt the message using Crypto-js
<code>
var message = "my message text";
var password = "user password";
var encrypted = CryptoJS.AES.encrypt( message ,password );
console.log(encrypted.toString());
// this prints an encrypted text "D0GBMGzxKXU757RKI8hDuQ=="
</code>
What I would like to do is pass the encrypted text "D0GBMGzxKXU757RKI8hDuQ==
" to a java server side code and get the necrypted message decrypted.
I tried many options to decrypt the crypto-js encrypted message at the java server side.
Please find below my code at the server side that is supposed to do the decryption of the encrypted text.
<code>
public static String decrypt(String keyText,String encryptedText)
{
// generate key
Key key = new SecretKeySpec(keyText.getBytes(), "AES");
Cipher chiper = Cipher.getInstance("AES");
chiper.init(Cipher.DECRYPT_MODE, key);
byte[] decordedValue = new BASE64Decoder().decodeBuffer(encryptedText);
byte[] decValue = chiper.doFinal(decordedValue);
String decryptedValue = new String(decValue);
return decryptedValue;
}
</code>
I call the java method decrypt from below code
<code>
// performs decryption
public static void main(String[] args) throws Exception
{
String decryptedText = CrypterUtil.decrypt("user password","D0GBMGzxKXU757RKI8hDuQ==");
}
</code>
But i get the following exception when i run the java decrypt code
<code>
Exception in thread "main" java.security.InvalidKeyException: Invalid AES key length: 13 bytes
at com.sun.crypto.provider.AESCipher.engineGetKeySize(AESCipher.java:372)
at javax.crypto.Cipher.passCryptoPermCheck(Cipher.java:1052)
at javax.crypto.Cipher.checkCryptoPerm(Cipher.java:1010)
at javax.crypto.Cipher.implInit(Cipher.java:786)
at javax.crypto.Cipher.chooseProvider(Cipher.java:849)
at javax.crypto.Cipher.init(Cipher.java:1213)
at javax.crypto.Cipher.init(Cipher.java:1153)
at au.gov.daff.pems.model.utils.CrypterUtil.decrypt(CrypterUtil.java:34)
at au.gov.daff.pems.model.utils.CrypterUtil.main(CrypterUtil.java:47)
Process exited with exit code 1.
</code>
Am not sure what am I doing wrong ?... What is the best way to encrypt a message using the crypto-js library so that it can be decripted else where using user keyed in password.
Thanks to Artjom B and Isaac Potoczny-Jones for the prompt response and advice. I am giving the complete solution that worked for me below for the benefit of others.
Java code to do the decryption of the cryptojs encrypted message at the Java server side
public static void main(String args[]) throws Exception{
String password = "Secret Passphrase";
String salt = "222f51f42e744981cf7ce4240eeffc3a";
String iv = "2b69947b95f3a4bb422d1475b7dc90ea";
String encrypted = "CQVXTPM2ecOuZk+9Oy7OyGJ1M6d9rW2D/00Bzn9lkkehNra65nRZUkiCgA3qlpzL";
byte[] saltBytes = hexStringToByteArray(salt);
byte[] ivBytes = hexStringToByteArray(iv);
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
SecretKeySpec sKey = (SecretKeySpec) generateKeyFromPassword(password, saltBytes);
System.out.println( decrypt( encrypted , sKey ,ivParameterSpec));
}
public static SecretKey generateKeyFromPassword(String password, byte[] saltBytes) throws GeneralSecurityException {
KeySpec keySpec = new PBEKeySpec(password.toCharArray(), saltBytes, 100, 128);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
SecretKey secretKey = keyFactory.generateSecret(keySpec);
return new SecretKeySpec(secretKey.getEncoded(), "AES");
}
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
public static String decrypt(String encryptedData, SecretKeySpec sKey, IvParameterSpec ivParameterSpec) throws Exception {
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
c.init(Cipher.DECRYPT_MODE, sKey, ivParameterSpec);
byte[] decordedValue = new BASE64Decoder().decodeBuffer(encryptedData);
byte[] decValue = c.doFinal(decordedValue);
String decryptedValue = new String(decValue);
return decryptedValue;
}
The cryptojs javascript code that can do the encryption and decryption at the client side
function generateKey(){
var salt = CryptoJS.lib.WordArray.random(128/8);
var iv = CryptoJS.lib.WordArray.random(128/8);
console.log('salt '+ salt );
console.log('iv '+ iv );
var key128Bits100Iterations = CryptoJS.PBKDF2("Secret Passphrase", salt, { keySize: 128/32, iterations: 100 });
console.log( 'key128Bits100Iterations '+ key128Bits100Iterations);
var encrypted = CryptoJS.AES.encrypt("Message", key128Bits100Iterations, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 });
}
function decrypt(){
var salt = CryptoJS.enc.Hex.parse("4acfedc7dc72a9003a0dd721d7642bde");
var iv = CryptoJS.enc.Hex.parse("69135769514102d0eded589ff874cacd");
var encrypted = "PU7jfTmkyvD71ZtISKFcUQ==";
var key = CryptoJS.PBKDF2("Secret Passphrase", salt, { keySize: 128/32, iterations: 100 });
console.log( 'key '+ key);
var decrypt = CryptoJS.AES.decrypt(encrypted, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 });
var ddd = decrypt.toString(CryptoJS.enc.Utf8);
console.log('ddd '+ddd);
}
You have to understand that a password is not a key. A password usually goes through some hashing function to result in a bit string or byte array which is a key. It cannot be printed, so it is represented as hex or base64.
In JavaScript you use a password, but in Java you assume the same password is the key which it isn't. You could determine how CryptoJS hashes the password to arrive at the key and recreate this in Java, but it seems that it is implemented in such a way that a fresh salt is generated every time something is encrypted with a password and there is no way to change the salt.
If you really want to work will password from the user then you need to derive the key yourself. CryptoJS provides PBKDF2 for this, but it also takes a salt. You can generate one for your application and add it to the code. You would generate it this way once:
CryptoJS.lib.WordArray.random(128/8).toString();
To derive the key everytime you would pass the static salt into the password-based key derivation function (here for AES-256)
var key = CryptoJS.PBKDF2(userPassword,
CryptoJS.enc.Hex.parse(salt),
{ keySize: 256/32, iterations: 1000 });
var iv = CryptoJS.lib.WordArray.random(256/8); // random IV
var encrypted = CryptoJS.AES.encrypt("Message", key, { iv: iv });
On the server you need to convert the hex key string into a byte array. You will also need to tweak the scheme on the server from AES to AES/CBC/PKCS5Padding as it is the default in CryptoJS. Note PKCS5 and PKCS7 are the same for AES.
Also note that you will need to pass the IV from client to server and init it as
chiper.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(ivBytes));
You can of course recreate the key from the password and the salt on the server using a Java implementation of PBKDF or just save the key for a known password and salt. You can play around with the iterations of the PBKDF what is acceptable for your users.
AES and the related algorithms can be used in many different ways, and when mixing languages, it can always be a little tricky to figure out what modes the client is using and match them to the modes of the server.
The first problem with your Java code is that you cannot use the bytes of a string as an AES key. There are lots of examples on the Internet of people doing this, but it's terribly wrong. Just like #artjom-B showed with the CryptoJS code, you need to use a "Password-based key derivation function" and it needs to also be parametrized exactly the same on the client & server.
Also, the client needs to generate salt and send it along with the crypto text; otherwise, the server cannot generate the same key from the given password. I'm not sure exactly how CryptoJS does this here's something reasonable in Java, and you can tweak the parameters as you learn how cryptoJS works:
public static SecretKey generateKeyFromPassword(String password, byte[] salt) throws GeneralSecurityException {
KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, 1000, 256);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
byte[] keyBytes = keyFactory.generateSecret(keySpec).getEncoded();
return new SecretKeySpec(keyBytes, "AES");
}
With AES CBC, you also need to randomly generate an IV and send that along with the crypto text.
So in summary:
Figure out the AES parameters used by CryptoJS. Not sure what they are, but it sounds like: key size (256), padding (pkcs5), mode (CBC), PBE algorithm (PBKDF2), salt (random), iteration count (100)
Configure your server with the same parameters
Use a PBE key generator, along with a non-secret (but random) salt
Use AES CBC with a non-secret (but random) IV
Send the cipher text, the IV, and the salt to the server
Then on the server side, use the salt, iteration count, and the password to generate the AES key
Then base64 decode and decrypt it

Java public private key decryption issue

I am trying to encrypt and decrypt a message as mentioned in the below code. Basically I want to encrypt a message with a public key and convert that encrypted message from byte array to String. And decrypt this string into original text. Here are the both methods. Here encryption works fine but decryption fails (error is "Data must start with zero"). I think this is causing because I convert encrypted byte array into String.
How do I solve this? (I want to have encrypted byte array as string and use it for decryption) Is there any other approach (with public and private keys)
public static String getEncryptedMessage(String publicKeyFilePath,
String plainMessage) {
byte[] encryptedBytes;
try {
Cipher cipher = Cipher.getInstance("RSA");
byte[] publicKeyContentsAsByteArray = getBytesFromFile(publicKeyFilePath);
PublicKey publicKey = getPublicKey(publicKeyContentsAsByteArray);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
encryptedBytes = cipher.doFinal(plainMessage.getBytes());
return new String(encryptedBytes);
} catch (Throwable t) {
}
}
public static String getDecryptedMessage(
String privateKeyFilePath, String encryptedMessage)
{
byte[] decryptedMessage;
try {
Cipher cipher = Cipher.getInstance("RSA");
byte[] privateKeyContentsAsByteArray = getBytesFromFile(privateKeyFilePath);
PrivateKey privateKey = getPrivateKey(privateKeyContentsAsByteArray);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
decryptedMessage = cipher.doFinal(encryptedMessage.getBytes());
return new String(decryptedMessage);
} catch (Throwable t) {
}
If you look at this page (http://www.wikijava.org/wiki/Secret_Key_Cryptography_Tutorial) you will need to do base-64 encoding to turn the bytes into a string, then to decrypt it you would just decode it then decrypt.
Base-64 encoding uses the first 7 bits of a byte, to make something that is printable or emailable, for example.
UPDATE:
I made a mistake, there are 64 characters that it would be encoded in, again, in order to make it easier to use as something printable.
Why don't you treat the message as byte array from encryption to decryption? Why changing it to String in the middle? (I know it seems like a question, but it's actually an answer...)
Using RSA directly on unformatted data may leave your application vulnerable to an adaptive chosen ciphertext attack. For details please see Chapter 8, pages 288-289, of the Handbook of Applied Cryptography, a freely-available book from CRC Press. (It's well worth buying the bound edition, if you're really interested in cryptography -- you'll be stunned at the quality for the price.)
Because of this attack, most protocols that integrate RSA use RSA for encrypting randomly-generated session keys or signing hash functions with outputs that ought to be indistinguishable from random, OR using very carefully formatted messages that will fail to be correctly interpreted. (See Note 8.63 in HAC for details.)

Categories