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.SuppressJava6Requirement;
21 import io.netty.util.internal.logging.InternalLogger;
22 import io.netty.util.internal.logging.InternalLoggerFactory;
23 import sun.security.x509.AlgorithmId;
24 import sun.security.x509.CertificateAlgorithmId;
25 import sun.security.x509.CertificateSerialNumber;
26 import sun.security.x509.CertificateSubjectName;
27 import sun.security.x509.CertificateValidity;
28 import sun.security.x509.CertificateVersion;
29 import sun.security.x509.CertificateX509Key;
30 import sun.security.x509.X500Name;
31 import sun.security.x509.X509CertImpl;
32 import sun.security.x509.X509CertInfo;
33
34 import java.lang.reflect.Constructor;
35 import java.lang.reflect.InvocationTargetException;
36 import java.lang.reflect.Method;
37 import java.security.AccessController;
38 import java.security.PrivilegedAction;
39 import java.util.Date;
40 import java.math.BigInteger;
41 import java.security.KeyPair;
42 import java.security.PrivateKey;
43 import java.security.SecureRandom;
44 import java.security.cert.CertificateException;
45
46 import static io.netty.handler.ssl.util.SelfSignedCertificate.*;
47
48
49
50
51 final class OpenJdkSelfSignedCertGenerator {
52 private static final InternalLogger logger =
53 InternalLoggerFactory.getInstance(OpenJdkSelfSignedCertGenerator.class);
54 private static final Method CERT_INFO_SET_METHOD;
55 private static final Constructor<?> ISSUER_NAME_CONSTRUCTOR;
56 private static final Constructor<X509CertImpl> CERT_IMPL_CONSTRUCTOR;
57 private static final Method CERT_IMPL_GET_METHOD;
58 private static final Method CERT_IMPL_SIGN_METHOD;
59
60
61 static {
62 Method certInfoSetMethod = null;
63 Constructor<?> issuerNameConstructor = null;
64 Constructor<X509CertImpl> certImplConstructor = null;
65 Method certImplGetMethod = null;
66 Method certImplSignMethod = null;
67 try {
68 Object maybeCertInfoSetMethod = AccessController.doPrivileged(new PrivilegedAction<Object>() {
69 @Override
70 public Object run() {
71 try {
72 return X509CertInfo.class.getMethod("set", String.class, Object.class);
73 } catch (Throwable cause) {
74 return cause;
75 }
76 }
77 });
78 if (maybeCertInfoSetMethod instanceof Method) {
79 certInfoSetMethod = (Method) maybeCertInfoSetMethod;
80 } else {
81 throw (Throwable) maybeCertInfoSetMethod;
82 }
83
84 Object maybeIssuerNameConstructor = AccessController.doPrivileged(new PrivilegedAction<Object>() {
85 @Override
86 public Object run() {
87 try {
88 Class<?> issuerName = Class.forName("sun.security.x509.CertificateIssuerName", false,
89 PlatformDependent.getClassLoader(OpenJdkSelfSignedCertGenerator.class));
90 return issuerName.getConstructor(X500Name.class);
91 } catch (Throwable cause) {
92 return cause;
93 }
94 }
95 });
96 if (maybeIssuerNameConstructor instanceof Constructor) {
97 issuerNameConstructor = (Constructor<?>) maybeIssuerNameConstructor;
98 } else {
99 throw (Throwable) maybeIssuerNameConstructor;
100 }
101
102 Object maybeCertImplConstructor = AccessController.doPrivileged(new PrivilegedAction<Object>() {
103 @Override
104 public Object run() {
105 try {
106 return X509CertImpl.class.getConstructor(X509CertInfo.class);
107 } catch (Throwable cause) {
108 return cause;
109 }
110 }
111 });
112 if (maybeCertImplConstructor instanceof Constructor) {
113 @SuppressWarnings("unchecked")
114 Constructor<X509CertImpl> constructor = (Constructor<X509CertImpl>) maybeCertImplConstructor;
115 certImplConstructor = constructor;
116 } else {
117 throw (Throwable) maybeCertImplConstructor;
118 }
119
120 Object maybeCertImplGetMethod = AccessController.doPrivileged(new PrivilegedAction<Object>() {
121 @Override
122 public Object run() {
123 try {
124 return X509CertImpl.class.getMethod("get", String.class);
125 } catch (Throwable cause) {
126 return cause;
127 }
128 }
129 });
130 if (maybeCertImplGetMethod instanceof Method) {
131 certImplGetMethod = (Method) maybeCertImplGetMethod;
132 } else {
133 throw (Throwable) maybeCertImplGetMethod;
134 }
135
136 Object maybeCertImplSignMethod = AccessController.doPrivileged(new PrivilegedAction<Object>() {
137 @Override
138 public Object run() {
139 try {
140 return X509CertImpl.class.getMethod("sign", PrivateKey.class, String.class);
141 } catch (Throwable cause) {
142 return cause;
143 }
144 }
145 });
146 if (maybeCertImplSignMethod instanceof Method) {
147 certImplSignMethod = (Method) maybeCertImplSignMethod;
148 } else {
149 throw (Throwable) maybeCertImplSignMethod;
150 }
151 } catch (Throwable cause) {
152 logger.debug(OpenJdkSelfSignedCertGenerator.class.getSimpleName() + " not supported", cause);
153 }
154 CERT_INFO_SET_METHOD = certInfoSetMethod;
155 ISSUER_NAME_CONSTRUCTOR = issuerNameConstructor;
156 CERT_IMPL_CONSTRUCTOR = certImplConstructor;
157 CERT_IMPL_GET_METHOD = certImplGetMethod;
158 CERT_IMPL_SIGN_METHOD = certImplSignMethod;
159 }
160
161 @SuppressJava6Requirement(reason = "Usage guarded by dependency check")
162 static String[] generate(String fqdn, KeyPair keypair, SecureRandom random, Date notBefore, Date notAfter,
163 String algorithm) throws Exception {
164 if (CERT_INFO_SET_METHOD == null || ISSUER_NAME_CONSTRUCTOR == null ||
165 CERT_IMPL_CONSTRUCTOR == null || CERT_IMPL_GET_METHOD == null || CERT_IMPL_SIGN_METHOD == null) {
166 throw new UnsupportedOperationException(
167 OpenJdkSelfSignedCertGenerator.class.getSimpleName() + " not supported on the used JDK version");
168 }
169 PrivateKey key = keypair.getPrivate();
170
171
172 X509CertInfo info = new X509CertInfo();
173 X500Name owner = new X500Name("CN=" + fqdn);
174
175 CERT_INFO_SET_METHOD.invoke(info, X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3));
176 CERT_INFO_SET_METHOD.invoke(info, X509CertInfo.SERIAL_NUMBER,
177 new CertificateSerialNumber(new BigInteger(64, random)));
178 try {
179 CERT_INFO_SET_METHOD.invoke(info, X509CertInfo.SUBJECT, new CertificateSubjectName(owner));
180 } catch (InvocationTargetException ex) {
181 if (ex.getCause() instanceof CertificateException) {
182 CERT_INFO_SET_METHOD.invoke(info, X509CertInfo.SUBJECT, owner);
183 } else {
184 throw ex;
185 }
186 }
187 try {
188 CERT_INFO_SET_METHOD.invoke(info, X509CertInfo.ISSUER, ISSUER_NAME_CONSTRUCTOR.newInstance(owner));
189 } catch (InvocationTargetException ex) {
190 if (ex.getCause() instanceof CertificateException) {
191 CERT_INFO_SET_METHOD.invoke(info, X509CertInfo.ISSUER, owner);
192 } else {
193 throw ex;
194 }
195 }
196 CERT_INFO_SET_METHOD.invoke(info, X509CertInfo.VALIDITY, new CertificateValidity(notBefore, notAfter));
197 CERT_INFO_SET_METHOD.invoke(info, X509CertInfo.KEY, new CertificateX509Key(keypair.getPublic()));
198 CERT_INFO_SET_METHOD.invoke(info, X509CertInfo.ALGORITHM_ID,
199
200 new CertificateAlgorithmId(AlgorithmId.get("1.2.840.113549.1.1.11")));
201
202
203 X509CertImpl cert = CERT_IMPL_CONSTRUCTOR.newInstance(info);
204 CERT_IMPL_SIGN_METHOD.invoke(cert, key, algorithm.equalsIgnoreCase("EC") ? "SHA256withECDSA" : "SHA256withRSA");
205
206
207 CERT_INFO_SET_METHOD.invoke(info, CertificateAlgorithmId.NAME + ".algorithm",
208 CERT_IMPL_GET_METHOD.invoke(cert, "x509.algorithm"));
209 cert = CERT_IMPL_CONSTRUCTOR.newInstance(info);
210 CERT_IMPL_SIGN_METHOD.invoke(cert, key,
211 algorithm.equalsIgnoreCase("EC") ? "SHA256withECDSA" : "SHA256withRSA");
212 cert.verify(keypair.getPublic());
213
214 return newSelfSignedCertificate(fqdn, key, cert);
215 }
216
217 private OpenJdkSelfSignedCertGenerator() { }
218 }