Работа над расшифровкой TLS
This commit is contained in:
28
rsa_keys/example.key
Normal file
28
rsa_keys/example.key
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDWJO65om/5LMA0
|
||||||
|
8w9Uk36h4ukQ7Qt8nbpbeHzxorl4lGwWBASEAEmDYNUcGO0CxglOE93F9BPNGn6q
|
||||||
|
Vj8Ypp3kcTGOzsXcFrd0wRpXbSwbynnmqTCYigiLzIidasfUrGul4s1fVZFdkQZS
|
||||||
|
p2Y5pEUxq1GKcAgCVwjMyWC1dhGqvTcA5ps0JoSRoA+Nzs/BeTHlTm8UvT9eD9ER
|
||||||
|
8RmYVOi1edcJ/eztj1CVydq5X27QNmwLuqsAwq38I27nlq1NU5ShqDQ16bg8IY/c
|
||||||
|
Ll4QJB7SVbrLf3dJ7KY5i7DNEoYUiJGRwDJZt+wcZLtFSzj0cn0BuEU6M0PYglUI
|
||||||
|
uQTeosUZAgMBAAECggEAehq7CJyHzoPm4QpLDoW/qh1RmfYgG6FwVqHgVIPdz9SJ
|
||||||
|
wQ/vZzkmscPwIEJSOsejHKMVTL983vGhkgz1k1/GHjEw+eYLShCl8Ov+0iUNBpew
|
||||||
|
ZIbKj9/9OYGZ0HDHmwvpocAuLJME/V4pRc3v6yQw1D6EkzSITJVGDkcxXqcBMeIA
|
||||||
|
uNVr+pwLH9vO7ybva+e3T4ROWxlecHrcB94THops4fy5+SGVILwvKaP4cRhjLfD4
|
||||||
|
2XV4O5N0imdPAYsNNHyHbAzjvZPoCOsuH3B/tWmRHq3oOa4ZcFUNTDmO9GgfbtY/
|
||||||
|
PHEFV34XxMjy3bK0vLxHqS9CEj1cvfq8e1NqkDTugQKBgQD6CEezGf9OFb3byBui
|
||||||
|
X3OzXWdWQ5jnodOTPb/P+y9DrORJPy1/0BcXh/cHF58kNDZvzVwTFcAjfx6bxS41
|
||||||
|
JAddFRZjNuHXEOtFRkD3Wp4W7Atrv/yeKbpE9PCaNYtUDasL8RKcdJiHNFpN4xRl
|
||||||
|
jpQtIiQ9pikrjUXLgW0S88zzyQKBgQDbQV+DMxGS2Cee6nfMmUcGjgQd8D0cXLjk
|
||||||
|
OZSmEnk4FCvV8ZdysjirqmuitFTE+PYmOJzhlQl8lubEs4Kc7L9CfEwbK9mNN0ZG
|
||||||
|
BNdT21nFuJp7YoZzZDTHuwF0nBjQFYcdaWDW+qFqrqs9mKbmCQ5vSzql6al+pzdX
|
||||||
|
X/YS0QTO0QKBgDUMprHQdUPLByJnnb1gxTqsOa2q3/ldc3eNJXJqWAfi2fjUh8HT
|
||||||
|
k+KxPW9qyqAy1832429FMSQW55ajSn+J6moMfFiGn3ozI8fp9QTGXD5+zJmK/X1N
|
||||||
|
WzEgSyBc9ffago0hFBLQBkDBkdtur7gwfS3qTYgrBhcwfTuFdXAM/FJJAoGABIQ2
|
||||||
|
OXel1waI2mcuDJLjuajXQN6gA6ONU3Y0L6+Vu6f+tyuA2SX+sNqT2Qgp7tzKBUOJ
|
||||||
|
R8RQK7bYDhk8iYr+7Zmt36lpk9Udp3eWD+4mzUHePMhsyJe51pttjj9g63hmDh8L
|
||||||
|
laIYDSCH+n7YgUiSeYxtKtnDWg6Lv0sEwKJ5nOECgYBsF5PoHRE4Q/Vs18qbI4t/
|
||||||
|
zPwWWNP0sb3PYRlWLTKMBowQdDOxnXAF12txoLNhpOn9DjZdNEb2EMsqlzdNjphN
|
||||||
|
uUWZq89d5kDwKfj4ji087elcjsW79R5oqwrN8a0NimftZ4eBPbcn8Y0r5psPcSzE
|
||||||
|
36iKGM2euQYD8Ub+aDOSLQ==
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
@@ -55,6 +55,8 @@ public class Packet {
|
|||||||
|
|
||||||
private boolean webSocketParsed;
|
private boolean webSocketParsed;
|
||||||
|
|
||||||
|
private boolean tlsDecrypted;
|
||||||
|
|
||||||
private byte[] content;
|
private byte[] content;
|
||||||
|
|
||||||
@Transient
|
@Transient
|
||||||
|
|||||||
@@ -137,6 +137,7 @@ public class HttpChunksProcessor {
|
|||||||
.timestamp(packets.get(0).getTimestamp())
|
.timestamp(packets.get(0).getTimestamp())
|
||||||
.ungzipped(false)
|
.ungzipped(false)
|
||||||
.webSocketParsed(false)
|
.webSocketParsed(false)
|
||||||
|
.tlsDecrypted(packets.get(0).isTlsDecrypted())
|
||||||
.content(output.toByteArray())
|
.content(output.toByteArray())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
|||||||
@@ -105,6 +105,7 @@ public class HttpGzipProcessor {
|
|||||||
.timestamp(cut.get(0).getTimestamp())
|
.timestamp(cut.get(0).getTimestamp())
|
||||||
.ungzipped(true)
|
.ungzipped(true)
|
||||||
.webSocketParsed(false)
|
.webSocketParsed(false)
|
||||||
|
.tlsDecrypted(cut.get(0).isTlsDecrypted())
|
||||||
.content(newContent)
|
.content(newContent)
|
||||||
.build();
|
.build();
|
||||||
} catch (ZipException e) {
|
} catch (ZipException e) {
|
||||||
|
|||||||
@@ -50,7 +50,8 @@ public class PacketsMerger {
|
|||||||
final long timestamp = cut.get(0).getTimestamp();
|
final long timestamp = cut.get(0).getTimestamp();
|
||||||
final boolean ungzipped = cut.stream().anyMatch(Packet::isUngzipped);
|
final boolean ungzipped = cut.stream().anyMatch(Packet::isUngzipped);
|
||||||
final boolean webSocketParsed = cut.stream().anyMatch(Packet::isWebSocketParsed);
|
final boolean webSocketParsed = cut.stream().anyMatch(Packet::isWebSocketParsed);
|
||||||
boolean incoming = cut.get(0).isIncoming();
|
final boolean tlsDecrypted = cut.get(0).isTlsDecrypted();
|
||||||
|
final boolean incoming = cut.get(0).isIncoming();
|
||||||
//noinspection OptionalGetWithoutIsPresent
|
//noinspection OptionalGetWithoutIsPresent
|
||||||
final byte[] content = PacketUtils.mergePackets(cut).get();
|
final byte[] content = PacketUtils.mergePackets(cut).get();
|
||||||
|
|
||||||
@@ -60,6 +61,7 @@ public class PacketsMerger {
|
|||||||
.timestamp(timestamp)
|
.timestamp(timestamp)
|
||||||
.ungzipped(ungzipped)
|
.ungzipped(ungzipped)
|
||||||
.webSocketParsed(webSocketParsed)
|
.webSocketParsed(webSocketParsed)
|
||||||
|
.tlsDecrypted(tlsDecrypted)
|
||||||
.content(content)
|
.content(content)
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,41 +1,74 @@
|
|||||||
package ru.serega6531.packmate.service.optimization;
|
package ru.serega6531.packmate.service.optimization;
|
||||||
|
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||||
import org.springframework.context.event.EventListener;
|
import org.springframework.context.event.EventListener;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import ru.serega6531.packmate.utils.TlsUtils;
|
|
||||||
|
|
||||||
import javax.net.ssl.X509KeyManager;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.security.KeyFactory;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.interfaces.RSAPrivateKey;
|
import java.security.interfaces.RSAPrivateKey;
|
||||||
|
import java.security.spec.InvalidKeySpecException;
|
||||||
|
import java.security.spec.PKCS8EncodedKeySpec;
|
||||||
|
import java.util.Base64;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
|
@Slf4j
|
||||||
public class RsaKeysHolder {
|
public class RsaKeysHolder {
|
||||||
|
|
||||||
// Key: N from RSA public key
|
// Key: N from RSA public key
|
||||||
private final Map<BigInteger, RSAPrivateKey> keys = new HashMap<>();
|
private final Map<BigInteger, RSAPrivateKey> 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);
|
|
||||||
|
|
||||||
RSAPrivateKey privateKey = ((RSAPrivateKey) keyManager.getPrivateKey("1"));
|
|
||||||
keys.put(privateKey.getModulus(), privateKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
public RSAPrivateKey getKey(BigInteger modulus) {
|
public RSAPrivateKey getKey(BigInteger modulus) {
|
||||||
return keys.get(modulus);
|
return keys.get(modulus);
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventListener(ApplicationReadyEvent.class)
|
@EventListener(ApplicationReadyEvent.class)
|
||||||
public void afterStartup(ApplicationReadyEvent event) {
|
public void afterStartup(ApplicationReadyEvent event) {
|
||||||
//TODO load keys
|
File dir = new File("rsa_keys");
|
||||||
|
if (dir.exists() && dir.isDirectory()) {
|
||||||
|
for (File keyFile : Objects.requireNonNull(dir.listFiles())) {
|
||||||
|
addKey(keyFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public void addKey(File keyFile) {
|
||||||
|
if (!keyFile.exists()) {
|
||||||
|
throw new IllegalArgumentException("Key file does not exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
RSAPrivateKey privateKey = loadFromFile(keyFile);
|
||||||
|
keys.put(privateKey.getModulus(), privateKey);
|
||||||
|
String n = privateKey.getModulus().toString();
|
||||||
|
log.info("Loaded RSA key with N={}...", n.substring(0, Math.min(n.length(), 8)));
|
||||||
|
} catch (IOException | InvalidKeySpecException e) {
|
||||||
|
log.error("Error loading rsa key", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private RSAPrivateKey loadFromFile(File keyFile) throws IOException, InvalidKeySpecException, NoSuchAlgorithmException {
|
||||||
|
String content = Files.readString(keyFile.toPath());
|
||||||
|
|
||||||
|
content = content.replaceAll("-----BEGIN (RSA )?PRIVATE KEY-----", "")
|
||||||
|
.replaceAll("-----END (RSA )?PRIVATE KEY-----", "")
|
||||||
|
.replace("\n", "");
|
||||||
|
|
||||||
|
byte[] keyBytes = Base64.getDecoder().decode(content);
|
||||||
|
|
||||||
|
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
|
||||||
|
KeyFactory kf = KeyFactory.getInstance("RSA");
|
||||||
|
return (RSAPrivateKey) kf.generatePrivate(spec);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,11 @@ public class StreamOptimizer {
|
|||||||
|
|
||||||
private void decryptTls() {
|
private void decryptTls() {
|
||||||
final TlsDecryptor tlsDecryptor = new TlsDecryptor(packets, keysHolder);
|
final TlsDecryptor tlsDecryptor = new TlsDecryptor(packets, keysHolder);
|
||||||
tlsDecryptor.decryptTls(); // TODO
|
tlsDecryptor.decryptTls();
|
||||||
|
|
||||||
|
if(tlsDecryptor.isParsed()) {
|
||||||
|
packets = tlsDecryptor.getParsedPackets();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package ru.serega6531.packmate.service.optimization;
|
|||||||
|
|
||||||
import com.google.common.collect.ArrayListMultimap;
|
import com.google.common.collect.ArrayListMultimap;
|
||||||
import com.google.common.collect.ListMultimap;
|
import com.google.common.collect.ListMultimap;
|
||||||
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -22,12 +23,18 @@ import ru.serega6531.packmate.service.optimization.tls.records.ApplicationDataRe
|
|||||||
import ru.serega6531.packmate.service.optimization.tls.records.HandshakeRecord;
|
import ru.serega6531.packmate.service.optimization.tls.records.HandshakeRecord;
|
||||||
import ru.serega6531.packmate.service.optimization.tls.records.handshakes.*;
|
import ru.serega6531.packmate.service.optimization.tls.records.handshakes.*;
|
||||||
|
|
||||||
|
import javax.crypto.BadPaddingException;
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.IllegalBlockSizeException;
|
||||||
|
import javax.crypto.NoSuchPaddingException;
|
||||||
import javax.crypto.spec.IvParameterSpec;
|
import javax.crypto.spec.IvParameterSpec;
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.cert.Certificate;
|
import java.security.cert.Certificate;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
import java.security.cert.CertificateFactory;
|
import java.security.cert.CertificateFactory;
|
||||||
import java.security.interfaces.RSAPrivateKey;
|
import java.security.interfaces.RSAPrivateKey;
|
||||||
import java.security.interfaces.RSAPublicKey;
|
import java.security.interfaces.RSAPublicKey;
|
||||||
@@ -45,10 +52,18 @@ public class TlsDecryptor {
|
|||||||
private final List<Packet> packets;
|
private final List<Packet> packets;
|
||||||
private final RsaKeysHolder keysHolder;
|
private final RsaKeysHolder keysHolder;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private boolean parsed = false;
|
||||||
|
private List<Packet> result;
|
||||||
|
|
||||||
|
private ListMultimap<Packet, TlsPacket.TlsHeader> tlsPackets;
|
||||||
|
private CipherSuite cipherSuite;
|
||||||
|
private byte[] clientRandom;
|
||||||
|
private byte[] serverRandom;
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public void decryptTls() {
|
public void decryptTls() {
|
||||||
ListMultimap<Packet, TlsPacket.TlsHeader> tlsPackets = ArrayListMultimap.create(packets.size(), 1);
|
tlsPackets = ArrayListMultimap.create(packets.size(), 1);
|
||||||
|
|
||||||
packets.forEach(p -> tlsPackets.putAll(p, createTlsHeaders(p)));
|
packets.forEach(p -> tlsPackets.putAll(p, createTlsHeaders(p)));
|
||||||
|
|
||||||
var clientHello = (ClientHelloHandshakeRecordContent)
|
var clientHello = (ClientHelloHandshakeRecordContent)
|
||||||
@@ -56,10 +71,7 @@ public class TlsDecryptor {
|
|||||||
var serverHello = (ServerHelloHandshakeRecordContent)
|
var serverHello = (ServerHelloHandshakeRecordContent)
|
||||||
getHandshake(tlsPackets.values(), HandshakeType.SERVER_HELLO).orElseThrow();
|
getHandshake(tlsPackets.values(), HandshakeType.SERVER_HELLO).orElseThrow();
|
||||||
|
|
||||||
byte[] clientRandom = clientHello.getRandom();
|
cipherSuite = serverHello.getCipherSuite();
|
||||||
byte[] serverRandom = serverHello.getRandom();
|
|
||||||
|
|
||||||
CipherSuite cipherSuite = serverHello.getCipherSuite();
|
|
||||||
|
|
||||||
if (cipherSuite.name().startsWith("TLS_RSA_WITH_")) {
|
if (cipherSuite.name().startsWith("TLS_RSA_WITH_")) {
|
||||||
Matcher matcher = cipherSuitePattern.matcher(cipherSuite.name());
|
Matcher matcher = cipherSuitePattern.matcher(cipherSuite.name());
|
||||||
@@ -68,33 +80,34 @@ public class TlsDecryptor {
|
|||||||
String blockCipher = matcher.group(1); //TODO использовать не только AES256
|
String blockCipher = matcher.group(1); //TODO использовать не только AES256
|
||||||
String hashAlgo = matcher.group(2);
|
String hashAlgo = matcher.group(2);
|
||||||
|
|
||||||
var certificateHandshake = ((CertificateHandshakeRecordContent)
|
clientRandom = clientHello.getRandom();
|
||||||
getHandshake(tlsPackets.values(), HandshakeType.CERTIFICATE).orElseThrow());
|
serverRandom = serverHello.getRandom();
|
||||||
List<byte[]> chain = certificateHandshake.getRawCertificates();
|
|
||||||
byte[] rawCertificate = chain.get(0);
|
|
||||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
|
||||||
Certificate certificate = cf.generateCertificate(new ByteArrayInputStream(rawCertificate));
|
|
||||||
RSAPublicKey publicKey = (RSAPublicKey) certificate.getPublicKey();
|
|
||||||
|
|
||||||
|
decryptTlsRsa(blockCipher, hashAlgo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void decryptTlsRsa(String blockCipher, String hashAlgo) throws CertificateException, NoSuchPaddingException, NoSuchAlgorithmException {
|
||||||
|
RSAPublicKey publicKey = getRsaPublicKey();
|
||||||
RSAPrivateKey privateKey = keysHolder.getKey(publicKey.getModulus());
|
RSAPrivateKey privateKey = keysHolder.getKey(publicKey.getModulus());
|
||||||
if(privateKey == null) {
|
if (privateKey == null) {
|
||||||
log.warn("Key for modulus not found: {}", publicKey.getModulus());
|
String n = publicKey.getModulus().toString();
|
||||||
|
log.warn("Key for modulus not found: {}...", n.substring(0, Math.min(n.length(), 8)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var clientKeyExchange = (BasicHandshakeRecordContent)
|
BcTlsSecret preMaster;
|
||||||
getHandshake(tlsPackets.values(), HandshakeType.CLIENT_KEY_EXCHANGE).orElseThrow();
|
try {
|
||||||
|
preMaster = getPreMaster(privateKey);
|
||||||
|
} catch (InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
|
||||||
|
log.warn("Failed do get pre-master key", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
byte[] encryptedPreMaster = TlsKeyUtils.getClientRsaPreMaster(clientKeyExchange.getContent(), 0);
|
|
||||||
|
|
||||||
Cipher rsa = Cipher.getInstance("RSA/ECB/PKCS1Padding");
|
|
||||||
rsa.init(Cipher.DECRYPT_MODE, privateKey);
|
|
||||||
byte[] preMaster = rsa.doFinal(encryptedPreMaster);
|
|
||||||
byte[] randomCS = ArrayUtils.addAll(clientRandom, serverRandom);
|
byte[] randomCS = ArrayUtils.addAll(clientRandom, serverRandom);
|
||||||
byte[] randomSC = ArrayUtils.addAll(serverRandom, clientRandom);
|
byte[] randomSC = ArrayUtils.addAll(serverRandom, clientRandom);
|
||||||
|
|
||||||
BcTlsSecret preSecret = new BcTlsSecret(new BcTlsCrypto(null), preMaster);
|
TlsSecret masterSecret = preMaster.deriveUsingPRF(
|
||||||
TlsSecret masterSecret = preSecret.deriveUsingPRF(
|
|
||||||
PRFAlgorithm.tls_prf_sha256, ExporterLabel.master_secret, randomCS, 48);
|
PRFAlgorithm.tls_prf_sha256, ExporterLabel.master_secret, randomCS, 48);
|
||||||
byte[] expanded = masterSecret.deriveUsingPRF(
|
byte[] expanded = masterSecret.deriveUsingPRF(
|
||||||
PRFAlgorithm.tls_prf_sha256, ExporterLabel.key_expansion, randomSC, 136).extract(); // для sha256
|
PRFAlgorithm.tls_prf_sha256, ExporterLabel.key_expansion, randomSC, 136).extract(); // для sha256
|
||||||
@@ -120,8 +133,10 @@ public class TlsDecryptor {
|
|||||||
Cipher clientCipher = createCipher(clientEncryptionKey, clientIV, clientFinishedEncrypted);
|
Cipher clientCipher = createCipher(clientEncryptionKey, clientIV, clientFinishedEncrypted);
|
||||||
Cipher serverCipher = createCipher(serverEncryptionKey, serverIV, serverFinishedEncrypted);
|
Cipher serverCipher = createCipher(serverEncryptionKey, serverIV, serverFinishedEncrypted);
|
||||||
|
|
||||||
|
result = new ArrayList<>(packets.size());
|
||||||
|
|
||||||
for (Packet packet : packets) {
|
for (Packet packet : packets) {
|
||||||
List<TlsPacket.TlsHeader> tlsData = (List<TlsPacket.TlsHeader>) tlsPackets.get(packet);
|
List<TlsPacket.TlsHeader> tlsData = tlsPackets.get(packet);
|
||||||
|
|
||||||
for (TlsPacket.TlsHeader tlsPacket : tlsData) {
|
for (TlsPacket.TlsHeader tlsPacket : tlsData) {
|
||||||
if (tlsPacket.getContentType() == ContentType.APPLICATION_DATA) {
|
if (tlsPacket.getContentType() == ContentType.APPLICATION_DATA) {
|
||||||
@@ -130,20 +145,50 @@ public class TlsDecryptor {
|
|||||||
|
|
||||||
byte[] decoded;
|
byte[] decoded;
|
||||||
|
|
||||||
if(client) {
|
if (client) {
|
||||||
decoded = clientCipher.update(data);
|
decoded = clientCipher.update(data);
|
||||||
} else {
|
} else {
|
||||||
decoded = serverCipher.update(data);
|
decoded = serverCipher.update(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
decoded = clearDecodedData(decoded);
|
decoded = clearDecodedData(decoded);
|
||||||
String string = new String(decoded);
|
|
||||||
log.info(string);
|
result.add(Packet.builder()
|
||||||
}
|
.content(decoded)
|
||||||
|
.incoming(packet.isIncoming())
|
||||||
|
.timestamp(packet.getTimestamp())
|
||||||
|
.ungzipped(false)
|
||||||
|
.webSocketParsed(false)
|
||||||
|
.tlsDecrypted(true)
|
||||||
|
.ttl(packet.getTtl())
|
||||||
|
.build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parsed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BcTlsSecret getPreMaster(RSAPrivateKey privateKey) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
|
||||||
|
var clientKeyExchange = (BasicHandshakeRecordContent)
|
||||||
|
getHandshake(tlsPackets.values(), HandshakeType.CLIENT_KEY_EXCHANGE).orElseThrow();
|
||||||
|
|
||||||
|
byte[] encryptedPreMaster = TlsKeyUtils.getClientRsaPreMaster(clientKeyExchange.getContent(), 0);
|
||||||
|
|
||||||
|
Cipher rsa = Cipher.getInstance("RSA/ECB/PKCS1Padding");
|
||||||
|
rsa.init(Cipher.DECRYPT_MODE, privateKey);
|
||||||
|
byte[] preMaster = rsa.doFinal(encryptedPreMaster);
|
||||||
|
return new BcTlsSecret(new BcTlsCrypto(null), preMaster);
|
||||||
|
}
|
||||||
|
|
||||||
|
private RSAPublicKey getRsaPublicKey() throws CertificateException {
|
||||||
|
var certificateHandshake = ((CertificateHandshakeRecordContent)
|
||||||
|
getHandshake(tlsPackets.values(), HandshakeType.CERTIFICATE).orElseThrow());
|
||||||
|
List<byte[]> chain = certificateHandshake.getRawCertificates();
|
||||||
|
byte[] rawCertificate = chain.get(0);
|
||||||
|
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||||
|
Certificate certificate = cf.generateCertificate(new ByteArrayInputStream(rawCertificate));
|
||||||
|
return (RSAPublicKey) certificate.getPublicKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@@ -206,4 +251,12 @@ public class TlsDecryptor {
|
|||||||
return headers;
|
return headers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Packet> getParsedPackets() {
|
||||||
|
if (!parsed) {
|
||||||
|
throw new IllegalStateException("TLS is not parsed");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,6 +132,7 @@ public class WebSocketsParser {
|
|||||||
.ttl(lastPacket.getTtl())
|
.ttl(lastPacket.getTtl())
|
||||||
.ungzipped(lastPacket.isUngzipped())
|
.ungzipped(lastPacket.isUngzipped())
|
||||||
.webSocketParsed(true)
|
.webSocketParsed(true)
|
||||||
|
.tlsDecrypted(lastPacket.isTlsDecrypted())
|
||||||
.build()
|
.build()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
package ru.serega6531.packmate.utils;
|
|
||||||
|
|
||||||
import com.google.common.base.Splitter;
|
|
||||||
import lombok.SneakyThrows;
|
|
||||||
|
|
||||||
import javax.net.ssl.KeyManagerFactory;
|
|
||||||
import javax.net.ssl.X509KeyManager;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.security.KeyStore;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkState;
|
|
||||||
|
|
||||||
public class TlsUtils {
|
|
||||||
|
|
||||||
@SneakyThrows
|
|
||||||
public static X509KeyManager createKeyManager(File pemFile, File keyFile) {
|
|
||||||
final String pass = "abcdef";
|
|
||||||
|
|
||||||
File jksKeystoreFile = File.createTempFile("packmate_", ".jks");
|
|
||||||
File pkcsKeystoreFile = File.createTempFile("packmate_", ".pkcs12");
|
|
||||||
Splitter splitter = Splitter.on(' ');
|
|
||||||
|
|
||||||
jksKeystoreFile.delete();
|
|
||||||
|
|
||||||
String command = "openssl pkcs12 -export -out " + pkcsKeystoreFile.getAbsolutePath() + " -in " + pemFile.getAbsolutePath() +
|
|
||||||
" -inkey " + keyFile.getAbsolutePath() + " -passout pass:" + pass;
|
|
||||||
|
|
||||||
Process process = new ProcessBuilder(splitter.splitToList(command)).inheritIO().start();
|
|
||||||
checkState(process.waitFor() == 0);
|
|
||||||
|
|
||||||
command = "keytool -importkeystore -srckeystore " + pkcsKeystoreFile.getAbsolutePath() + " -srcstoretype PKCS12 -destkeystore " +
|
|
||||||
jksKeystoreFile.getAbsolutePath() + " -srcstorepass " + pass + " -deststorepass " + pass;
|
|
||||||
|
|
||||||
process = new ProcessBuilder(splitter.splitToList(command)).inheritIO().start();
|
|
||||||
checkState(process.waitFor() == 0);
|
|
||||||
|
|
||||||
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
|
|
||||||
keystore.load(new FileInputStream(jksKeystoreFile), pass.toCharArray());
|
|
||||||
|
|
||||||
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
|
||||||
keyManagerFactory.init(keystore, pass.toCharArray());
|
|
||||||
|
|
||||||
return (X509KeyManager) keyManagerFactory.getKeyManagers()[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -16,9 +16,8 @@ public class TlsDecryptorTest {
|
|||||||
List<Packet> packets = new PackmateDumpFileLoader("tls.pkmt").getPackets();
|
List<Packet> packets = new PackmateDumpFileLoader("tls.pkmt").getPackets();
|
||||||
|
|
||||||
RsaKeysHolder keysHolder = new RsaKeysHolder();
|
RsaKeysHolder keysHolder = new RsaKeysHolder();
|
||||||
File pemFile = new File(getClass().getClassLoader().getResource("tls.pem").getFile());
|
|
||||||
File keyFile = new File(getClass().getClassLoader().getResource("tls.key").getFile());
|
File keyFile = new File(getClass().getClassLoader().getResource("tls.key").getFile());
|
||||||
keysHolder.addKey(pemFile, keyFile);
|
keysHolder.addKey(keyFile);
|
||||||
|
|
||||||
TlsDecryptor decryptor = new TlsDecryptor(packets, keysHolder);
|
TlsDecryptor decryptor = new TlsDecryptor(packets, keysHolder);
|
||||||
decryptor.decryptTls();
|
decryptor.decryptTls();
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIDCTCCAfGgAwIBAgIUP8G0cbkVa5e5XMfhm0ewi9FKmVgwDQYJKoZIhvcNAQEL
|
|
||||||
BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTIwMDQxNzEzMjEyNVoXDTIxMDQx
|
|
||||||
NzEzMjEyNVowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF
|
|
||||||
AAOCAQ8AMIIBCgKCAQEA1iTuuaJv+SzANPMPVJN+oeLpEO0LfJ26W3h88aK5eJRs
|
|
||||||
FgQEhABJg2DVHBjtAsYJThPdxfQTzRp+qlY/GKad5HExjs7F3Ba3dMEaV20sG8p5
|
|
||||||
5qkwmIoIi8yInWrH1KxrpeLNX1WRXZEGUqdmOaRFMatRinAIAlcIzMlgtXYRqr03
|
|
||||||
AOabNCaEkaAPjc7PwXkx5U5vFL0/Xg/REfEZmFTotXnXCf3s7Y9QlcnauV9u0DZs
|
|
||||||
C7qrAMKt/CNu55atTVOUoag0Nem4PCGP3C5eECQe0lW6y393SeymOYuwzRKGFIiR
|
|
||||||
kcAyWbfsHGS7RUs49HJ9AbhFOjND2IJVCLkE3qLFGQIDAQABo1MwUTAdBgNVHQ4E
|
|
||||||
FgQU2nillgHV/VxE0Rf5sVlNbEogs8cwHwYDVR0jBBgwFoAU2nillgHV/VxE0Rf5
|
|
||||||
sVlNbEogs8cwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAg287
|
|
||||||
XL7cBkT3WlR4Mxocg2k7khBfhUGTU5Y9HbZcsV83vkjY6Q2zRhtB1kwNBO/KPOE+
|
|
||||||
+TUPgO9cL6wBk1QZISxUzEl1AvNxZOqUqZUubRKMzeAzoVF+ItkRxiXrQe80RVY3
|
|
||||||
IjEpdajqBKOeEg6n3/5COh3UnvcdHaFsnbCspSfAUYUO9J0s6hLPHIJVSaqEfO9p
|
|
||||||
eW2I9vUu7HnzM8bvawwzciFV0v5DrO6/2TbbfiGCYdsebsZD1QAzsWu2KTFmjGWo
|
|
||||||
pDXWcd+h7oeKTGYvRtSEU/g/IMttH6HrT/N1tpBpv9GG7FLRsICgEzcpgAmvfR7a
|
|
||||||
tg5VUZwbxAXLxKSjjw==
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
Reference in New Issue
Block a user