Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
e4fc0a0
feat: support keystore in transport for mtls
arithmetic1728 Oct 15, 2020
3f3d403
fix format
arithmetic1728 Oct 15, 2020
0feec05
update code
arithmetic1728 Oct 15, 2020
a71a009
add tests
arithmetic1728 Oct 16, 2020
3c10eaa
update test and doc
arithmetic1728 Oct 16, 2020
275bad0
update names
arithmetic1728 Oct 23, 2020
e21791c
create keystore from cert and key string
arithmetic1728 Oct 23, 2020
2edb134
change certAndKey from string to inputstream
arithmetic1728 Oct 26, 2020
09847d7
add mtls file
arithmetic1728 Oct 27, 2020
c4bc00d
Update google-http-client/src/main/java/com/google/api/client/http/ja…
arithmetic1728 Oct 29, 2020
a97ea3d
Update google-http-client/src/main/java/com/google/api/client/http/ja…
arithmetic1728 Oct 29, 2020
7469467
Update google-http-client/src/main/java/com/google/api/client/util/Ss…
arithmetic1728 Oct 29, 2020
a61ed1a
Update google-http-client/src/main/java/com/google/api/client/util/Ss…
arithmetic1728 Oct 29, 2020
93c3452
Update google-http-client/src/test/java/com/google/api/client/util/Se…
arithmetic1728 Oct 29, 2020
d195f65
Update google-http-client/src/main/java/com/google/api/client/util/Ss…
arithmetic1728 Oct 29, 2020
13d7d19
update the code
arithmetic1728 Oct 29, 2020
bade79a
fix name
arithmetic1728 Oct 29, 2020
a8d60ea
chore: add Beta annotation for new mtls functions
arithmetic1728 Oct 30, 2020
271c262
resolve conflict
arithmetic1728 Oct 30, 2020
eb9a90d
update Beta
arithmetic1728 Oct 30, 2020
f90ac74
add since tag
arithmetic1728 Oct 30, 2020
509c481
Merge branch 'master' of https://github.com/googleapis/google-http-ja…
arithmetic1728 Oct 31, 2020
fc72936
feat: add isMtls property to ApacheHttpTransport
arithmetic1728 Oct 29, 2020
02e53d2
update Beta annotation
arithmetic1728 Oct 31, 2020
254675e
format
arithmetic1728 Nov 1, 2020
f477636
Merge pull request #2 from arithmetic1728/apache
arithmetic1728 Nov 1, 2020
a14cac4
fix tag
arithmetic1728 Nov 1, 2020
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
Prev Previous commit
Next Next commit
create keystore from cert and key string
  • Loading branch information
arithmetic1728 committed Oct 23, 2020
commit e21791c6ae750d70fc89710304e429de2b141cb1
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
Expand All @@ -31,6 +32,7 @@
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.List;
import javax.net.ssl.X509TrustManager;

Expand Down Expand Up @@ -258,5 +260,62 @@ public static void loadKeyStoreFromCertificates(
}
}

/**
* Create a keystore for mutual TLS with the certificate and private key provided.
*
* <p>certAndKey should have the following format:
*
* <pre>
* -----BEGIN CERTIFICATE-----
* ......
* -----END CERTIFICATE------
* ----BEGIN PRIVATE KEY-----
* ......
* -----END PRIVATE KEY-----
* </pre>
*
* @param certAndKey Concatenation of a x509 certificate PEM string and a PKCS#8 unencrypted
* private key PEM string.
* @return keystore for mutual TLS.
*/
public static KeyStore createMtlsKeyStore(String certAndKey)
throws GeneralSecurityException, IOException {
KeyStore keystore = KeyStore.getInstance("JKS");
try {
keystore.load(null);
} catch (IOException ignored) {
// shouldn't throw any exception to load a null keystore.
}

byte[] certAndKeyBytes = certAndKey.getBytes();

// Read the certificate.
InputStreamReader reader = new InputStreamReader(new ByteArrayInputStream(certAndKeyBytes));
PemReader.Section section = PemReader.readFirstSectionAndClose(reader, "CERTIFICATE");
if (section == null) {
throw new IllegalArgumentException("certificate is missing from certAndKey string");
}
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
X509Certificate cert =
(X509Certificate)
certFactory.generateCertificate(
new ByteArrayInputStream(section.getBase64DecodedBytes()));

// Read the private key.
reader = new InputStreamReader(new ByteArrayInputStream(certAndKeyBytes));
section = PemReader.readFirstSectionAndClose(reader, "PRIVATE KEY");
if (section == null) {
throw new IllegalArgumentException("private key is missing from certAndKey string");
}
PKCS8EncodedKeySpec keySpecPKCS8 = new PKCS8EncodedKeySpec(section.getBase64DecodedBytes());
PrivateKey key =
KeyFactory.getInstance(cert.getPublicKey().getAlgorithm()).generatePrivate(keySpecPKCS8);

// Fit the certificate and private key into the keystore.
keystore.setKeyEntry("alias", key, new char[] {}, new X509Certificate[] {cert});

return keystore;
}

private SecurityUtils() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,16 @@

package com.google.api.client.util;

import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;

import com.google.api.client.testing.json.webtoken.TestCertificates;
import com.google.api.client.testing.util.SecurityTestUtils;
import com.google.common.io.Resources;
import java.io.ByteArrayInputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.cert.X509Certificate;
Expand All @@ -25,6 +32,7 @@
import javax.net.ssl.X509TrustManager;
import junit.framework.TestCase;
import org.junit.Assert;
import org.junit.function.ThrowingRunnable;

