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