View Javadoc
1   /*
2    * Copyright 2014 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.util;
17  
18  import io.netty.util.Recycler;
19  import io.netty.util.internal.ObjectUtil;
20  
21  import java.io.ByteArrayInputStream;
22  import java.math.BigInteger;
23  import java.security.InvalidKeyException;
24  import java.security.NoSuchAlgorithmException;
25  import java.security.NoSuchProviderException;
26  import java.security.Principal;
27  import java.security.Provider;
28  import java.security.PublicKey;
29  import java.security.SignatureException;
30  import java.security.cert.CertificateEncodingException;
31  import java.security.cert.CertificateException;
32  import java.security.cert.CertificateExpiredException;
33  import java.security.cert.CertificateFactory;
34  import java.security.cert.CertificateNotYetValidException;
35  import java.security.cert.CertificateParsingException;
36  import java.security.cert.X509Certificate;
37  import java.util.Collection;
38  import java.util.Date;
39  import java.util.List;
40  import java.util.Set;
41  import javax.security.auth.x500.X500Principal;
42  
43  public final class LazyX509Certificate extends X509Certificate {
44      private static final Recycler<CertFactoryHandle> CERT_FACTORIES = new Recycler<CertFactoryHandle>() {
45          @Override
46          protected CertFactoryHandle newObject(Handle<CertFactoryHandle> handle) {
47              try {
48                  return new CertFactoryHandle(CertificateFactory.getInstance("X.509"), handle);
49              } catch (CertificateException e) {
50                  throw new IllegalStateException(e);
51              }
52          }
53      };
54  
55      private static final class CertFactoryHandle {
56          private final CertificateFactory factory;
57          private final Recycler.EnhancedHandle<CertFactoryHandle> handle;
58  
59          private CertFactoryHandle(CertificateFactory factory, Recycler.Handle<CertFactoryHandle> handle) {
60              this.factory = factory;
61              this.handle = (Recycler.EnhancedHandle<CertFactoryHandle>) handle;
62          }
63  
64          public X509Certificate generateCertificate(byte[] bytes) throws CertificateException {
65              return (X509Certificate) factory.generateCertificate(new ByteArrayInputStream(bytes));
66          }
67  
68          public void recycle() {
69              handle.unguardedRecycle(this);
70          }
71      }
72  
73      private final byte[] bytes;
74      private volatile X509Certificate wrapped;
75  
76      /**
77       * Creates a new instance which will lazy parse the given bytes. Be aware that the bytes will not be cloned.
78       */
79      public LazyX509Certificate(byte[] bytes) {
80          this.bytes = ObjectUtil.checkNotNull(bytes, "bytes");
81      }
82  
83      @Override
84      public void checkValidity() throws CertificateExpiredException, CertificateNotYetValidException {
85          unwrap().checkValidity();
86      }
87  
88      @Override
89      public void checkValidity(Date date) throws CertificateExpiredException, CertificateNotYetValidException {
90          unwrap().checkValidity(date);
91      }
92  
93      @Override
94      public X500Principal getIssuerX500Principal() {
95          return unwrap().getIssuerX500Principal();
96      }
97  
98      @Override
99      public X500Principal getSubjectX500Principal() {
100         return unwrap().getSubjectX500Principal();
101     }
102 
103     @Override
104     public List<String> getExtendedKeyUsage() throws CertificateParsingException {
105         return unwrap().getExtendedKeyUsage();
106     }
107 
108     @Override
109     public Collection<List<?>> getSubjectAlternativeNames() throws CertificateParsingException {
110         return unwrap().getSubjectAlternativeNames();
111     }
112 
113     @Override
114     public Collection<List<?>> getIssuerAlternativeNames() throws CertificateParsingException {
115         return unwrap().getIssuerAlternativeNames();
116     }
117 
118     @Override
119     public void verify(PublicKey key, Provider sigProvider)
120             throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, SignatureException {
121         unwrap().verify(key, sigProvider);
122     }
123 
124     @Override
125     public int getVersion() {
126         return unwrap().getVersion();
127     }
128 
129     @Override
130     public BigInteger getSerialNumber() {
131         return unwrap().getSerialNumber();
132     }
133 
134     @Override
135     public Principal getIssuerDN() {
136         return unwrap().getIssuerDN();
137     }
138 
139     @Override
140     public Principal getSubjectDN() {
141         return unwrap().getSubjectDN();
142     }
143 
144     @Override
145     public Date getNotBefore() {
146         return unwrap().getNotBefore();
147     }
148 
149     @Override
150     public Date getNotAfter() {
151         return unwrap().getNotAfter();
152     }
153 
154     @Override
155     public byte[] getTBSCertificate() throws CertificateEncodingException {
156         return unwrap().getTBSCertificate();
157     }
158 
159     @Override
160     public byte[] getSignature() {
161         return unwrap().getSignature();
162     }
163 
164     @Override
165     public String getSigAlgName() {
166         return unwrap().getSigAlgName();
167     }
168 
169     @Override
170     public String getSigAlgOID() {
171         return unwrap().getSigAlgOID();
172     }
173 
174     @Override
175     public byte[] getSigAlgParams() {
176         return unwrap().getSigAlgParams();
177     }
178 
179     @Override
180     public boolean[] getIssuerUniqueID() {
181         return unwrap().getIssuerUniqueID();
182     }
183 
184     @Override
185     public boolean[] getSubjectUniqueID() {
186         return unwrap().getSubjectUniqueID();
187     }
188 
189     @Override
190     public boolean[] getKeyUsage() {
191         return unwrap().getKeyUsage();
192     }
193 
194     @Override
195     public int getBasicConstraints() {
196         return unwrap().getBasicConstraints();
197     }
198 
199     @Override
200     public byte[] getEncoded() {
201         return bytes.clone();
202     }
203 
204     @Override
205     public void verify(PublicKey key)
206             throws CertificateException, NoSuchAlgorithmException,
207             InvalidKeyException, NoSuchProviderException, SignatureException {
208         unwrap().verify(key);
209     }
210 
211     @Override
212     public void verify(PublicKey key, String sigProvider)
213             throws CertificateException, NoSuchAlgorithmException, InvalidKeyException,
214             NoSuchProviderException, SignatureException {
215         unwrap().verify(key, sigProvider);
216     }
217 
218     @Override
219     public String toString() {
220         return unwrap().toString();
221     }
222 
223     @Override
224     public PublicKey getPublicKey() {
225         return unwrap().getPublicKey();
226     }
227 
228     @Override
229     public boolean hasUnsupportedCriticalExtension() {
230         return unwrap().hasUnsupportedCriticalExtension();
231     }
232 
233     @Override
234     public Set<String> getCriticalExtensionOIDs() {
235         return unwrap().getCriticalExtensionOIDs();
236     }
237 
238     @Override
239     public Set<String> getNonCriticalExtensionOIDs() {
240         return unwrap().getNonCriticalExtensionOIDs();
241     }
242 
243     @Override
244     public byte[] getExtensionValue(String oid) {
245         return unwrap().getExtensionValue(oid);
246     }
247 
248     private X509Certificate unwrap() {
249         X509Certificate wrapped = this.wrapped;
250         if (wrapped == null) {
251             CertFactoryHandle factory = null;
252             try {
253                 factory = CERT_FACTORIES.get();
254                 wrapped = this.wrapped = factory.generateCertificate(bytes);
255             } catch (CertificateException e) {
256                 throw new IllegalStateException(e);
257             } finally {
258                 if (factory != null) {
259                     factory.recycle();
260                 }
261             }
262         }
263         return wrapped;
264     }
265 }