Skip to content

Commit

Permalink
remove need for template certificate
Browse files Browse the repository at this point in the history
  • Loading branch information
danthe1st committed Feb 29, 2024
1 parent cb104ef commit 5d6dfef
Show file tree
Hide file tree
Showing 6 changed files with 35 additions and 22 deletions.
15 changes: 7 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,12 @@ If a malicous entity gets access to the key,
they can read or change most encrypted communication performed using the device trusting the certificate.

## How?
The `certs.sh` script creates a CA certificate and a template certificate for the server.
The CA certificate is exported into a file `root.pem`.
This file can then be imported in trust stores of browsers or Operating Systems.
Adding a CA certificate to the Linux trust store is explained [here](https://askubuntu.com/a/94861/966107).
The `certs.sh` script creates a CA certificate and exports it into the file `root.pem` and `root.crt`.
These files can then be imported in trust stores of browsers or Operating Systems.
Adding a CA certificate to the Linux trust store is explained [here](https://askubuntu.com/a/94861/966107) (though it would be necessary to use the `root.pem` here).

The Java program loads the keys and certificate and when it receives an HTTPs request,
it decrypts it, encrypts it again for the intended server and forwards it.
it decrypts the request, encrypts it again for the specified server and forwards it.
Responses from the server are also decrypted and re-encrypted before being sent to the client.
This is commonly known as a "Man in the Middle" attack.

Expand All @@ -35,11 +34,11 @@ I thought it might be interesting to possibly build a simple ad-blocker-like pro
operating on the system-level by attacking TLS.

## Setup
- run the `certs.sh` script in order to generate a CA and template server certificate.
- run the `certs.sh` script in order to generate a CA certificate.
This generates the following files:
- `root.pem` and `root.crt` containing the CA certificate
- `interceptor.jks` containing the keystore with the private keys and certificates - This file is PRIVATE
- `.secrets` containing the passphrases for the keystore and private keys - This file is PRIVATE
- `interceptor.jks` containing the keystore with the private key and certificate - This file is PRIVATE
- `.secrets` containing the passphrases for the keystore and private key - This file is PRIVATE
- Run the program (the main class is `io.github.danthe1st.httpsintercept.HttpsIntercept`)
This requires the files `interceptor.jks` and `.secrets` to be located in the current working directory.
- The program should listen on port `1337` and forward requests to port `443`.
Expand Down
2 changes: 0 additions & 2 deletions certs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ set -e
# create CA
keytool -keystore "$KEYSTORE_NAME" -storepass "$keystorePassphrase" -alias root -dname "cn=RootCA, ou=Root_CertificateAuthority, o=CertificateAuthority, c=AT" -genkeypair -keyalg RSA -ext bc:c
chmod 600 "$KEYSTORE_NAME"
# create template for server certificate
keytool -keystore "$KEYSTORE_NAME" -storepass "$keystorePassphrase" -alias server -dname "cn=$SERVER_CN, ou=ServerCertOU, o=personal, c=AT" -genkeypair -keyalg RSA

# export root CA so that it can be imported into browsers/the OS
keytool -keystore "$KEYSTORE_NAME" -storepass "$keystorePassphrase" -export -alias root > "$ROOT_CERT_FILE"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
Expand Down Expand Up @@ -88,4 +90,9 @@ private static X500Name getSubject() {
);
}

public static KeyPair generateKeyPair() throws NoSuchAlgorithmException {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
return keyGen.generateKeyPair();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.io.IOException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;

import io.github.danthe1st.httpsintercept.handler.ServerHandlersInit;
Expand All @@ -19,7 +20,7 @@
public class HttpsIntercept {
private static final int LOCAL_PORT = Integer.getInteger("localPort", 1337);

public static void main(String[] args) throws InterruptedException, IOException, KeyStoreException, NoSuchAlgorithmException, CertificateException {
public static void main(String[] args) throws InterruptedException, IOException, KeyStoreException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException {
EventLoopGroup bossGroup = new NioEventLoopGroup(r -> Thread.ofVirtual().unstarted(r));// NOSONAR using shutdownGracefully instead of close
EventLoopGroup workerGroup = new NioEventLoopGroup(r -> Thread.ofVirtual().unstarted(r));// NOSONAR using shutdownGracefully instead of close

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.io.IOException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;

import io.github.danthe1st.httpsintercept.handler.http.IncomingHttpRequestHandler;
Expand All @@ -27,7 +28,7 @@ public class ServerHandlersInit extends ChannelInitializer<SocketChannel> {
private final SslContext clientSslContext;
private final SNIHandlerMapping sniMapping;

public ServerHandlersInit(Bootstrap clientBootstrap) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
public ServerHandlersInit(Bootstrap clientBootstrap) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, UnrecoverableKeyException {
this.clientBootstrapTemplate = clientBootstrap;
sniMapping = SNIHandlerMapping.createMapping();
clientSslContext = SslContextBuilder.forClient().build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,13 @@ public class SNIHandlerMapping implements Mapping<String, SslContext> {
private static final Duration CACHE_INVALIDATION_DURATION = Duration.ofSeconds(10);

private final KeyStore ks;
private final char[] privateKeyPassword;
private final Map<String, SslContextCacheEntry> certificateCache = new ConcurrentHashMap<>();
private final KeyPair rootKeyPair;
private final X509Certificate rootCert;

private final KeyPair serverKeyPair;

private SNIHandlerMapping() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
private SNIHandlerMapping() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException {

Path secretFile = Path.of(".secret");
if(!Files.exists(secretFile)){
Expand All @@ -51,7 +54,7 @@ private SNIHandlerMapping() throws KeyStoreException, IOException, NoSuchAlgorit
}

char[] passphrase = secretLines.get(0).toCharArray();
privateKeyPassword = secretLines.get(1).toCharArray();
char[] privateKeyPassword = secretLines.get(1).toCharArray();

LOG.info("Initiating SSL context");

Expand All @@ -60,9 +63,18 @@ private SNIHandlerMapping() throws KeyStoreException, IOException, NoSuchAlgorit
try(InputStream is = Files.newInputStream(Path.of(KEYSTORE))){
ks.load(is, passphrase);
}

rootCert = (X509Certificate) ks.getCertificate("root");

rootKeyPair = new KeyPair(
rootCert.getPublicKey(),
(PrivateKey) ks.getKey("root", privateKeyPassword)
);

serverKeyPair = CertificateGenerator.generateKeyPair();
}

public static SNIHandlerMapping createMapping() throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
public static SNIHandlerMapping createMapping() throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, UnrecoverableKeyException {
SNIHandlerMapping mapping = new SNIHandlerMapping();
Thread.startVirtualThread(mapping::runCleanupDaemon);
return mapping;
Expand Down Expand Up @@ -101,14 +113,9 @@ public SslContext map(String hostname) {

private SslContext createSslContext(String hostname) {
try{
char[] passphrase = privateKeyPassword;
KeyPair serverKeyPair = extractKeyPair(ks, "server", passphrase);
KeyPair rootKeyPair = extractKeyPair(ks, "root", passphrase);
X509Certificate rootCert = (X509Certificate) ks.getCertificate("root");
X509Certificate newCert = CertificateGenerator.createCertificate(serverKeyPair, hostname, rootKeyPair, rootCert, false);

return SslContextBuilder.forServer(serverKeyPair.getPrivate(), (String)null, newCert, rootCert).build();
}catch(SSLException | KeyStoreException | CertIOException | OperatorCreationException | CertificateException | UnrecoverableKeyException | NoSuchAlgorithmException e){
}catch(SSLException | CertIOException | OperatorCreationException | CertificateException e){
throw new CertificateGenerationException("ailed to initialize the server-side SSLContext", e);
}
}
Expand Down

0 comments on commit 5d6dfef

Please sign in to comment.