1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.netty.handler.ssl.util;
18
19 import io.netty.util.internal.PlatformDependent;
20 import io.netty.util.internal.logging.InternalLogger;
21 import io.netty.util.internal.logging.InternalLoggerFactory;
22
23 import java.lang.invoke.MethodHandle;
24 import java.lang.invoke.MethodHandles;
25 import java.math.BigInteger;
26 import java.security.AccessController;
27 import java.security.KeyPair;
28 import java.security.PrivateKey;
29 import java.security.PrivilegedAction;
30 import java.security.PublicKey;
31 import java.security.SecureRandom;
32 import java.security.cert.CertificateException;
33 import java.security.cert.X509Certificate;
34 import java.util.ArrayList;
35 import java.util.Date;
36 import java.util.List;
37
38 import static io.netty.handler.ssl.util.SelfSignedCertificate.newSelfSignedCertificate;
39 import static java.lang.invoke.MethodType.methodType;
40
41
42
43
44 final class OpenJdkSelfSignedCertGenerator {
45 private static final InternalLogger logger =
46 InternalLoggerFactory.getInstance(OpenJdkSelfSignedCertGenerator.class);
47 private static final MethodHandle CERT_INFO_SET_HANDLE;
48 private static final MethodHandle ISSUER_NAME_CONSTRUCTOR;
49 private static final MethodHandle CERT_IMPL_CONSTRUCTOR;
50 private static final MethodHandle X509_CERT_INFO_CONSTRUCTOR;
51 private static final MethodHandle CERTIFICATE_VERSION_CONSTRUCTOR;
52 private static final MethodHandle CERTIFICATE_SUBJECT_NAME_CONSTRUCTOR;
53 private static final MethodHandle X500_NAME_CONSTRUCTOR;
54 private static final MethodHandle CERTIFICATE_SERIAL_NUMBER_CONSTRUCTOR;
55 private static final MethodHandle CERTIFICATE_VALIDITY_CONSTRUCTOR;
56 private static final MethodHandle CERTIFICATE_X509_KEY_CONSTRUCTOR;
57 private static final MethodHandle CERTIFICATE_ALORITHM_ID_CONSTRUCTOR;
58 private static final MethodHandle CERT_IMPL_GET_HANDLE;
59 private static final MethodHandle CERT_IMPL_SIGN_HANDLE;
60 private static final MethodHandle ALGORITHM_ID_GET_HANDLE;
61
62 private static final boolean SUPPORTED;
63
64
65 static {
66 final MethodHandles.Lookup lookup = MethodHandles.lookup();
67
68 final Class<?> x509CertInfoClass;
69 final Class<?> x500NameClass;
70 final Class<?> certificateIssuerNameClass;
71 final Class<?> x509CertImplClass;
72 final Class<?> certificateVersionClass;
73 final Class<?> certificateSubjectNameClass;
74 final Class<?> certificateSerialNumberClass;
75 final Class<?> certificateValidityClass;
76 final Class<?> certificateX509KeyClass;
77 final Class<?> algorithmIdClass;
78 final Class<?> certificateAlgorithmIdClass;
79
80 boolean supported;
81 MethodHandle certInfoSetHandle = null;
82 MethodHandle x509CertInfoConstructor = null;
83 MethodHandle issuerNameConstructor = null;
84 MethodHandle certImplConstructor = null;
85 MethodHandle x500NameConstructor = null;
86 MethodHandle certificateVersionConstructor = null;
87 MethodHandle certificateSubjectNameConstructor = null;
88 MethodHandle certificateSerialNumberConstructor = null;
89 MethodHandle certificateValidityConstructor = null;
90 MethodHandle certificateX509KeyConstructor = null;
91 MethodHandle certificateAlgorithmIdConstructor = null;
92 MethodHandle certImplGetHandle = null;
93 MethodHandle certImplSignHandle = null;
94 MethodHandle algorithmIdGetHandle = null;
95
96 try {
97 Object maybeClasses = AccessController.doPrivileged(new PrivilegedAction<Object>() {
98 @Override
99 public Object run() {
100 try {
101 List<Class<?>> classes = new ArrayList<>();
102 classes.add(Class.forName("sun.security.x509.X509CertInfo", false,
103 PlatformDependent.getClassLoader(OpenJdkSelfSignedCertGenerator.class)));
104 classes.add(Class.forName("sun.security.x509.X500Name", false,
105 PlatformDependent.getClassLoader(OpenJdkSelfSignedCertGenerator.class)));
106 classes.add(Class.forName("sun.security.x509.CertificateIssuerName", false,
107 PlatformDependent.getClassLoader(OpenJdkSelfSignedCertGenerator.class)));
108 classes.add(Class.forName("sun.security.x509.X509CertImpl", false,
109 PlatformDependent.getClassLoader(OpenJdkSelfSignedCertGenerator.class)));
110 classes.add(Class.forName("sun.security.x509.CertificateVersion", false,
111 PlatformDependent.getClassLoader(OpenJdkSelfSignedCertGenerator.class)));
112 classes.add(Class.forName("sun.security.x509.CertificateSubjectName", false,
113 PlatformDependent.getClassLoader(OpenJdkSelfSignedCertGenerator.class)));
114 classes.add(Class.forName("sun.security.x509.CertificateSerialNumber", false,
115 PlatformDependent.getClassLoader(OpenJdkSelfSignedCertGenerator.class)));
116 classes.add(Class.forName("sun.security.x509.CertificateValidity", false,
117 PlatformDependent.getClassLoader(OpenJdkSelfSignedCertGenerator.class)));
118 classes.add(Class.forName("sun.security.x509.CertificateX509Key", false,
119 PlatformDependent.getClassLoader(OpenJdkSelfSignedCertGenerator.class)));
120 classes.add(Class.forName("sun.security.x509.AlgorithmId", false,
121 PlatformDependent.getClassLoader(OpenJdkSelfSignedCertGenerator.class)));
122 classes.add(Class.forName("sun.security.x509.CertificateAlgorithmId", false,
123 PlatformDependent.getClassLoader(OpenJdkSelfSignedCertGenerator.class)));
124
125 return classes;
126 } catch (Throwable cause) {
127 return cause;
128 }
129 }
130 });
131 if (maybeClasses instanceof List) {
132 @SuppressWarnings("unchecked") List<Class<?>> classes = (List<Class<?>>) maybeClasses;
133 x509CertInfoClass = classes.get(0);
134 x500NameClass = classes.get(1);
135 certificateIssuerNameClass = classes.get(2);
136 x509CertImplClass = classes.get(3);
137 certificateVersionClass = classes.get(4);
138 certificateSubjectNameClass = classes.get(5);
139 certificateSerialNumberClass = classes.get(6);
140 certificateValidityClass = classes.get(7);
141 certificateX509KeyClass = classes.get(8);
142 algorithmIdClass = classes.get(9);
143 certificateAlgorithmIdClass = classes.get(10);
144 } else {
145 throw (Throwable) maybeClasses;
146 }
147
148 Object maybeConstructors = AccessController.doPrivileged(new PrivilegedAction<Object>() {
149 @Override
150 public Object run() {
151 try {
152 List<MethodHandle> constructors = new ArrayList<>();
153 constructors.add(
154 lookup.unreflectConstructor(x509CertInfoClass.getConstructor())
155 .asType(methodType(x509CertInfoClass))
156 );
157 constructors.add(
158 lookup.unreflectConstructor(certificateIssuerNameClass.getConstructor(x500NameClass))
159 .asType(methodType(certificateIssuerNameClass, x500NameClass))
160 );
161 constructors.add(
162 lookup.unreflectConstructor(x509CertImplClass.getConstructor(x509CertInfoClass))
163 .asType(methodType(x509CertImplClass, x509CertInfoClass))
164 );
165 constructors.add(
166 lookup.unreflectConstructor(x500NameClass.getConstructor(String.class))
167 .asType(methodType(x500NameClass, String.class))
168 );
169 constructors.add(
170 lookup.unreflectConstructor(certificateVersionClass.getConstructor(int.class))
171 .asType(methodType(certificateVersionClass, int.class))
172 );
173 constructors.add(
174 lookup.unreflectConstructor(certificateSubjectNameClass.getConstructor(x500NameClass))
175 .asType(methodType(certificateSubjectNameClass, x500NameClass))
176 );
177 constructors.add(
178 lookup.unreflectConstructor(
179 certificateSerialNumberClass.getConstructor(BigInteger.class))
180 .asType(methodType(certificateSerialNumberClass, BigInteger.class))
181 );
182 constructors.add(
183 lookup.unreflectConstructor(
184 certificateValidityClass.getConstructor(Date.class, Date.class))
185 .asType(methodType(certificateValidityClass, Date.class, Date.class))
186 );
187 constructors.add(
188 lookup.unreflectConstructor(certificateX509KeyClass.getConstructor(PublicKey.class))
189 .asType(methodType(certificateX509KeyClass, PublicKey.class))
190 );
191
192 constructors.add(
193 lookup.unreflectConstructor(
194 certificateAlgorithmIdClass.getConstructor(algorithmIdClass))
195 .asType(methodType(certificateAlgorithmIdClass, algorithmIdClass))
196 );
197 return constructors;
198 } catch (Throwable cause) {
199 return cause;
200 }
201 }
202 });
203 if (maybeConstructors instanceof List) {
204 @SuppressWarnings("unchecked") List<MethodHandle> constructorList =
205 (List<MethodHandle>) maybeConstructors;
206 x509CertInfoConstructor = constructorList.get(0);
207 issuerNameConstructor = constructorList.get(1);
208 certImplConstructor = constructorList.get(2);
209 x500NameConstructor = constructorList.get(3);
210 certificateVersionConstructor = constructorList.get(4);
211 certificateSubjectNameConstructor = constructorList.get(5);
212 certificateSerialNumberConstructor = constructorList.get(6);
213 certificateValidityConstructor = constructorList.get(7);
214 certificateX509KeyConstructor = constructorList.get(8);
215 certificateAlgorithmIdConstructor = constructorList.get(9);
216 } else {
217 throw (Throwable) maybeConstructors;
218 }
219
220 Object maybeMethodHandles = AccessController.doPrivileged(new PrivilegedAction<Object>() {
221 @Override
222 public Object run() {
223 try {
224 List<MethodHandle> methods = new ArrayList<>();
225 methods.add(
226 lookup.findVirtual(x509CertInfoClass, "set",
227 methodType(void.class, String.class, Object.class))
228 );
229 methods.add(
230 lookup.findVirtual(x509CertImplClass, "get",
231 methodType(Object.class, String.class))
232 );
233
234 methods.add(
235 lookup.findVirtual(x509CertImplClass, "sign",
236 methodType(void.class, PrivateKey.class, String.class))
237 );
238 methods.add(
239 lookup.findStatic(algorithmIdClass, "get",
240 methodType(algorithmIdClass, String.class))
241 );
242 return methods;
243 } catch (Throwable cause) {
244 return cause;
245 }
246 }
247 });
248 if (maybeMethodHandles instanceof List) {
249 @SuppressWarnings("unchecked") List<MethodHandle> methodHandles =
250 (List<MethodHandle>) maybeMethodHandles;
251 certInfoSetHandle = methodHandles.get(0);
252 certImplGetHandle = methodHandles.get(1);
253 certImplSignHandle = methodHandles.get(2);
254 algorithmIdGetHandle = methodHandles.get(3);
255 } else {
256 throw (Throwable) maybeMethodHandles;
257 }
258 supported = true;
259 } catch (Throwable cause) {
260 supported = false;
261 logger.debug(OpenJdkSelfSignedCertGenerator.class.getSimpleName() + " not supported", cause);
262 }
263 CERT_INFO_SET_HANDLE = certInfoSetHandle;
264 X509_CERT_INFO_CONSTRUCTOR = x509CertInfoConstructor;
265 ISSUER_NAME_CONSTRUCTOR = issuerNameConstructor;
266 CERTIFICATE_VERSION_CONSTRUCTOR = certificateVersionConstructor;
267 CERTIFICATE_SUBJECT_NAME_CONSTRUCTOR = certificateSubjectNameConstructor;
268 CERT_IMPL_CONSTRUCTOR = certImplConstructor;
269 X500_NAME_CONSTRUCTOR = x500NameConstructor;
270 CERTIFICATE_SERIAL_NUMBER_CONSTRUCTOR = certificateSerialNumberConstructor;
271 CERTIFICATE_VALIDITY_CONSTRUCTOR = certificateValidityConstructor;
272 CERTIFICATE_X509_KEY_CONSTRUCTOR = certificateX509KeyConstructor;
273 CERT_IMPL_GET_HANDLE = certImplGetHandle;
274 CERT_IMPL_SIGN_HANDLE = certImplSignHandle;
275 ALGORITHM_ID_GET_HANDLE = algorithmIdGetHandle;
276 CERTIFICATE_ALORITHM_ID_CONSTRUCTOR = certificateAlgorithmIdConstructor;
277 SUPPORTED = supported;
278 }
279
280 static String[] generate(String fqdn, KeyPair keypair, SecureRandom random, Date notBefore, Date notAfter,
281 String algorithm) throws Exception {
282 if (!SUPPORTED) {
283 throw new UnsupportedOperationException(
284 OpenJdkSelfSignedCertGenerator.class.getSimpleName() + " not supported on the used JDK version");
285 }
286 try {
287 PrivateKey key = keypair.getPrivate();
288
289
290 Object info = X509_CERT_INFO_CONSTRUCTOR.invoke();
291 Object owner = X500_NAME_CONSTRUCTOR.invoke("CN=" + fqdn);
292
293 CERT_INFO_SET_HANDLE.invoke(info, "version", CERTIFICATE_VERSION_CONSTRUCTOR.invoke(2));
294 CERT_INFO_SET_HANDLE.invoke(info, "serialNumber",
295 CERTIFICATE_SERIAL_NUMBER_CONSTRUCTOR.invoke(new BigInteger(64, random)));
296 try {
297 CERT_INFO_SET_HANDLE.invoke(info, "subject", CERTIFICATE_SUBJECT_NAME_CONSTRUCTOR.invoke(owner));
298 } catch (CertificateException ex) {
299 CERT_INFO_SET_HANDLE.invoke(info, "subject", owner);
300 }
301 try {
302 CERT_INFO_SET_HANDLE.invoke(info, "issuer", ISSUER_NAME_CONSTRUCTOR.invoke(owner));
303 } catch (CertificateException ex) {
304 CERT_INFO_SET_HANDLE.invoke(info, "issuer", owner);
305 }
306 CERT_INFO_SET_HANDLE.invoke(info, "validity",
307 CERTIFICATE_VALIDITY_CONSTRUCTOR.invoke(notBefore, notAfter));
308 CERT_INFO_SET_HANDLE.invoke(info, "key", CERTIFICATE_X509_KEY_CONSTRUCTOR.invoke(keypair.getPublic()));
309 CERT_INFO_SET_HANDLE.invoke(info, "algorithmID",
310
311 CERTIFICATE_ALORITHM_ID_CONSTRUCTOR.invoke(
312 ALGORITHM_ID_GET_HANDLE.invoke("1.2.840.113549.1.1.11")));
313
314
315 Object cert = CERT_IMPL_CONSTRUCTOR.invoke(info);
316 CERT_IMPL_SIGN_HANDLE.invoke(cert, key,
317 algorithm.equalsIgnoreCase("EC") ? "SHA256withECDSA" : "SHA256withRSA");
318
319
320 CERT_INFO_SET_HANDLE.invoke(info, "algorithmID.algorithm",
321 CERT_IMPL_GET_HANDLE.invoke(cert, "x509.algorithm"));
322 cert = CERT_IMPL_CONSTRUCTOR.invoke(info);
323 CERT_IMPL_SIGN_HANDLE.invoke(cert, key,
324 algorithm.equalsIgnoreCase("EC") ? "SHA256withECDSA" : "SHA256withRSA");
325
326 X509Certificate x509Cert = (X509Certificate) cert;
327 x509Cert.verify(keypair.getPublic());
328
329 return newSelfSignedCertificate(fqdn, key, x509Cert);
330 } catch (Throwable cause) {
331 if (cause instanceof Exception) {
332 throw (Exception) cause;
333 }
334 if (cause instanceof Error) {
335 throw (Error) cause;
336 }
337 throw new IllegalStateException(cause);
338 }
339 }
340
341 private OpenJdkSelfSignedCertGenerator() { }
342 }