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.schema.SchemaBuilder;
48 import org.opensaml.xml.security.SecurityException;
49 import org.opensaml.xml.security.SecurityHelper;
50 import org.opensaml.xml.util.DatatypeHelper;
51 import org.opensaml.xml.util.IPAddressHelper;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54
55
56
57
58 public class X509Util {
59
60
61 public static enum ENCODING_FORMAT {
62 PEM, DER
63 };
64
65
66 public static final String CN_OID = "2.5.4.3";
67
68
69 public static final Integer OTHER_ALT_NAME = new Integer(0);
70
71
72 public static final Integer RFC822_ALT_NAME = new Integer(1);
73
74
75 public static final Integer DNS_ALT_NAME = new Integer(2);
76
77
78 public static final Integer X400ADDRESS_ALT_NAME = new Integer(3);
79
80
81 public static final Integer DIRECTORY_ALT_NAME = new Integer(4);
82
83
84 public static final Integer EDI_PARTY_ALT_NAME = new Integer(5);
85
86
87 public static final Integer URI_ALT_NAME = new Integer(6);
88
89
90 public static final Integer IP_ADDRESS_ALT_NAME = new Integer(7);
91
92
93 public static final Integer REGISTERED_ID_ALT_NAME = new Integer(8);
94
95
96 protected X509Util() {
97
98 }
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113 public static X509Certificate determineEntityCertificate(Collection<X509Certificate> certs, PrivateKey privateKey)
114 throws SecurityException {
115 if (certs == null || privateKey == null) {
116 return null;
117 }
118
119 for (X509Certificate certificate : certs) {
120 if (SecurityHelper.matchKeyPair(certificate.getPublicKey(), privateKey)) {
121 return certificate;
122 }
123 }
124
125 return null;
126 }
127
128
129
130
131
132
133
134
135
136 public static List<String> getCommonNames(X500Principal dn) {
137 Logger log = getLogger();
138 if (dn == null) {
139 return null;
140 }
141
142 log.debug("Extracting CNs from the following DN: {}", dn.toString());
143 List<String> commonNames = new LinkedList<String>();
144 try {
145 ASN1InputStream asn1Stream = new ASN1InputStream(dn.getEncoded());
146 DERObject parent = asn1Stream.readObject();
147
148 String cn = null;
149 DERObject dnComponent;
150 DERSequence grandChild;
151 DERObjectIdentifier componentId;
152 for (int i = 0; i < ((DERSequence) parent).size(); i++) {
153 dnComponent = ((DERSequence) parent).getObjectAt(i).getDERObject();
154 if (!(dnComponent instanceof DERSet)) {
155 log.debug("No DN components.");
156 continue;
157 }
158
159
160 for (int j = 0; j < ((DERSet) dnComponent).size(); j++) {
161 grandChild = (DERSequence) ((DERSet) dnComponent).getObjectAt(j).getDERObject();
162
163 if (grandChild.getObjectAt(0) != null
164 && grandChild.getObjectAt(0).getDERObject() instanceof DERObjectIdentifier) {
165 componentId = (DERObjectIdentifier) grandChild.getObjectAt(0).getDERObject();
166
167 if (CN_OID.equals(componentId.getId())) {
168
169 if (grandChild.getObjectAt(1) != null
170 && grandChild.getObjectAt(1).getDERObject() instanceof DERString) {
171 cn = ((DERString) grandChild.getObjectAt(1).getDERObject()).getString();
172 commonNames.add(cn);
173 }
174 }
175 }
176 }
177 }
178
179 asn1Stream.close();
180
181 return commonNames;
182
183 } catch (IOException e) {
184 log.error("Unable to extract common names from DN: ASN.1 parsing failed: " + e);
185 return null;
186 }
187 }
188
189
190
191
192
193
194
195
196
197 public static List getAltNames(X509Certificate certificate, Integer[] nameTypes) {
198 Logger log = getLogger();
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 Logger log = getLogger();
259 byte[] derValue = certificate.getExtensionValue(X509Extensions.SubjectKeyIdentifier.getId());
260 if (derValue == null || derValue.length == 0) {
261 return null;
262 }
263
264 SubjectKeyIdentifier ski = null;
265 try {
266 ski = new SubjectKeyIdentifierStructure(derValue);
267 } catch (IOException e) {
268 log.error("Unable to extract subject key identifier from certificate: ASN.1 parsing failed: " + e);
269 return null;
270 }
271
272 if (ski != null) {
273 return ski.getKeyIdentifier();
274 } else {
275 return null;
276 }
277 }
278
279
280
281
282
283
284
285
286
287
288
289
290 public static Collection<X509Certificate> decodeCertificate(File certs) throws CertificateException{
291 if(!certs.exists()){
292 throw new CertificateException("Certificate file " + certs.getAbsolutePath() + " does not exist");
293 }
294
295 if(!certs.canRead()){
296 throw new CertificateException("Certificate file " + certs.getAbsolutePath() + " is not readable");
297 }
298
299 try{
300 return decodeCertificate(DatatypeHelper.fileToByteArray(certs));
301 }catch(IOException e){
302 throw new CertificateException("Error reading certificate file " + certs.getAbsolutePath(), e);
303 }
304 }
305
306
307
308
309
310
311
312
313
314
315 @SuppressWarnings("unchecked")
316 public static Collection<X509Certificate> decodeCertificate(byte[] certs) throws CertificateException {
317 try {
318 TrustMaterial tm = new TrustMaterial(certs);
319 return tm.getCertificates();
320 } catch (Exception e) {
321 throw new CertificateException("Unable to decode X.509 certificates", e);
322 }
323 }
324
325
326
327
328
329
330
331
332
333
334
335
336
337 public static Collection<X509CRL> decodeCRLs(File crls) throws CRLException{
338 if(!crls.exists()){
339 throw new CRLException("CRL file " + crls.getAbsolutePath() + " does not exist");
340 }
341
342 if(!crls.canRead()){
343 throw new CRLException("CRL file " + crls.getAbsolutePath() + " is not readable");
344 }
345
346 try{
347 return decodeCRLs(DatatypeHelper.fileToByteArray(crls));
348 }catch(IOException e){
349 throw new CRLException("Error reading CRL file " + crls.getAbsolutePath(), e);
350 }
351 }
352
353
354
355
356
357
358
359
360
361
362
363 @SuppressWarnings("unchecked")
364 public static Collection<X509CRL> decodeCRLs(byte[] crls) throws CRLException {
365 try {
366 CertificateFactory cf = CertificateFactory.getInstance("X.509");
367 return (Collection<X509CRL>) cf.generateCRLs(new ByteArrayInputStream(crls));
368 } catch (GeneralSecurityException e) {
369 throw new CRLException("Unable to decode X.509 certificates");
370 }
371 }
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391 public static String getIdentifiersToken(X509Credential credential, X500DNHandler handler) {
392 X500DNHandler x500DNHandler;
393 if (handler != null) {
394 x500DNHandler = handler;
395 } else {
396 x500DNHandler = new InternalX500DNHandler();
397 }
398 X500Principal x500Principal = credential.getEntityCertificate().getSubjectX500Principal();
399 StringBuilder builder = new StringBuilder();
400 builder.append('[');
401 builder.append(String.format("subjectName='%s'", x500DNHandler.getName(x500Principal)));
402 if (!DatatypeHelper.isEmpty(credential.getEntityId())) {
403 builder.append(String.format(" |credential entityID='%s'", DatatypeHelper.safeTrimOrNullString(credential
404 .getEntityId())));
405 }
406 builder.append(']');
407 return builder.toString();
408 }
409
410
411
412
413
414
415
416
417
418 private static Object convertAltNameType(Integer nameType, Object nameValue) {
419 Logger log = getLogger();
420 if (DIRECTORY_ALT_NAME.equals(nameType) || DNS_ALT_NAME.equals(nameType) || RFC822_ALT_NAME.equals(nameType)
421 || URI_ALT_NAME.equals(nameType) || REGISTERED_ID_ALT_NAME.equals(nameType)) {
422
423
424 return nameValue;
425 }
426
427 if (IP_ADDRESS_ALT_NAME.equals(nameType)) {
428
429 return IPAddressHelper.addressToString((byte[]) nameValue);
430 }
431
432 if (EDI_PARTY_ALT_NAME.equals(nameType) || X400ADDRESS_ALT_NAME.equals(nameType)
433 || OTHER_ALT_NAME.equals(nameType)) {
434
435
436 return ((DERObject) nameValue).getDEREncoded();
437 }
438
439 log.warn("Encountered unknown alt name type '{}', adding as-is", nameType);
440 return nameValue;
441 }
442
443
444
445
446
447
448 private static Logger getLogger() {
449 return LoggerFactory.getLogger(X509Util.class);
450 }
451 }