1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.opensaml.xml.security.x509;
18
19 import java.io.ByteArrayInputStream;
20 import java.io.File;
21 import java.io.IOException;
22 import java.security.GeneralSecurityException;
23 import java.security.PrivateKey;
24 import java.security.cert.CRLException;
25 import java.security.cert.CertificateException;
26 import java.security.cert.CertificateFactory;
27 import java.security.cert.CertificateParsingException;
28 import java.security.cert.X509CRL;
29 import java.security.cert.X509Certificate;
30 import java.util.Collection;
31 import java.util.LinkedList;
32 import java.util.List;
33
34 import javax.security.auth.x500.X500Principal;
35
36 import org.apache.commons.ssl.TrustMaterial;
37 import org.bouncycastle.asn1.ASN1InputStream;
38 import org.bouncycastle.asn1.DERObject;
39 import org.bouncycastle.asn1.DERObjectIdentifier;
40 import org.bouncycastle.asn1.DERSequence;
41 import org.bouncycastle.asn1.DERSet;
42 import org.bouncycastle.asn1.DERString;
43 import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
44 import org.bouncycastle.asn1.x509.X509Extensions;
45 import org.bouncycastle.x509.extension.SubjectKeyIdentifierStructure;
46 import org.bouncycastle.x509.extension.X509ExtensionUtil;
47 import org.opensaml.xml.security.SecurityException;
48 import org.opensaml.xml.security.SecurityHelper;
49 import org.opensaml.xml.util.DatatypeHelper;
50 import org.opensaml.xml.util.IPAddressHelper;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
53
54
55
56
57 public class X509Util {
58
59
60 public static enum ENCODING_FORMAT {
61 PEM, DER
62 };
63
64
65 public static final String CN_OID = "2.5.4.3";
66
67
68 public static final Integer OTHER_ALT_NAME = new Integer(0);
69
70
71 public static final Integer RFC822_ALT_NAME = new Integer(1);
72
73
74 public static final Integer DNS_ALT_NAME = new Integer(2);
75
76
77 public static final Integer X400ADDRESS_ALT_NAME = new Integer(3);
78
79
80 public static final Integer DIRECTORY_ALT_NAME = new Integer(4);
81
82
83 public static final Integer EDI_PARTY_ALT_NAME = new Integer(5);
84
85
86 public static final Integer URI_ALT_NAME = new Integer(6);
87
88
89 public static final Integer IP_ADDRESS_ALT_NAME = new Integer(7);
90
91
92 public static final Integer REGISTERED_ID_ALT_NAME = new Integer(8);
93
94
95 private static Logger log = LoggerFactory.getLogger(X509Util.class);
96
97
98 protected X509Util() {
99
100 }
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115 public static X509Certificate determineEntityCertificate(Collection<X509Certificate> certs, PrivateKey privateKey)
116 throws SecurityException {
117 if (certs == null || privateKey == null) {
118 return null;
119 }
120
121 for (X509Certificate certificate : certs) {
122 if (SecurityHelper.matchKeyPair(certificate.getPublicKey(), privateKey)) {
123 return certificate;
124 }
125 }
126
127 return null;
128 }
129
130
131
132
133
134
135
136
137
138 public static List<String> getCommonNames(X500Principal dn) {
139 if (dn == null) {
140 return null;
141 }
142
143 log.debug("Extracting CNs from the following DN: {}", dn.toString());
144 List<String> commonNames = new LinkedList<String>();
145 try {
146 ASN1InputStream asn1Stream = new ASN1InputStream(dn.getEncoded());
147 DERObject parent = asn1Stream.readObject();
148
149 String cn = null;
150 DERObject dnComponent;
151 DERSequence grandChild;
152 DERObjectIdentifier componentId;
153 for (int i = 0; i < ((DERSequence) parent).size(); i++) {
154 dnComponent = ((DERSequence) parent).getObjectAt(i).getDERObject();
155 if (!(dnComponent instanceof DERSet)) {
156 log.debug("No DN components.");
157 continue;
158 }
159
160
161 for (int j = 0; j < ((DERSet) dnComponent).size(); j++) {
162 grandChild = (DERSequence) ((DERSet) dnComponent).getObjectAt(j).getDERObject();
163
164 if (grandChild.getObjectAt(0) != null
165 && grandChild.getObjectAt(0).getDERObject() instanceof DERObjectIdentifier) {
166 componentId = (DERObjectIdentifier) grandChild.getObjectAt(0).getDERObject();
167
168 if (CN_OID.equals(componentId.getId())) {
169
170 if (grandChild.getObjectAt(1) != null
171 && grandChild.getObjectAt(1).getDERObject() instanceof DERString) {
172 cn = ((DERString) grandChild.getObjectAt(1).getDERObject()).getString();
173 commonNames.add(cn);
174 }
175 }
176 }
177 }
178 }
179
180 asn1Stream.close();
181
182 return commonNames;
183
184 } catch (IOException e) {
185 log.error("Unable to extract common names from DN: ASN.1 parsing failed: " + e);
186 return null;
187 }
188 }
189
190
191
192
193
194
195
196
197
198 public static List getAltNames(X509Certificate certificate, Integer[] nameTypes) {
199 if (certificate == null) {
200 return null;
201 }
202
203 List<Object> names = new LinkedList<Object>();
204 Collection<List<?>> altNames = null;
205 try {
206 altNames = X509ExtensionUtil.getSubjectAlternativeNames(certificate);
207 } catch (CertificateParsingException e) {
208 log.error("Encountered an problem trying to extract Subject Alternate "
209 + "Name from supplied certificate: " + e);
210 return names;
211 }
212
213 if (altNames != null) {
214
215
216 for (List altName : altNames) {
217 for (Integer nameType : nameTypes) {
218 if (altName.get(0).equals(nameType)) {
219 names.add(convertAltNameType(nameType, altName.get(1)));
220 break;
221 }
222 }
223 }
224 }
225
226 return names;
227 }
228
229
230
231
232
233
234
235
236
237 @SuppressWarnings("unchecked")
238 public static List getSubjectNames(X509Certificate certificate, Integer[] altNameTypes) {
239 List issuerNames = new LinkedList();
240
241 List<String> entityCertCNs = X509Util.getCommonNames(certificate.getSubjectX500Principal());
242 issuerNames.add(entityCertCNs.get(0));
243 issuerNames.addAll(X509Util.getAltNames(certificate, altNameTypes));
244
245 return issuerNames;
246 }
247
248
249
250
251
252
253
254
255
256
257 public static byte[] getSubjectKeyIdentifier(X509Certificate certificate) {
258 byte[] derValue = certificate.getExtensionValue(X509Extensions.SubjectKeyIdentifier.getId());
259 if (derValue == null || derValue.length == 0) {
260 return null;
261 }
262
263 SubjectKeyIdentifier ski = null;
264 try {
265 ski = new SubjectKeyIdentifierStructure(derValue);
266 } catch (IOException e) {
267 log.error("Unable to extract subject key identifier from certificate: ASN.1 parsing failed: " + e);
268 return null;
269 }
270
271 if (ski != null) {
272 return ski.getKeyIdentifier();
273 } else {
274 return null;
275 }
276 }
277
278
279
280
281
282
283
284
285
286
287
288
289 public static Collection<X509Certificate> decodeCertificate(File certs) throws CertificateException{
290 if(!certs.exists()){
291 throw new CertificateException("Certificate file " + certs.getAbsolutePath() + " does not exist");
292 }
293
294 if(!certs.canRead()){
295 throw new CertificateException("Certificate file " + certs.getAbsolutePath() + " is not readable");
296 }
297
298 try{
299 return decodeCertificate(DatatypeHelper.fileToByteArray(certs));
300 }catch(IOException e){
301 throw new CertificateException("Error reading certificate file " + certs.getAbsolutePath(), e);
302 }
303 }
304
305
306
307
308
309
310
311
312
313
314 @SuppressWarnings("unchecked")
315 public static Collection<X509Certificate> decodeCertificate(byte[] certs) throws CertificateException {
316 try {
317 TrustMaterial tm = new TrustMaterial(certs);
318 return tm.getCertificates();
319 } catch (Exception e) {
320 throw new CertificateException("Unable to decode X.509 certificates", e);
321 }
322 }
323
324
325
326
327
328
329
330
331
332
333
334
335
336 public static Collection<X509CRL> decodeCRLs(File crls) throws CRLException{
337 if(!crls.exists()){
338 throw new CRLException("CRL file " + crls.getAbsolutePath() + " does not exist");
339 }
340
341 if(!crls.canRead()){
342 throw new CRLException("CRL file " + crls.getAbsolutePath() + " is not readable");
343 }
344
345 try{
346 return decodeCRLs(DatatypeHelper.fileToByteArray(crls));
347 }catch(IOException e){
348 throw new CRLException("Error reading CRL file " + crls.getAbsolutePath(), e);
349 }
350 }
351
352
353
354
355
356
357
358
359
360
361
362 @SuppressWarnings("unchecked")
363 public static Collection<X509CRL> decodeCRLs(byte[] crls) throws CRLException {
364 try {
365 CertificateFactory cf = CertificateFactory.getInstance("X.509");
366 return (Collection<X509CRL>) cf.generateCRLs(new ByteArrayInputStream(crls));
367 } catch (GeneralSecurityException e) {
368 throw new CRLException("Unable to decode X.509 certificates");
369 }
370 }
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390 public static String getIdentifiersToken(X509Credential credential, X500DNHandler handler) {
391 X500DNHandler x500DNHandler;
392 if (handler != null) {
393 x500DNHandler = handler;
394 } else {
395 x500DNHandler = new InternalX500DNHandler();
396 }
397 X500Principal x500Principal = credential.getEntityCertificate().getSubjectX500Principal();
398 StringBuilder builder = new StringBuilder();
399 builder.append('[');
400 builder.append(String.format("subjectName='%s'", x500DNHandler.getName(x500Principal)));
401 if (!DatatypeHelper.isEmpty(credential.getEntityId())) {
402 builder.append(String.format(" |credential entityID='%s'", DatatypeHelper.safeTrimOrNullString(credential
403 .getEntityId())));
404 }
405 builder.append(']');
406 return builder.toString();
407 }
408
409
410
411
412
413
414
415
416
417 private static Object convertAltNameType(Integer nameType, Object nameValue) {
418 if (DIRECTORY_ALT_NAME.equals(nameType) || DNS_ALT_NAME.equals(nameType) || RFC822_ALT_NAME.equals(nameType)
419 || URI_ALT_NAME.equals(nameType) || REGISTERED_ID_ALT_NAME.equals(nameType)) {
420
421
422 return nameValue;
423 }
424
425 if (IP_ADDRESS_ALT_NAME.equals(nameType)) {
426
427 return IPAddressHelper.addressToString((byte[]) nameValue);
428 }
429
430 if (EDI_PARTY_ALT_NAME.equals(nameType) || X400ADDRESS_ALT_NAME.equals(nameType)
431 || OTHER_ALT_NAME.equals(nameType)) {
432
433
434 return ((DERObject) nameValue).getDEREncoded();
435 }
436
437 log.warn("Encountered unknown alt name type '{}', adding as-is", nameType);
438 return nameValue;
439 }
440 }