/**
* Tests {@link SecurityUtils}.
Expand Down Expand Up @@ -160,4 +168,51 @@ public void testVerifyX509() throws Exception {
public void testVerifyX509WrongCa() throws Exception {
assertNull(verifyX509(TestCertificates.BOGUS_CA_CERT));
}

public void testCreateMtlsKeyStoreNoCert() throws Exception {
URL url = getClass().getClassLoader().getResource("com/google/api/client/util/privateKey.pem");
final String certMissing = Resources.toString(url, StandardCharsets.UTF_8);
IllegalArgumentException exception =
assertThrows(
IllegalArgumentException.class,
new ThrowingRunnable() {
@Override
public void run() throws Throwable {
SecurityUtils.createMtlsKeyStore(certMissing);
}
});
assertThat(exception)
.hasMessageThat()
.isEqualTo("certificate is missing from certAndKey string");
}

public void testCreateMtlsKeyStoreNoPrivateKey() throws Exception {
URL url = getClass().getClassLoader().getResource("com/google/api/client/util/cert.pem");
final String privateKeyMissing = Resources.toString(url, StandardCharsets.UTF_8);
IllegalArgumentException exception =
assertThrows(
IllegalArgumentException.class,
new ThrowingRunnable() {
@Override
public void run() throws Throwable {
SecurityUtils.createMtlsKeyStore(privateKeyMissing);
}
});
assertThat(exception)
.hasMessageThat()
.isEqualTo("private key is missing from certAndKey string");
}

public void testCreateMtlsKeyStoreSuccess() throws Exception {
URL url = getClass().getClassLoader().getResource("com/google/api/client/util/cert.pem");
String cert = Resources.toString(url, StandardCharsets.UTF_8);

url = getClass().getClassLoader().getResource("com/google/api/client/util/privateKey.pem");
String privateKey = Resources.toString(url, StandardCharsets.UTF_8);

String certAndKey = cert + "\n" + privateKey;
KeyStore mtlsKeyStore = SecurityUtils.createMtlsKeyStore(certAndKey);

assertEquals(mtlsKeyStore.size(), 1);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
-----BEGIN CERTIFICATE-----
MIICGzCCAYSgAwIBAgIIWrt6xtmHPs4wDQYJKoZIhvcNAQEFBQAwMzExMC8GA1UE
AxMoMTAwOTEyMDcyNjg3OC5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbTAeFw0x
MjEyMDExNjEwNDRaFw0yMjExMjkxNjEwNDRaMDMxMTAvBgNVBAMTKDEwMDkxMjA3
MjY4NzguYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20wgZ8wDQYJKoZIhvcNAQEB
BQADgY0AMIGJAoGBAL1SdY8jTUVU7O4/XrZLYTw0ON1lV6MQRGajFDFCqD2Fd9tQ
GLW8Iftx9wfXe1zuaehJSgLcyCxazfyJoN3RiONBihBqWY6d3lQKqkgsRTNZkdFJ
Wdzl/6CxhK9sojh2p0r3tydtv9iwq5fuuWIvtODtT98EgphhncQAqkKoF3zVAgMB
AAGjODA2MAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgeAMBYGA1UdJQEB/wQM
MAoGCCsGAQUFBwMCMA0GCSqGSIb3DQEBBQUAA4GBAD8XQEqzGePa9VrvtEGpf+R4
fkxKbcYAzqYq202nKu0kfjhIYkYSBj6gi348YaxE64yu60TVl42l5HThmswUheW4
uQIaq36JvwvsDP5Zoj5BgiNSnDAFQp+jJFBRUA5vooJKgKgMDf/r/DCOsbO6VJF1
kWwa9n19NFiV0z3m6isj
-----END CERTIFICATE-----
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
-----BEGIN PRIVATE KEY-----
MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAL1SdY8jTUVU7O4/
XrZLYTw0ON1lV6MQRGajFDFCqD2Fd9tQGLW8Iftx9wfXe1zuaehJSgLcyCxazfyJ
oN3RiONBihBqWY6d3lQKqkgsRTNZkdFJWdzl/6CxhK9sojh2p0r3tydtv9iwq5fu
uWIvtODtT98EgphhncQAqkKoF3zVAgMBAAECgYB51B9cXe4yiGTzJ4pOKpHGySAy
sC1F/IjXt2eeD3PuKv4m/hL4l7kScpLx0+NJuQ4j8U2UK/kQOdrGANapB1ZbMZAK
/q0xmIUzdNIDiGSoTXGN2mEfdsEpQ/Xiv0lyhYBBPC/K4sYIpHccnhSRQUZlWLLY
lE5cFNKC9b7226mNvQJBAPt0hfCNIN0kUYOA9jdLtx7CE4ySGMPf5KPBuzPd8ty1
fxaFm9PB7B76VZQYmHcWy8rT5XjoLJHrmGW1ZvP+iDsCQQDAvnKoarPOGb5iJfkq
RrA4flf1TOlf+1+uqIOJ94959jkkJeb0gv/TshDnm6/bWn+1kJylQaKygCizwPwB
Z84vAkA0Duur4YvsPJijoQ9YY1SGCagCcjyuUKwFOxaGpmyhRPIKt56LOJqpzyno
fy8ReKa4VyYq4eZYT249oFCwMwIBAkAROPNF2UL3x5UbcAkznd1hLujtIlI4IV4L
XUNjsJtBap7we/KHJq11XRPlniO4lf2TW7iji5neGVWJulTKS1xBAkAerktk4Hsw
ErUaUG1s/d+Sgc8e/KMeBElV+NxGhcWEeZtfHMn/6VOlbzY82JyvC9OKC80A5CAE
VUV6b25kqrcu
-----END PRIVATE KEY-----