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