From e019ade85d8544b08f7f65de447fb4eaf9b3fd63 Mon Sep 17 00:00:00 2001 From: serega6531 Date: Thu, 23 Apr 2020 12:44:26 +0300 Subject: [PATCH] =?UTF-8?q?PRF=20=D0=B1=D0=B5=D1=80=D0=B5=D1=82=D1=81?= =?UTF-8?q?=D1=8F=20=D1=87=D0=B5=D1=80=D0=B5=D0=B7=20=D0=A3=D0=BF=D1=80?= =?UTF-8?q?=D1=83=D0=B3=D0=B8=D0=B9=D0=97=D0=B0=D0=BC=D0=BE=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 + .../service/optimization/TlsDecryptor.java | 20 ++-- .../ru/serega6531/packmate/utils/HMAC.java | 103 ---------------- .../ru/serega6531/packmate/utils/PRF.java | 110 ------------------ 4 files changed, 15 insertions(+), 220 deletions(-) delete mode 100644 src/main/java/ru/serega6531/packmate/utils/HMAC.java delete mode 100644 src/main/java/ru/serega6531/packmate/utils/PRF.java diff --git a/build.gradle b/build.gradle index edcca92..a46afa4 100644 --- a/build.gradle +++ b/build.gradle @@ -36,6 +36,8 @@ dependencies { compile 'org.pcap4j:pcap4j-packetfactory-static:1.8.2' compile group: 'com.google.guava', name: 'guava', version: '28.2-jre' compile group: 'org.java-websocket', name: 'Java-WebSocket', version: '1.5.0-SNAPSHOT' + compile group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.65' + compile group: 'org.bouncycastle', name: 'bctls-jdk15on', version: '1.65' compileOnly 'org.projectlombok:lombok' runtimeOnly 'org.springframework.boot:spring-boot-devtools' runtimeOnly 'org.postgresql:postgresql' 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 7bd6af0..a4dca7b 100644 --- a/src/main/java/ru/serega6531/packmate/service/optimization/TlsDecryptor.java +++ b/src/main/java/ru/serega6531/packmate/service/optimization/TlsDecryptor.java @@ -3,6 +3,11 @@ package ru.serega6531.packmate.service.optimization; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; 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 ru.serega6531.packmate.model.Packet; import ru.serega6531.packmate.service.optimization.tls.TlsPacket; import ru.serega6531.packmate.service.optimization.tls.keys.TlsKeyUtils; @@ -14,7 +19,6 @@ import ru.serega6531.packmate.service.optimization.tls.records.handshakes.BasicR 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.PRF; import ru.serega6531.packmate.utils.TlsUtils; import javax.crypto.Cipher; @@ -50,7 +54,7 @@ public class TlsDecryptor { .collect(Collectors.toMap(p -> p, this::createTlsHeaders)); ClientHelloHandshakeRecordContent clientHello = (ClientHelloHandshakeRecordContent) - getHandshake(tlsPackets.values(), HandshakeType.CLIENT_HELLO).orElseThrow(); + getHandshake(tlsPackets.values(), HandshakeType.CLIENT_HELLO).orElseThrow(); ServerHelloHandshakeRecordContent serverHello = (ServerHelloHandshakeRecordContent) getHandshake(tlsPackets.values(), HandshakeType.SERVER_HELLO).orElseThrow(); @@ -59,7 +63,7 @@ public class TlsDecryptor { 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.find(); String blockCipher = matcher.group(1); @@ -76,8 +80,10 @@ public class TlsDecryptor { byte[] randomCS = ArrayUtils.addAll(clientRandom, serverRandom); byte[] randomSC = ArrayUtils.addAll(serverRandom, clientRandom); - byte[] masterSecret = PRF.getBytes(preMaster, "master secret", randomCS, 48); - byte[] expanded = PRF.getBytes(masterSecret, "key expansion", randomSC, 136); + 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[] clientMacKey = new byte[20]; byte[] serverMacKey = new byte[20]; @@ -94,7 +100,7 @@ public class TlsDecryptor { bb.get(clientIV); bb.get(serverIV); - Cipher aes = Cipher.getInstance("AES/CBC/NoPadding"); // TLS_RSA_WITH_AES_256_CBC_SHA + 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); @@ -118,7 +124,7 @@ public class TlsDecryptor { } private Optional getHandshake(Collection> packets, - HandshakeType handshakeType) { + HandshakeType handshakeType) { return packets.stream() .flatMap(Collection::stream) .filter(p -> p.getContentType() == ContentType.HANDSHAKE) diff --git a/src/main/java/ru/serega6531/packmate/utils/HMAC.java b/src/main/java/ru/serega6531/packmate/utils/HMAC.java deleted file mode 100644 index 9ec336f..0000000 --- a/src/main/java/ru/serega6531/packmate/utils/HMAC.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2001-2011 Joel Hockey (joel.hockey@gmail.com). All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package ru.serega6531.packmate.utils; - -import java.security.MessageDigest; - -/** - * https://github.com/joelhockey/tls/blob/master/src/main/java/net/java/jless/tls/HMAC.java - */ -public class HMAC { - - private byte[] k_ipad = new byte[64]; - private byte[] k_opad = new byte[64]; - MessageDigest md = null; - - /** - * Class constructor specifying the MessageDigest and secret to use - * - * @param md the MessageDigest (MD5 or SHA1). - * @param key the secret to seed the md. - */ - public HMAC(MessageDigest md, byte[] key) { - setMD(md); - setKey(key); - } - - /** - * Set the MessageDigest for HMAC - * - * @param md the MessageDigest - */ - public void setMD(MessageDigest md) { - this.md = md; - } - - /** - * Set the secret key for HMAC - * - * @param key the key. - */ - public void setKey(byte[] key) { - int keyLength = 0; - - // get keyLength. - if (key == null) { - keyLength = 0; - } else { - keyLength = key.length; - } - - // if the key is longer than 64 bytes then hash it. - byte[] tempKey = keyLength > 64 ? md.digest(key) : key; - - // get m_k_ipad and m_k_opad - for (int i = 0; i < keyLength; i++) { - k_ipad[i] = (byte) (0x36 ^ tempKey[i]); - k_opad[i] = (byte) (0x5C ^ tempKey[i]); - } - - for (int i = keyLength; i < 64; i++) { - k_ipad[i] = 0x36; - k_opad[i] = 0x5C; - } - } - - /** - * Digest the HMAC - * - * @param input the byte array input - * @return HMAC value - */ - public byte[] digest(byte[] input) { - - md.reset(); - md.update(k_ipad); - md.update(input); - byte[] inner = md.digest(); - md.update(k_opad); - md.update(inner); - return md.digest(); - } - -} diff --git a/src/main/java/ru/serega6531/packmate/utils/PRF.java b/src/main/java/ru/serega6531/packmate/utils/PRF.java deleted file mode 100644 index 9a934e4..0000000 --- a/src/main/java/ru/serega6531/packmate/utils/PRF.java +++ /dev/null @@ -1,110 +0,0 @@ -package ru.serega6531.packmate.utils; - -import lombok.experimental.UtilityClass; - -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - -/** - * Based on PRF.java - */ -@UtilityClass -public class PRF { - - private static final MessageDigest md5; - private static final MessageDigest sha; - private static final HMAC hmac = new HMAC(null, null); - - static { - try { - md5 = MessageDigest.getInstance("MD5"); - sha = MessageDigest.getInstance("SHA"); - } catch (NoSuchAlgorithmException e) { - throw new IllegalStateException("MD5 or SHA failed to initialize"); - } - } - - /** - * Generates the PRF of the given inputs - * @param secret - * @param label - * @param seed - * @param length The length of the output to generate. - * @return PRF of inputs - */ - public byte[] getBytes(byte[] secret, String label, byte[] seed, int length) { - - byte[] output = new byte[length]; - - // split secret into S1 and S2 - int lenS1 = secret.length / 2 + secret.length % 2; - - byte[] s1 = new byte[lenS1]; - byte[] s2 = new byte[lenS1]; - - System.arraycopy(secret, 0, s1, 0, lenS1); - System.arraycopy(secret, secret.length - lenS1, s2, 0, lenS1); - - // get the seed as concatenation of label and seed - byte[] labelAndSeed = new byte[label.length() + seed.length]; - System.arraycopy(label.getBytes(), 0, labelAndSeed, 0, label.length()); - System.arraycopy(seed, 0, labelAndSeed, label.length(), seed.length); - - byte[] md5Output = p_hash(md5, 16, s1, labelAndSeed, length); - byte[] shaOutput = p_hash(sha, 20, s2, labelAndSeed, length); - - // XOR md5 and sha to get output - for (int i = 0; i < length; i++) { - output[i] = (byte) (md5Output[i] ^ shaOutput[i]); - } - - return output; - } - - /** - * Perform the P_hash function - * @param md The MessageDigest function to use - * @param digestLength The length of output from the given digest - * @param secret The TLS secret - * @param seed The seed to use - * @param length The desired length of the output. - * @return The P_hash of the inputs. - */ - private byte[] p_hash(MessageDigest md, int digestLength, byte[] secret, - byte[] seed, int length) { - - // set up our hmac - hmac.setMD(md); - hmac.setKey(secret); - - byte[] output = new byte[length]; // what we return - int offset = 0; // how much data we have created so far - int toCopy = 0; // the amount of data to copy from current HMAC - - byte[] a = seed; // initialise A(0) - - // concatenation of A and seed - byte[] aSeed = new byte[digestLength + seed.length]; - System.arraycopy(seed, 0, aSeed, digestLength, seed.length); - - byte[] tempBuf = null; - - // continually perform HMACs and concatenate until we have enough output - while( offset < length ) { - - // calculate the A to use. - a = hmac.digest(a); - - // concatenate A and seed and perform HMAC - System.arraycopy(a, 0, aSeed, 0, digestLength); - tempBuf = hmac.digest(aSeed); - - // work out how much needs to be copied and copy it - toCopy = Math.min(tempBuf.length, (length - offset)); - System.arraycopy(tempBuf, 0, output, offset, toCopy); - offset += toCopy; - } - return output; - } - -}