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