mirror of https://github.com/grpc/grpc-java.git
xds: Add EC key support
io.grpc.util.CertificateUtils does much of the same thing as xds's CertificateUtils, but also supports EC keys. The xds code pre-dates the grpc-util class, so it isn't surprising it wasn't using it. There's a good number of usages of the xds CertificateUtils, so I just got rid of the duplicate implementation, but didn't yet bother changing callers io.grpc.util.
This commit is contained in:
parent
cb03bd2346
commit
100d5a55fd
|
@ -16,50 +16,19 @@
|
|||
|
||||
package io.grpc.xds.internal.security.trust;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.handler.codec.base64.Base64;
|
||||
import io.netty.util.CharsetUtil;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.security.KeyException;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.util.Collection;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Contains certificate utility method(s).
|
||||
*/
|
||||
public final class CertificateUtils {
|
||||
private static final Logger logger = Logger.getLogger(CertificateUtils.class.getName());
|
||||
|
||||
private static CertificateFactory factory;
|
||||
private static final Pattern KEY_PATTERN = Pattern.compile(
|
||||
"-+BEGIN\\s+.*PRIVATE\\s+KEY[^-]*-+(?:\\s|\\r|\\n)+" // Header
|
||||
+ "([a-z0-9+/=\\r\\n]+)" // Base64 text
|
||||
+ "-+END\\s+.*PRIVATE\\s+KEY[^-]*-+", // Footer
|
||||
Pattern.CASE_INSENSITIVE);
|
||||
|
||||
private static synchronized void initInstance() throws CertificateException {
|
||||
if (factory == null) {
|
||||
factory = CertificateFactory.getInstance("X.509");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates X509Certificate array from a file on disk.
|
||||
*
|
||||
|
@ -73,72 +42,15 @@ public final class CertificateUtils {
|
|||
}
|
||||
|
||||
/** Generates X509Certificate array from the {@link InputStream}. */
|
||||
public static synchronized X509Certificate[] toX509Certificates(InputStream inputStream)
|
||||
public static X509Certificate[] toX509Certificates(InputStream inputStream)
|
||||
throws CertificateException, IOException {
|
||||
initInstance();
|
||||
Collection<? extends Certificate> certs = factory.generateCertificates(inputStream);
|
||||
return certs.toArray(new X509Certificate[0]);
|
||||
|
||||
}
|
||||
|
||||
/** See {@link CertificateFactory#generateCertificate(InputStream)}. */
|
||||
public static synchronized X509Certificate toX509Certificate(InputStream inputStream)
|
||||
throws CertificateException, IOException {
|
||||
initInstance();
|
||||
Certificate cert = factory.generateCertificate(inputStream);
|
||||
return (X509Certificate) cert;
|
||||
return io.grpc.util.CertificateUtils.getX509Certificates(inputStream);
|
||||
}
|
||||
|
||||
/** Generates a {@link PrivateKey} from the {@link InputStream}. */
|
||||
public static PrivateKey getPrivateKey(InputStream inputStream)
|
||||
throws Exception {
|
||||
ByteBuf encodedKeyBuf = readPrivateKey(inputStream);
|
||||
byte[] encodedKey = new byte[encodedKeyBuf.readableBytes()];
|
||||
encodedKeyBuf.readBytes(encodedKey).release();
|
||||
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(encodedKey);
|
||||
return KeyFactory.getInstance("RSA").generatePrivate(spec);
|
||||
}
|
||||
|
||||
private static ByteBuf readPrivateKey(InputStream in) throws KeyException {
|
||||
String content;
|
||||
try {
|
||||
content = readContent(in);
|
||||
} catch (IOException e) {
|
||||
throw new KeyException("failed to read key input stream", e);
|
||||
}
|
||||
Matcher m = KEY_PATTERN.matcher(content);
|
||||
if (!m.find()) {
|
||||
throw new KeyException("could not find a PKCS #8 private key in input stream");
|
||||
}
|
||||
ByteBuf base64 = Unpooled.copiedBuffer(m.group(1), CharsetUtil.US_ASCII);
|
||||
ByteBuf der = Base64.decode(base64);
|
||||
base64.release();
|
||||
return der;
|
||||
}
|
||||
|
||||
private static String readContent(InputStream in) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
try {
|
||||
byte[] buf = new byte[8192];
|
||||
for (; ; ) {
|
||||
int ret = in.read(buf);
|
||||
if (ret < 0) {
|
||||
break;
|
||||
}
|
||||
out.write(buf, 0, ret);
|
||||
}
|
||||
return out.toString(CharsetUtil.US_ASCII.name());
|
||||
} finally {
|
||||
safeClose(out);
|
||||
}
|
||||
}
|
||||
|
||||
private static void safeClose(OutputStream out) {
|
||||
try {
|
||||
out.close();
|
||||
} catch (IOException e) {
|
||||
logger.log(Level.WARNING, "Failed to close a stream.", e);
|
||||
}
|
||||
return io.grpc.util.CertificateUtils.getPrivateKey(inputStream);
|
||||
}
|
||||
|
||||
private CertificateUtils() {}
|
||||
|
|
|
@ -17,9 +17,7 @@
|
|||
package io.grpc.xds.internal.security;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.google.common.io.CharStreams;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
import com.google.protobuf.BoolValue;
|
||||
import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.CertificateProviderPluginInstance;
|
||||
|
@ -32,14 +30,11 @@ import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContex
|
|||
import io.envoyproxy.envoy.type.matcher.v3.StringMatcher;
|
||||
import io.grpc.internal.testing.TestUtils;
|
||||
import io.grpc.testing.TlsTesting;
|
||||
import io.grpc.util.CertificateUtils;
|
||||
import io.grpc.xds.EnvoyServerProtoData;
|
||||
import io.grpc.xds.internal.security.trust.CertificateUtils;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Arrays;
|
||||
|
@ -195,22 +190,11 @@ public class CommonTlsContextTestsUtil {
|
|||
/** Gets a cert from contents of a resource. */
|
||||
public static X509Certificate getCertFromResourceName(String resourceName)
|
||||
throws IOException, CertificateException {
|
||||
try (ByteArrayInputStream bais =
|
||||
new ByteArrayInputStream(getResourceContents(resourceName).getBytes(UTF_8))) {
|
||||
return CertificateUtils.toX509Certificate(bais);
|
||||
try (InputStream cert = TlsTesting.loadCert(resourceName)) {
|
||||
return CertificateUtils.getX509Certificates(cert)[0];
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets contents of a certs resource. */
|
||||
public static String getResourceContents(String resourceName) throws IOException {
|
||||
InputStream inputStream = TlsTesting.loadCert(resourceName);
|
||||
String text = null;
|
||||
try (Reader reader = new InputStreamReader(inputStream, UTF_8)) {
|
||||
text = CharStreams.toString(reader);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private static CommonTlsContext buildCommonTlsContextForCertProviderInstance(
|
||||
String certInstanceName,
|
||||
|
|
|
@ -16,19 +16,13 @@
|
|||
|
||||
package io.grpc.xds.internal.security.certprovider;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.google.common.io.CharStreams;
|
||||
import io.grpc.internal.FakeClock;
|
||||
import io.grpc.internal.TimeProvider;
|
||||
import io.grpc.internal.testing.TestUtils;
|
||||
import io.grpc.util.CertificateUtils;
|
||||
import io.grpc.xds.internal.security.certprovider.FileWatcherCertificateProviderProvider.ScheduledExecutorServiceFactory;
|
||||
import io.grpc.xds.internal.security.trust.CertificateUtils;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
@ -44,17 +38,9 @@ public class CommonCertProviderTestUtils {
|
|||
|
||||
static X509Certificate getCertFromResourceName(String resourceName)
|
||||
throws IOException, CertificateException {
|
||||
return CertificateUtils.toX509Certificate(
|
||||
new ByteArrayInputStream(getResourceContents(resourceName).getBytes(UTF_8)));
|
||||
}
|
||||
|
||||
private static String getResourceContents(String resourceName) throws IOException {
|
||||
InputStream inputStream = TestUtils.class.getResourceAsStream("/certs/" + resourceName);
|
||||
String text = null;
|
||||
try (Reader reader = new InputStreamReader(inputStream, UTF_8)) {
|
||||
text = CharStreams.toString(reader);
|
||||
try (InputStream cert = TestUtils.class.getResourceAsStream("/certs/" + resourceName)) {
|
||||
return CertificateUtils.getX509Certificates(cert)[0];
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
/** Allow tests to register a provider using test clock.
|
||||
|
|
|
@ -270,12 +270,12 @@ public class FileWatcherCertificateProviderTest {
|
|||
CLIENT_PEM_FILE,
|
||||
SERVER_0_PEM_FILE,
|
||||
CA_PEM_FILE,
|
||||
java.security.KeyException.class,
|
||||
java.security.spec.InvalidKeySpecException.class,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
"could not find a PKCS #8 private key in input stream");
|
||||
"Neither RSA nor EC worked");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
Loading…
Reference in New Issue