Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added changes for self signed cert generation #2315

Merged
merged 1 commit into from
May 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

import com.intuit.karate.FileUtils;
import com.intuit.karate.StringUtils;
import com.intuit.karate.http.cert.SelfSignedCertGenerator;
import com.intuit.karate.shell.Command;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
Expand All @@ -37,7 +38,6 @@
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.ssl.util.SelfSignedCertificate;
import java.io.File;
import java.io.FileInputStream;
import java.net.URI;
Expand Down Expand Up @@ -171,9 +171,9 @@ public static void flushAndClose(Channel ch) {

public static void createSelfSignedCertificate(File cert, File key) {
try {
SelfSignedCertificate ssc = new SelfSignedCertificate();
FileUtils.copy(ssc.certificate(), cert);
FileUtils.copy(ssc.privateKey(), key);
SelfSignedCertGenerator ssc = new SelfSignedCertGenerator();
FileUtils.copy(ssc.getCertificate(), cert);
FileUtils.copy(ssc.getPrivateKey(), key);
} catch (Exception e) {
throw new RuntimeException();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
package com.intuit.karate.http.cert;

import com.intuit.karate.Logger;
import com.linecorp.armeria.internal.shaded.bouncycastle.asn1.x500.X500Name;
import com.linecorp.armeria.internal.shaded.bouncycastle.cert.X509CertificateHolder;
import com.linecorp.armeria.internal.shaded.bouncycastle.cert.X509v3CertificateBuilder;
import com.linecorp.armeria.internal.shaded.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import com.linecorp.armeria.internal.shaded.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import com.linecorp.armeria.internal.shaded.bouncycastle.operator.ContentSigner;
import com.linecorp.armeria.internal.shaded.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.base64.Base64;
import io.netty.util.CharsetUtil;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.SystemPropertyUtil;
import io.netty.util.internal.ThrowableUtil;

import java.io.*;
import java.math.BigInteger;
import java.security.*;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Date;

public final class SelfSignedCertGenerator {
private static final Logger logger = new Logger();
private static final Provider PROVIDER = Security.getProvider("SUN");
private static final String DEFAULT_FQDN = "localhost";
private static final Date DEFAULT_NOT_BEFORE = new Date(SystemPropertyUtil.getLong("io.netty.selfSignedCertificate.defaultNotBefore", System.currentTimeMillis() - 31536000000L));
private static final Date DEFAULT_NOT_AFTER = new Date(SystemPropertyUtil.getLong("io.netty.selfSignedCertificate.defaultNotAfter", 253402300799000L));
private static final String ALGORITHM = "RSA";
private static final int DEFAULT_KEY_LENGTH_BITS = SystemPropertyUtil.getInt("io.netty.handler.ssl.util.selfSignedKeyStrength", 2048);

private File certificate;
private File privateKey;
private X509Certificate cert;
private PrivateKey key;

public SelfSignedCertGenerator() throws CertificateException {
KeyPair keypair;
SecureRandom random = ThreadLocalInsecureRandom.current();
try {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance(ALGORITHM);
keyGen.initialize(DEFAULT_KEY_LENGTH_BITS, random);
keypair = keyGen.generateKeyPair();
} catch (NoSuchAlgorithmException var24) {
throw new Error(var24);
}

String[] paths;
try {
paths = generate(DEFAULT_FQDN, keypair, random, DEFAULT_NOT_BEFORE, DEFAULT_NOT_AFTER);
} catch (Throwable var23) {
logger.debug("Failed to generate a self-signed X.509 certificate:", var23);
CertificateException certificateException = new CertificateException("No provider succeeded to generate a self-signed certificate. See debug log for the root cause.", var23);
ThrowableUtil.addSuppressed(certificateException, var23);
throw certificateException;
}

this.certificate = new File(paths[0]);
this.privateKey = new File(paths[1]);
this.key = keypair.getPrivate();
FileInputStream certificateInput = null;

try {
certificateInput = new FileInputStream(this.certificate);
this.cert = (X509Certificate) CertificateFactory.getInstance("X509").generateCertificate(certificateInput);
} catch (Exception var21) {
throw new CertificateEncodingException(var21);
} finally {
if (certificateInput != null) {
try {
certificateInput.close();
} catch (IOException var25) {
logger.warn("Failed to close a file: " + this.certificate, var25);
}
}
}
}

public File getCertificate() {
return certificate;
}

public File getPrivateKey() {
return privateKey;
}

private String[] generate(String fqdn, KeyPair keypair, SecureRandom random, Date notBefore, Date notAfter) throws Exception {
PrivateKey key = keypair.getPrivate();
X500Name owner = new X500Name("CN=" + fqdn);
X509v3CertificateBuilder builder = new JcaX509v3CertificateBuilder(owner, new BigInteger(64, random), notBefore, notAfter, owner, keypair.getPublic());
ContentSigner signer = (new JcaContentSignerBuilder("SHA256WithRSAEncryption")).build(key);
X509CertificateHolder certHolder = builder.build(signer);
X509Certificate cert = new JcaX509CertificateConverter().setProvider(PROVIDER).getCertificate(certHolder);
cert.verify(keypair.getPublic());
return newSelfSignedCertificate(fqdn, key, cert);
}

private String[] newSelfSignedCertificate(String fqdn, PrivateKey key, X509Certificate cert) throws IOException, CertificateEncodingException {
ByteBuf wrappedBuf = Unpooled.wrappedBuffer(key.getEncoded());
ByteBuf encodedBuf;
String keyText;
try {
encodedBuf = Base64.encode(wrappedBuf, true);
try {
keyText = "-----BEGIN PRIVATE KEY-----\n" + encodedBuf.toString(CharsetUtil.US_ASCII) + "\n-----END PRIVATE KEY-----\n";
} finally {
encodedBuf.release();
}
} finally {
wrappedBuf.release();
}
fqdn = fqdn.replaceAll("[^\\w.-]", "x");
File keyFile = PlatformDependent.createTempFile("keyutil_" + fqdn + '_', ".key", (File)null);
keyFile.deleteOnExit();
OutputStream keyOut = new FileOutputStream(keyFile);
try {
keyOut.write(keyText.getBytes(CharsetUtil.US_ASCII));
keyOut.close();
keyOut = null;
} finally {
if (keyOut != null) {
safeClose(keyFile, keyOut);
safeDelete(keyFile);
}
}
wrappedBuf = Unpooled.wrappedBuffer(cert.getEncoded());
String certText;
try {
encodedBuf = Base64.encode(wrappedBuf, true);
try {
certText = "-----BEGIN CERTIFICATE-----\n" + encodedBuf.toString(CharsetUtil.US_ASCII) + "\n-----END CERTIFICATE-----\n";
} finally {
encodedBuf.release();
}
} finally {
wrappedBuf.release();
}
File certFile = PlatformDependent.createTempFile("keyutil_" + fqdn + '_', ".crt", (File)null);
certFile.deleteOnExit();
OutputStream certOut = new FileOutputStream(certFile);
try {
certOut.write(certText.getBytes(CharsetUtil.US_ASCII));
certOut.close();
certOut = null;
} finally {
if (certOut != null) {
safeClose(certFile, certOut);
safeDelete(certFile);
safeDelete(keyFile);
}
}
return new String[]{certFile.getPath(), keyFile.getPath()};
}

private static void safeDelete(File certFile) {
if (!certFile.delete()) {
logger.warn("Failed to delete a file: " + certFile);
}
}

private static void safeClose(File keyFile, OutputStream keyOut) {
try {
keyOut.close();
} catch (IOException var3) {
logger.warn("Failed to close a file: " + keyFile, var3);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.intuit.karate.http.cert;

import io.netty.util.internal.PlatformDependent;
import java.security.SecureRandom;
import java.util.Random;

final class ThreadLocalInsecureRandom extends SecureRandom {
private static final long serialVersionUID = -8209473337192526191L;
private static final SecureRandom INSTANCE = new ThreadLocalInsecureRandom();

static SecureRandom current() {
return INSTANCE;
}

private ThreadLocalInsecureRandom() {
}

public String getAlgorithm() {
return "insecure";
}

public void setSeed(byte[] seed) {
}

public void setSeed(long seed) {
}

public void nextBytes(byte[] bytes) {
random().nextBytes(bytes);
}

public byte[] generateSeed(int numBytes) {
byte[] seed = new byte[numBytes];
random().nextBytes(seed);
return seed;
}

public int nextInt() {
return random().nextInt();
}

public int nextInt(int n) {
return random().nextInt(n);
}

public boolean nextBoolean() {
return random().nextBoolean();
}

public long nextLong() {
return random().nextLong();
}

public float nextFloat() {
return random().nextFloat();
}

public double nextDouble() {
return random().nextDouble();
}

public double nextGaussian() {
return random().nextGaussian();
}

private static Random random() {
return PlatformDependent.threadLocalRandom();
}
}