1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty5.handler.ssl;
17
18 import io.netty5.buffer.api.Buffer;
19 import io.netty5.buffer.api.BufferAllocator;
20 import io.netty5.util.Resource;
21 import io.netty5.util.Send;
22 import io.netty5.buffer.api.internal.ResourceSupport;
23 import io.netty5.buffer.api.internal.Statics;
24 import io.netty5.util.CharsetUtil;
25
26 import java.math.BigInteger;
27 import java.security.Principal;
28 import java.security.PublicKey;
29 import java.security.cert.CertificateEncodingException;
30 import java.security.cert.X509Certificate;
31 import java.util.Arrays;
32 import java.util.Date;
33 import java.util.Set;
34
35 import static io.netty5.buffer.api.DefaultBufferAllocators.offHeapAllocator;
36 import static io.netty5.util.internal.ObjectUtil.checkNonEmpty;
37 import static java.util.Objects.requireNonNull;
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52 public final class PemX509Certificate extends X509Certificate implements PemEncoded, Resource<PemX509Certificate> {
53
54 private static final byte[] BEGIN_CERT = "-----BEGIN CERTIFICATE-----\n".getBytes(CharsetUtil.US_ASCII);
55 private static final byte[] END_CERT = "\n-----END CERTIFICATE-----\n".getBytes(CharsetUtil.US_ASCII);
56
57
58
59
60 static PemEncoded toPEM(BufferAllocator alloc, X509Certificate... chain) throws CertificateEncodingException {
61 checkNonEmpty(chain, "chain");
62
63
64
65
66
67
68 if (chain.length == 1) {
69 X509Certificate first = chain[0];
70 if (first instanceof PemEncoded) {
71 return ((PemEncoded) first).copy();
72 }
73 }
74
75 boolean success = false;
76 Buffer pem = null;
77 try {
78 for (X509Certificate cert : chain) {
79
80 if (cert == null) {
81 throw new IllegalArgumentException("Null element in chain: " + Arrays.toString(chain));
82 }
83
84 if (cert instanceof PemEncoded) {
85 pem = append(alloc, (PemEncoded) cert, chain.length, pem);
86 } else {
87 pem = append(alloc, cert, chain.length, pem);
88 }
89 }
90
91 PemValue value = new PemValue(pem);
92 success = true;
93 return value;
94 } finally {
95
96 if (!success && pem != null) {
97 pem.close();
98 }
99 }
100 }
101
102
103
104
105
106 private static Buffer append(
107 BufferAllocator alloc, PemEncoded encoded, int count, Buffer pem) {
108 Buffer content = encoded.content();
109
110 if (pem == null) {
111
112 pem = alloc.allocate(content.readableBytes() * count);
113 } else {
114 pem.ensureWritable(content.readableBytes());
115 }
116
117 int length = content.readableBytes();
118 pem.skipWritableBytes(length);
119 content.copyInto(content.readerOffset(), pem, pem.writerOffset(), length);
120 return pem;
121 }
122
123
124
125
126
127 private static Buffer append(BufferAllocator alloc, X509Certificate cert, int count, Buffer pem)
128 throws CertificateEncodingException {
129
130 try (Buffer encoded = alloc.copyOf(cert.getEncoded());
131 Buffer base64 = SslUtils.toBase64(alloc, encoded)) {
132 int length = BEGIN_CERT.length + base64.readableBytes() + END_CERT.length;
133 if (pem == null) {
134
135
136
137 pem = alloc.allocate(length * count);
138 } else {
139 pem.ensureWritable(length);
140 }
141
142 pem.writeBytes(BEGIN_CERT);
143 pem.writeBytes(base64);
144 pem.writeBytes(END_CERT);
145 }
146
147 return pem;
148 }
149
150
151
152
153
154
155
156 public static PemX509Certificate valueOf(byte[] key) {
157 return valueOf(offHeapAllocator().copyOf(key));
158 }
159
160
161
162
163
164
165
166 public static PemX509Certificate valueOf(Buffer key) {
167 return new PemX509Certificate(key);
168 }
169
170 private final Buffer content;
171
172 private PemX509Certificate(Buffer content) {
173 this.content = requireNonNull(content, "content").makeReadOnly();
174 }
175
176 @Override
177 public Buffer content() {
178 if (!content.isAccessible()) {
179 throw Statics.attachTrace((ResourceSupport<?, ?>) content,
180 new IllegalStateException("PemX509Certificate is closed."));
181 }
182
183 return content;
184 }
185
186 @Override
187 public void close() {
188 content.close();
189 }
190
191 @Override
192 public PemX509Certificate copy() {
193 return new PemX509Certificate(content.copy(true));
194 }
195
196 @Override
197 public Send<PemX509Certificate> send() {
198 return content.send().map(PemX509Certificate.class, PemX509Certificate::new);
199 }
200
201 @Override
202 public boolean isAccessible() {
203 return content.isAccessible();
204 }
205
206 @Override
207 public byte[] getEncoded() {
208 throw new UnsupportedOperationException();
209 }
210
211 @Override
212 public boolean hasUnsupportedCriticalExtension() {
213 throw new UnsupportedOperationException();
214 }
215
216 @Override
217 public Set<String> getCriticalExtensionOIDs() {
218 throw new UnsupportedOperationException();
219 }
220
221 @Override
222 public Set<String> getNonCriticalExtensionOIDs() {
223 throw new UnsupportedOperationException();
224 }
225
226 @Override
227 public byte[] getExtensionValue(String oid) {
228 throw new UnsupportedOperationException();
229 }
230
231 @Override
232 public void checkValidity() {
233 throw new UnsupportedOperationException();
234 }
235
236 @Override
237 public void checkValidity(Date date) {
238 throw new UnsupportedOperationException();
239 }
240
241 @Override
242 public int getVersion() {
243 throw new UnsupportedOperationException();
244 }
245
246 @Override
247 public BigInteger getSerialNumber() {
248 throw new UnsupportedOperationException();
249 }
250
251 @Override
252 public Principal getIssuerDN() {
253 throw new UnsupportedOperationException();
254 }
255
256 @Override
257 public Principal getSubjectDN() {
258 throw new UnsupportedOperationException();
259 }
260
261 @Override
262 public Date getNotBefore() {
263 throw new UnsupportedOperationException();
264 }
265
266 @Override
267 public Date getNotAfter() {
268 throw new UnsupportedOperationException();
269 }
270
271 @Override
272 public byte[] getTBSCertificate() {
273 throw new UnsupportedOperationException();
274 }
275
276 @Override
277 public byte[] getSignature() {
278 throw new UnsupportedOperationException();
279 }
280
281 @Override
282 public String getSigAlgName() {
283 throw new UnsupportedOperationException();
284 }
285
286 @Override
287 public String getSigAlgOID() {
288 throw new UnsupportedOperationException();
289 }
290
291 @Override
292 public byte[] getSigAlgParams() {
293 throw new UnsupportedOperationException();
294 }
295
296 @Override
297 public boolean[] getIssuerUniqueID() {
298 throw new UnsupportedOperationException();
299 }
300
301 @Override
302 public boolean[] getSubjectUniqueID() {
303 throw new UnsupportedOperationException();
304 }
305
306 @Override
307 public boolean[] getKeyUsage() {
308 throw new UnsupportedOperationException();
309 }
310
311 @Override
312 public int getBasicConstraints() {
313 throw new UnsupportedOperationException();
314 }
315
316 @Override
317 public void verify(PublicKey key) {
318 throw new UnsupportedOperationException();
319 }
320
321 @Override
322 public void verify(PublicKey key, String sigProvider) {
323 throw new UnsupportedOperationException();
324 }
325
326 @Override
327 public PublicKey getPublicKey() {
328 throw new UnsupportedOperationException();
329 }
330
331 @Override
332 public boolean equals(Object o) {
333 if (o == this) {
334 return true;
335 }
336 if (!(o instanceof PemX509Certificate)) {
337 return false;
338 }
339
340 PemX509Certificate other = (PemX509Certificate) o;
341 return content.equals(other.content);
342 }
343
344 @Override
345 public int hashCode() {
346 return content.hashCode();
347 }
348
349 @Override
350 public String toString() {
351 return content.toString(CharsetUtil.UTF_8);
352 }
353 }