View Javadoc
1   /*
2    * Copyright 2018 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://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, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.handler.ssl;
17  
18  import io.netty.buffer.ByteBufAllocator;
19  import io.netty.buffer.UnpooledByteBufAllocator;
20  import io.netty.internal.tcnative.SSL;
21  
22  import javax.net.ssl.SSLException;
23  import javax.net.ssl.X509KeyManager;
24  import java.security.PrivateKey;
25  import java.security.cert.X509Certificate;
26  
27  import static io.netty.handler.ssl.ReferenceCountedOpenSslContext.toBIO;
28  
29  /**
30   * Provides {@link OpenSslKeyMaterial} for a given alias.
31   */
32  class OpenSslKeyMaterialProvider {
33  
34      private final X509KeyManager keyManager;
35      private final String password;
36  
37      OpenSslKeyMaterialProvider(X509KeyManager keyManager, String password) {
38          this.keyManager = keyManager;
39          this.password = password;
40      }
41  
42      static void validateKeyMaterialSupported(X509Certificate[] keyCertChain, PrivateKey key, String keyPassword)
43              throws SSLException {
44          validateSupported(keyCertChain);
45          validateSupported(key, keyPassword);
46      }
47  
48      private static void validateSupported(PrivateKey key, String password) throws SSLException {
49          if (key == null) {
50              return;
51          }
52  
53          long pkeyBio = 0;
54          long pkey = 0;
55  
56          try {
57              pkeyBio = toBIO(UnpooledByteBufAllocator.DEFAULT, key);
58              pkey = SSL.parsePrivateKey(pkeyBio, password);
59          } catch (Exception e) {
60              throw new SSLException("PrivateKey type not supported " + key.getFormat(), e);
61          } finally {
62              SSL.freeBIO(pkeyBio);
63              if (pkey != 0) {
64                  SSL.freePrivateKey(pkey);
65              }
66          }
67      }
68  
69      private static void validateSupported(X509Certificate[] certificates) throws SSLException {
70          if (certificates == null || certificates.length == 0) {
71              return;
72          }
73  
74          long chainBio = 0;
75          long chain = 0;
76          PemEncoded encoded = null;
77          try {
78              encoded = PemX509Certificate.toPEM(UnpooledByteBufAllocator.DEFAULT, true, certificates);
79              chainBio = toBIO(UnpooledByteBufAllocator.DEFAULT, encoded.retain());
80              chain = SSL.parseX509Chain(chainBio);
81          } catch (Exception e) {
82              throw new SSLException("Certificate type not supported", e);
83          } finally {
84              SSL.freeBIO(chainBio);
85              if (chain != 0) {
86                  SSL.freeX509Chain(chain);
87              }
88              if (encoded != null) {
89                  encoded.release();
90              }
91          }
92      }
93  
94      /**
95       * Returns the underlying {@link X509KeyManager} that is used.
96       */
97      X509KeyManager keyManager() {
98          return keyManager;
99      }
100 
101     /**
102      * Returns the {@link OpenSslKeyMaterial} or {@code null} (if none) that should be used during the handshake by
103      * OpenSSL.
104      */
105     OpenSslKeyMaterial chooseKeyMaterial(ByteBufAllocator allocator, String alias) throws Exception {
106         X509Certificate[] certificates = keyManager.getCertificateChain(alias);
107         if (certificates == null || certificates.length == 0) {
108             return null;
109         }
110 
111         PrivateKey key = keyManager.getPrivateKey(alias);
112         PemEncoded encoded = PemX509Certificate.toPEM(allocator, true, certificates);
113         long chainBio = 0;
114         long pkeyBio = 0;
115         long chain = 0;
116         long pkey = 0;
117         try {
118             chainBio = toBIO(allocator, encoded.retain());
119             chain = SSL.parseX509Chain(chainBio);
120 
121             OpenSslKeyMaterial keyMaterial;
122             if (key instanceof OpenSslPrivateKey) {
123                 keyMaterial = ((OpenSslPrivateKey) key).newKeyMaterial(chain, certificates);
124             } else {
125                 pkeyBio = toBIO(allocator, key);
126                 pkey = key == null ? 0 : SSL.parsePrivateKey(pkeyBio, password);
127                 keyMaterial = new DefaultOpenSslKeyMaterial(chain, pkey, certificates);
128             }
129 
130             // See the chain and pkey to 0 so we will not release it as the ownership was
131             // transferred to OpenSslKeyMaterial.
132             chain = 0;
133             pkey = 0;
134             return keyMaterial;
135         } finally {
136             SSL.freeBIO(chainBio);
137             SSL.freeBIO(pkeyBio);
138             if (chain != 0) {
139                 SSL.freeX509Chain(chain);
140             }
141             if (pkey != 0) {
142                 SSL.freePrivateKey(pkey);
143             }
144             encoded.release();
145         }
146     }
147 
148     /**
149      * Will be invoked once the provider should be destroyed.
150      */
151     void destroy() {
152         // NOOP.
153     }
154 }