• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 The gRPC Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package io.grpc.util;
18 
19 import com.google.common.io.BaseEncoding;
20 import io.grpc.ExperimentalApi;
21 import java.io.BufferedReader;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.io.InputStreamReader;
25 import java.io.UnsupportedEncodingException;
26 import java.security.KeyFactory;
27 import java.security.NoSuchAlgorithmException;
28 import java.security.PrivateKey;
29 import java.security.cert.Certificate;
30 import java.security.cert.CertificateException;
31 import java.security.cert.CertificateFactory;
32 import java.security.cert.X509Certificate;
33 import java.security.spec.InvalidKeySpecException;
34 import java.security.spec.PKCS8EncodedKeySpec;
35 import java.util.Collection;
36 
37 /**
38  * Contains certificate/key PEM file utility method(s).
39  */
40 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/8024")
41 public final class CertificateUtils {
42   /**
43    * Generates X509Certificate array from a PEM file.
44    * The PEM file should contain one or more items in Base64 encoding, each with
45    * plain-text headers and footers
46    * (e.g. -----BEGIN CERTIFICATE----- and -----END CERTIFICATE-----).
47    *
48    * @param inputStream is a {@link InputStream} from the certificate files
49    */
getX509Certificates(InputStream inputStream)50   public static X509Certificate[] getX509Certificates(InputStream inputStream)
51       throws CertificateException {
52     CertificateFactory factory = CertificateFactory.getInstance("X.509");
53     Collection<? extends Certificate> certs = factory.generateCertificates(inputStream);
54     return certs.toArray(new X509Certificate[0]);
55   }
56 
57   /**
58    * Generates a {@link PrivateKey} from a PEM file.
59    * The key should be PKCS #8 formatted. The key algorithm should be "RSA" or "EC".
60    * The PEM file should contain one item in Base64 encoding, with plain-text headers and footers
61    * (e.g. -----BEGIN PRIVATE KEY----- and -----END PRIVATE KEY-----).
62    *
63    * @param inputStream is a {@link InputStream} from the private key file
64    */
getPrivateKey(InputStream inputStream)65   public static PrivateKey getPrivateKey(InputStream inputStream)
66       throws UnsupportedEncodingException, IOException, NoSuchAlgorithmException,
67       InvalidKeySpecException {
68     BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
69     String line;
70     while ((line = reader.readLine()) != null) {
71       if ("-----BEGIN PRIVATE KEY-----".equals(line)) {
72         break;
73       }
74     }
75     StringBuilder keyContent = new StringBuilder();
76     while ((line = reader.readLine()) != null) {
77       if ("-----END PRIVATE KEY-----".equals(line)) {
78         break;
79       }
80       keyContent.append(line);
81     }
82     byte[] decodedKeyBytes = BaseEncoding.base64().decode(keyContent.toString());
83     PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decodedKeyBytes);
84     try {
85       return KeyFactory.getInstance("RSA").generatePrivate(keySpec);
86     } catch (InvalidKeySpecException ignore) {
87       try {
88         return KeyFactory.getInstance("EC").generatePrivate(keySpec);
89       } catch (InvalidKeySpecException e) {
90         throw new InvalidKeySpecException("Neither RSA nor EC worked", e);
91       }
92     }
93   }
94 }
95 
96