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.netty5.handler.ssl;
17  
18  import io.netty.internal.tcnative.SSL;
19  import io.netty5.buffer.api.BufferAllocator;
20  
21  import javax.net.ssl.SSLException;
22  import javax.net.ssl.X509KeyManager;
23  import java.security.PrivateKey;
24  import java.security.cert.X509Certificate;
25  
26  import static io.netty5.buffer.api.DefaultBufferAllocators.offHeapAllocator;
27  import static io.netty5.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(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          BufferAllocator allocator = offHeapAllocator();
77          try (PemEncoded encoded = PemX509Certificate.toPEM(allocator, certificates)) {
78              chainBio = toBIO(allocator, encoded);
79              chain = SSL.parseX509Chain(chainBio);
80          } catch (Exception e) {
81              throw new SSLException("Certificate type not supported", e);
82          } finally {
83              SSL.freeBIO(chainBio);
84              if (chain != 0) {
85                  SSL.freeX509Chain(chain);
86              }
87          }
88      }
89  
90      /**
91       * Returns the underlying {@link X509KeyManager} that is used.
92       */
93      X509KeyManager keyManager() {
94          return keyManager;
95      }
96  
97      /**
98       * Returns the {@link OpenSslKeyMaterial} or {@code null} (if none) that should be used during the handshake by
99       * OpenSSL.
100      */
101     OpenSslKeyMaterial chooseKeyMaterial(BufferAllocator allocator, String alias) throws Exception {
102         X509Certificate[] certificates = keyManager.getCertificateChain(alias);
103         if (certificates == null || certificates.length == 0) {
104             return null;
105         }
106 
107         PrivateKey key = keyManager.getPrivateKey(alias);
108         long chainBio = 0;
109         long pkeyBio = 0;
110         long chain = 0;
111         long pkey = 0;
112         try (PemEncoded encoded = PemX509Certificate.toPEM(allocator, certificates)) {
113             chainBio = toBIO(allocator, encoded);
114             chain = SSL.parseX509Chain(chainBio);
115 
116             OpenSslKeyMaterial keyMaterial;
117             if (key instanceof OpenSslPrivateKey) {
118                 keyMaterial = ((OpenSslPrivateKey) key).newKeyMaterial(chain, certificates);
119             } else {
120                 pkeyBio = toBIO(key);
121                 pkey = key == null ? 0 : SSL.parsePrivateKey(pkeyBio, password);
122                 keyMaterial = new DefaultOpenSslKeyMaterial(chain, pkey, certificates);
123             }
124 
125             // See the chain and pkey to 0 so we will not release it as the ownership was
126             // transferred to OpenSslKeyMaterial.
127             chain = 0;
128             pkey = 0;
129             return keyMaterial;
130         } finally {
131             SSL.freeBIO(chainBio);
132             SSL.freeBIO(pkeyBio);
133             if (chain != 0) {
134                 SSL.freeX509Chain(chain);
135             }
136             if (pkey != 0) {
137                 SSL.freePrivateKey(pkey);
138             }
139         }
140     }
141 
142     /**
143      * Will be invoked once the provider should be destroyed.
144      */
145     void destroy() {
146         // NOOP.
147     }
148 }