From 0e460011c284b598d015afdc91f800c415821cef Mon Sep 17 00:00:00 2001 From: serega6531 Date: Thu, 23 Apr 2020 22:40:27 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A0=D0=B0=D1=81=D1=88=D0=B8=D1=84=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D0=BA=D0=B0=20TLS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/optimization/RsaKeysHolder.java | 35 ++++++ .../service/optimization/TlsDecryptor.java | 116 +++++++++++++----- .../serega6531/packmate/TlsDecryptorTest.java | 9 +- src/test/resources/tls.pkmt | 6 +- 4 files changed, 131 insertions(+), 35 deletions(-) create mode 100644 src/main/java/ru/serega6531/packmate/service/optimization/RsaKeysHolder.java diff --git a/src/main/java/ru/serega6531/packmate/service/optimization/RsaKeysHolder.java b/src/main/java/ru/serega6531/packmate/service/optimization/RsaKeysHolder.java new file mode 100644 index 0000000..a6a725e --- /dev/null +++ b/src/main/java/ru/serega6531/packmate/service/optimization/RsaKeysHolder.java @@ -0,0 +1,35 @@ +package ru.serega6531.packmate.service.optimization; + +import org.springframework.stereotype.Service; +import ru.serega6531.packmate.utils.TlsUtils; + +import javax.net.ssl.X509KeyManager; +import java.io.File; +import java.math.BigInteger; +import java.security.interfaces.RSAPrivateKey; +import java.util.HashMap; +import java.util.Map; + +@Service +public class RsaKeysHolder { + + // Key: N from RSA public key + private final Map keys = new HashMap<>(); + + public void addKey(File pemFile, File keyFile) { + if(!pemFile.exists() || !keyFile.exists()) { + throw new IllegalArgumentException("One of files does not exist"); + } + + X509KeyManager keyManager = TlsUtils.createKeyManager(pemFile, keyFile); + +// X509Certificate[] certificateChain = keyManager.getCertificateChain("1"); + RSAPrivateKey privateKey = ((RSAPrivateKey) keyManager.getPrivateKey("1")); + keys.put(privateKey.getModulus(), privateKey); + } + + public RSAPrivateKey getKey(BigInteger modulus) { + return keys.get(modulus); + } + +} diff --git a/src/main/java/ru/serega6531/packmate/service/optimization/TlsDecryptor.java b/src/main/java/ru/serega6531/packmate/service/optimization/TlsDecryptor.java index a4dca7b..7b53f08 100644 --- a/src/main/java/ru/serega6531/packmate/service/optimization/TlsDecryptor.java +++ b/src/main/java/ru/serega6531/packmate/service/optimization/TlsDecryptor.java @@ -1,57 +1,54 @@ package ru.serega6531.packmate.service.optimization; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.ListMultimap; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ArrayUtils; import org.bouncycastle.tls.ExporterLabel; import org.bouncycastle.tls.PRFAlgorithm; import org.bouncycastle.tls.crypto.TlsSecret; import org.bouncycastle.tls.crypto.impl.bc.BcTlsCrypto; import org.bouncycastle.tls.crypto.impl.bc.BcTlsSecret; +import org.pcap4j.util.ByteArrays; import ru.serega6531.packmate.model.Packet; import ru.serega6531.packmate.service.optimization.tls.TlsPacket; import ru.serega6531.packmate.service.optimization.tls.keys.TlsKeyUtils; import ru.serega6531.packmate.service.optimization.tls.numbers.CipherSuite; import ru.serega6531.packmate.service.optimization.tls.numbers.ContentType; import ru.serega6531.packmate.service.optimization.tls.numbers.HandshakeType; +import ru.serega6531.packmate.service.optimization.tls.records.ApplicationDataRecord; import ru.serega6531.packmate.service.optimization.tls.records.HandshakeRecord; import ru.serega6531.packmate.service.optimization.tls.records.handshakes.BasicRecordContent; import ru.serega6531.packmate.service.optimization.tls.records.handshakes.ClientHelloHandshakeRecordContent; import ru.serega6531.packmate.service.optimization.tls.records.handshakes.HandshakeRecordContent; import ru.serega6531.packmate.service.optimization.tls.records.handshakes.ServerHelloHandshakeRecordContent; -import ru.serega6531.packmate.utils.TlsUtils; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; -import javax.net.ssl.X509KeyManager; -import java.io.File; import java.nio.ByteBuffer; -import java.security.cert.X509Certificate; import java.security.interfaces.RSAPrivateKey; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; +import java.util.stream.Stream; +@Slf4j @RequiredArgsConstructor public class TlsDecryptor { private static final Pattern cipherSuitePattern = Pattern.compile("TLS_RSA_WITH_([A-Z0-9_]+)_([A-Z0-9]+)"); private final List packets; + private final RsaKeysHolder keysHolder; @SneakyThrows public void decryptTls() { - File pemFile = new File(getClass().getClassLoader().getResource("tls.pem").getFile()); - File keyFile = new File(getClass().getClassLoader().getResource("tls.key").getFile()); - X509KeyManager keyManager = TlsUtils.createKeyManager(pemFile, keyFile); + ListMultimap tlsPackets = ArrayListMultimap.create(packets.size(), 1); - X509Certificate[] certificateChain = keyManager.getCertificateChain("1"); - RSAPrivateKey privateKey = ((RSAPrivateKey) keyManager.getPrivateKey("1")); - - Map> tlsPackets = packets.stream() - .collect(Collectors.toMap(p -> p, this::createTlsHeaders)); + packets.forEach(p -> tlsPackets.putAll(p, createTlsHeaders(p))); ClientHelloHandshakeRecordContent clientHello = (ClientHelloHandshakeRecordContent) getHandshake(tlsPackets.values(), HandshakeType.CLIENT_HELLO).orElseThrow(); @@ -65,10 +62,18 @@ public class TlsDecryptor { if (cipherSuite.name().startsWith("TLS_RSA_WITH_")) { Matcher matcher = cipherSuitePattern.matcher(cipherSuite.name()); + //noinspection ResultOfMethodCallIgnored matcher.find(); String blockCipher = matcher.group(1); String hashAlgo = matcher.group(2); + //TODO + RSAPrivateKey privateKey = keysHolder.getKey(null); + if(privateKey == null) { + log.warn("Key for modulus not found: {}", "TODO"); + return; + } + BasicRecordContent clientKeyExchange = (BasicRecordContent) getHandshake(tlsPackets.values(), HandshakeType.CLIENT_KEY_EXCHANGE).orElseThrow(); @@ -83,7 +88,8 @@ public class TlsDecryptor { BcTlsSecret preSecret = new BcTlsSecret(new BcTlsCrypto(null), preMaster); TlsSecret masterSecret = preSecret.deriveUsingPRF( PRFAlgorithm.tls_prf_sha256, ExporterLabel.master_secret, randomCS, 48); - byte[] expanded = masterSecret.deriveUsingPRF(PRFAlgorithm.tls_prf_sha256, ExporterLabel.key_expansion, randomSC, 136).extract(); // для sha256 + byte[] expanded = masterSecret.deriveUsingPRF( + PRFAlgorithm.tls_prf_sha256, ExporterLabel.key_expansion, randomSC, 136).extract(); // для sha256 byte[] clientMacKey = new byte[20]; byte[] serverMacKey = new byte[20]; @@ -100,33 +106,77 @@ public class TlsDecryptor { bb.get(clientIV); bb.get(serverIV); - Cipher aes = Cipher.getInstance("AES/CBC/PKCS5Padding"); // TLS_RSA_WITH_AES_256_CBC_SHA - SecretKeySpec skeySpec = new SecretKeySpec(clientEncryptionKey, "AES"); - IvParameterSpec ivParameterSpec = new IvParameterSpec(clientIV); - aes.init(Cipher.DECRYPT_MODE, skeySpec, ivParameterSpec); + byte[] clientFinishedEncrypted = getFinishedData(tlsPackets, true); + byte[] serverFinishedEncrypted = getFinishedData(tlsPackets, false); - byte[] data = tlsPackets.entrySet().stream() - .filter(ent -> ent.getKey().isIncoming()) - .map(Map.Entry::getValue) - .flatMap(Collection::stream) - .filter(p -> p.getContentType() == ContentType.HANDSHAKE) - .map(p -> ((HandshakeRecord) p.getRecord())) - .filter(r -> r.getHandshakeType() == HandshakeType.ENCRYPTED_HANDSHAKE_MESSAGE) - .map(r -> ((BasicRecordContent) r.getContent())) - .findFirst() - .orElseThrow() - .getContent(); + Cipher clientCipher = createCipher(clientEncryptionKey, clientIV, clientFinishedEncrypted); +// byte[] clientFinishedData = clientCipher.update(clientFinishedEncrypted); +// HandshakeRecord clientFinished = HandshakeRecord.newInstance(clientFinishedData, 16, clientFinishedData.length - 16); - byte[] decrypt = aes.doFinal(data); - System.out.println(); + Cipher serverCipher = createCipher(serverEncryptionKey, serverIV, serverFinishedEncrypted); +// byte[] serverFinishedData = serverCipher.update(serverFinishedEncrypted); +// HandshakeRecord serverFinished = HandshakeRecord.newInstance(serverFinishedData, 16, serverFinishedData.length - 16); + + for (Map.Entry entry : tlsPackets.entries()) { + if (entry.getValue().getContentType() == ContentType.APPLICATION_DATA) { + byte[] data = ((ApplicationDataRecord) entry.getValue().getRecord()).getData(); + boolean client = entry.getKey().isIncoming(); + + byte[] decoded; + + if(client) { + decoded = clientCipher.update(data); + } else { + decoded = serverCipher.update(data); + } + + decoded = clearDecodedData(decoded); + String string = new String(decoded); + System.out.println(string); + } + } } } - private Optional getHandshake(Collection> packets, + @SneakyThrows + private Cipher createCipher(byte[] key, byte[] iv, byte[] initData) { + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); // TLS_RSA_WITH_AES_256_CBC_SHA + SecretKeySpec serverSkeySpec = new SecretKeySpec(key, "AES"); + IvParameterSpec serverIvParameterSpec = new IvParameterSpec(iv); + cipher.init(Cipher.DECRYPT_MODE, serverSkeySpec, serverIvParameterSpec); + cipher.update(initData); + + return cipher; + } + + private byte[] clearDecodedData(byte[] decoded) { + int start = 32; + int end = decoded.length - 6; //FIXME + decoded = ByteArrays.getSubArray(decoded, start, end - start); + return decoded; + } + + private byte[] getFinishedData(ListMultimap tlsPackets, boolean incoming) { + return ((BasicRecordContent) getHandshake(tlsPackets.asMap().entrySet().stream() + .filter(ent -> ent.getKey().isIncoming() == incoming) + .map(Map.Entry::getValue) + .flatMap(Collection::stream), HandshakeType.ENCRYPTED_HANDSHAKE_MESSAGE)) + .getContent(); + } + + private HandshakeRecordContent getHandshake(Stream stream, HandshakeType handshakeType) { + return stream.filter(p -> p.getContentType() == ContentType.HANDSHAKE) + .map(p -> ((HandshakeRecord) p.getRecord())) + .filter(r -> r.getHandshakeType() == handshakeType) + .map(r -> ((BasicRecordContent) r.getContent())) + .findFirst() + .orElseThrow(); + } + + private Optional getHandshake(Collection packets, HandshakeType handshakeType) { return packets.stream() - .flatMap(Collection::stream) .filter(p -> p.getContentType() == ContentType.HANDSHAKE) .map(p -> ((HandshakeRecord) p.getRecord())) .filter(r -> r.getHandshakeType() == handshakeType) diff --git a/src/test/java/ru/serega6531/packmate/TlsDecryptorTest.java b/src/test/java/ru/serega6531/packmate/TlsDecryptorTest.java index 7c49cdf..c4a787c 100644 --- a/src/test/java/ru/serega6531/packmate/TlsDecryptorTest.java +++ b/src/test/java/ru/serega6531/packmate/TlsDecryptorTest.java @@ -2,8 +2,10 @@ package ru.serega6531.packmate; import org.junit.jupiter.api.Test; import ru.serega6531.packmate.model.Packet; +import ru.serega6531.packmate.service.optimization.RsaKeysHolder; import ru.serega6531.packmate.service.optimization.TlsDecryptor; +import java.io.File; import java.io.IOException; import java.util.List; @@ -13,7 +15,12 @@ public class TlsDecryptorTest { public void testDecryptTls() throws IOException { List packets = new PackmateDumpFileLoader("tls.pkmt").getPackets(); - TlsDecryptor decryptor = new TlsDecryptor(packets); + RsaKeysHolder keysHolder = new RsaKeysHolder(); + File pemFile = new File(getClass().getClassLoader().getResource("tls.pem").getFile()); + File keyFile = new File(getClass().getClassLoader().getResource("tls.key").getFile()); + keysHolder.addKey(pemFile, keyFile); + + TlsDecryptor decryptor = new TlsDecryptor(packets, keysHolder); decryptor.decryptTls(); } diff --git a/src/test/resources/tls.pkmt b/src/test/resources/tls.pkmt index 5b7bdc7..67e4739 100644 --- a/src/test/resources/tls.pkmt +++ b/src/test/resources/tls.pkmt @@ -9,4 +9,8 @@ out in 17030302b00ac07f953e2f4e10a46082de38b31b9a45e70f008e545d48da877ce854238fe9b5f32dfde15b51c1e0c92410a169bb015287d92771ca2f8e64bcaec6da2ee03e793298a8f67ff5892e228dcac6d0d5c7e76e2ee98aaf394bd728a8d791b082bd05b6f4dce8d9d78e29fcf201c54d1cc721e7cfeeb3d9e0ee1d09acf03f6cd39a4a9581fa2cc01e81482daf39988f6ac451eaec4556827d8e928ddb5e761f5b005bacde9c129db4d0ae068063ae6cf24775a57c37438275a6dfab4621436617b3a197a958d01c60c6db21fec6f0ffd19a66bba730f3f91da0dd99407d17a03a9a0dcc6cac6bd89e1ad3e47b0e52f4c6e21b603d7843ef852526e597ef07a045b7c5ec9b2468e11a465e32b4349b17d2ac90c683d0fd510f81c53879679da22355eb4fae89574843941e3993e530ce956efc3b50807fa687912a4c3b5a087a926a41c49993c8da60f7873a41186eae562e8a9ec7367e6808f4cc83f5e67b0fc79e2efc4e5b734a519f2aa7b3dfb89dd848c0da255d3f20f6d169e8566788c38ef54b543ae41073123d44474e514b883f956ee1330792cab686db3e95363123dfa0b4246e139a365784d56c5bbd6db9a8099c5117487b8b1c313741921784c4b399be18a59bd98d729f9d048ecf2f0dae6c782a2d6ed8b45dfe5f47388d8da99b213e26a6a284ff1758ada6bdddbb991b62b410415bd38f823f57f83dafaa177ac86e68bfc17de4d7cf4333bf5098947913072491e9c3341ae1b95c8710b90a3dafc55f0643e8d240dd79259958c115b15520a98cdf7a43b3a7c6647aadf417bdacb2e7560763cdfa827ad5ce6846d267af4730dd221c9ac1e4adb6f47648af732bd88ecc6da2c13e0bd6881ee16fcc34830928737a52614fc80b6ceb7dd5dfffe95c377a999eb3505a9b82c13ae4da71d21bbb33583b12663364a3538c77df683adc78936ba525dd7c0e2fa662e505a2df out -170303011072181c58ad94344f833a3c6517232928ff5614ef47044478a71d54c4de8a6e9edc2d0808128d6b6440230c6cfecc653e3e02536f9bd7bcae8cb0668627147ba3b548725a3549dc31a5b69cb15654e692610d8472be09c083d47a7ef0dd4ea464845e62c9fd2b88def3c3feb2367309c4d2440d7bd3d89b26ae67c947c9887257462bc7f5e5088bbe15b132fe01e5eabc8cd60ae31db08046d5b0dbc4ac1705c93b7c74ce741d4e52a5c8591e214877f38bc0b9216a4bd8527ff10878b5f8cec4d37427c021fad6de75f643e6b83466a5833493e6bab8e07e5b6e73ad3b8a3171ddd1dc91a709476eb90d40e1994c3b7d2d8ddde97c23fff4fbb34435d90bd68f8f6b645c80e9bd5d0dba6d36ecf2d420 \ No newline at end of file +170303011072181c58ad94344f833a3c6517232928ff5614ef47044478a71d54c4de8a6e9edc2d0808128d6b6440230c6cfecc653e3e02536f9bd7bcae8cb0668627147ba3b548725a3549dc31a5b69cb15654e692610d8472be09c083d47a7ef0dd4ea464845e62c9fd2b88def3c3feb2367309c4d2440d7bd3d89b26ae67c947c9887257462bc7f5e5088bbe15b132fe01e5eabc8cd60ae31db08046d5b0dbc4ac1705c93b7c74ce741d4e52a5c8591e214877f38bc0b9216a4bd8527ff10878b5f8cec4d37427c021fad6de75f643e6b83466a5833493e6bab8e07e5b6e73ad3b8a3171ddd1dc91a709476eb90d40e1994c3b7d2d8ddde97c23fff4fbb34435d90bd68f8f6b645c80e9bd5d0dba6d36ecf2d420 +in +1703030250f6ec86ac5c98a6a04d7af7093380c07da57e695d6d00aca7c04dfd0ec4c7037de18370fec2a4cca0944e5f6f05c71310866d0e051a6cacf05aa974e7ba0c1c0246e7f80f8f88782134e027a77f5ac1c2429992bf767f24a3ea62290acea854f640254572d63356509e13913032a5c2bf47c9ff21582935509c2c411d3062dc4c9ac3ec1bdfeb811a4bf941b8e14654c8a59ec2d3dfa79ca6af286071176fca09daeaac644b8f4668b477195cb55e43651d0e17432dc9d4d905bf4df98f543470faa59776216eeb24939476536e650365c97faea4e1c238c4f019f4e7b47bc2e10badd8eddb54223a3723199f5acbf1c6b711cbba1824d605c4f4bf5d1bacc5403b9dd31b713977b5b09bdd37613883fcd9466f51a0f30100037217cf30c0d9660d031d047115308969d10cc4f694cbe6d5acbaf414906f34740a5d22954fb084d501d96a7410ed874b345193fb0c1e23acd994b6cae4c06a8c68c7bea4f617d20b8ab3df57fc02cf045770b3fc152f626b6e14575a0dd13f1259b1e63d5209e00a296f3a0374cbdc21730430b6422d4ea8e3388b2a5f81e889f915146526f6a38449e369381f2acd120d8a01aeedb41b9eb951e922d12ddd080ced1a7b143bb92344f7b8e645054ed4ac2e5c6457bc3f73963121646f5ed69ea961bdb4303e13be2c352461d48869b5d12019e109e263e56f22bb7fe8f2bd78b89c88a9d7dd014e9e5ac79669a8ec6885779a94663e77a13792637e0f2f3b2535cfb30cbe4e994d05101a321dffa355cd6c34b8f38f159e743325634b0181e5016292d22e0d493ab028ae183afe7b10b8bb52281c7e1d +out +17030300e06546909140faad74b8c74145f1e8b239d54e73a8cdb7484f8cf272b1d5fe6e55ff64e284b4c5d52e3ce27a5bf2b6f7e3e6593b47041761f622e8c7016efdc5a3ca9e5f343a5564d3201fa80f980dbb02154aaab808fb0bfe612aaa2d56385600b6957e87ab8e7b85ae5e55591db9218217d47e1b4d6950752e72b3a92bc252dd92db70b431277f5100f02939e89a0d16b2e7532253afa86be8821ab69d187d0c93f04b9d50e86339f4f599088b68d82e23f8b032cd3d5ffd3d03664bddd98f917c2470634f1454973765b066dda41edd87514bff3187103cede2a78e9807943f \ No newline at end of file