1 /**
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20 package org.apache.ws.security.components.crypto;
21
22 import org.apache.ws.security.WSSecurityException;
23 import org.apache.ws.security.util.WSSecurityUtil;
24
25 import java.io.ByteArrayInputStream;
26 import java.io.InputStream;
27 import java.lang.reflect.Constructor;
28 import java.security.NoSuchProviderException;
29 import java.security.cert.CertPath;
30 import java.security.cert.CertificateEncodingException;
31 import java.security.cert.CertificateException;
32 import java.security.cert.CertificateFactory;
33 import java.security.cert.X509Certificate;
34 import java.util.Arrays;
35 import java.util.HashMap;
36 import java.util.Iterator;
37 import java.util.List;
38 import java.util.Map;
39 import javax.security.auth.x500.X500Principal;
40
41 /**
42 * This Abstract Base Class implements the accessor and keystore-independent methods and
43 * functionality of the Crypto interface.
44 */
45 public abstract class CryptoBase implements Crypto {
46 public static final String SKI_OID = "2.5.29.14";
47 /**
48 * OID For the NameConstraints Extension to X.509
49 *
50 * http://java.sun.com/j2se/1.4.2/docs/api/
51 * http://www.ietf.org/rfc/rfc3280.txt (s. 4.2.1.11)
52 */
53 public static final String NAME_CONSTRAINTS_OID = "2.5.29.30";
54
55 private static final Constructor<?> BC_509CLASS_CONS;
56
57 protected Map<String, CertificateFactory> certFactMap =
58 new HashMap<String, CertificateFactory>();
59 protected String defaultAlias = null;
60 protected String cryptoProvider = null;
61
62 static {
63 Constructor<?> cons = null;
64 try {
65 Class<?> c = Class.forName("org.bouncycastle.asn1.x509.X509Name");
66 cons = c.getConstructor(new Class[] {String.class});
67 } catch (Exception e) {
68 //ignore
69 }
70 BC_509CLASS_CONS = cons;
71 }
72
73 /**
74 * Constructor
75 */
76 protected CryptoBase() {
77 }
78
79 /**
80 * Get the crypto provider associated with this implementation
81 * @return the crypto provider
82 */
83 public String getCryptoProvider() {
84 return cryptoProvider;
85 }
86
87 /**
88 * Set the crypto provider associated with this implementation
89 * @param provider the crypto provider to set
90 */
91 public void setCryptoProvider(String provider) {
92 cryptoProvider = provider;
93 }
94
95 /**
96 * Retrieves the identifier name of the default certificate. This should be the certificate
97 * that is used for signature and encryption. This identifier corresponds to the certificate
98 * that should be used whenever KeyInfo is not present in a signed or an encrypted
99 * message. May return null. The identifier is implementation specific, e.g. it could be the
100 * KeyStore alias.
101 *
102 * @return name of the default X509 certificate.
103 */
104 public String getDefaultX509Identifier() throws WSSecurityException {
105 return defaultAlias;
106 }
107
108 /**
109 * Sets the identifier name of the default certificate. This should be the certificate
110 * that is used for signature and encryption. This identifier corresponds to the certificate
111 * that should be used whenever KeyInfo is not present in a signed or an encrypted
112 * message. The identifier is implementation specific, e.g. it could be the KeyStore alias.
113 *
114 * @param identifier name of the default X509 certificate.
115 */
116 public void setDefaultX509Identifier(String identifier) {
117 defaultAlias = identifier;
118 }
119
120 /**
121 * Sets the CertificateFactory instance on this Crypto instance
122 *
123 * @param provider the CertificateFactory provider name
124 * @param certFactory the CertificateFactory the CertificateFactory instance to set
125 */
126 public void setCertificateFactory(String provider, CertificateFactory certFactory) {
127 if (provider == null || provider.length() == 0) {
128 certFactMap.put(certFactory.getProvider().getName(), certFactory);
129 } else {
130 certFactMap.put(provider, certFactory);
131 }
132 }
133
134 /**
135 * Get the CertificateFactory instance on this Crypto instance
136 *
137 * @return Returns a <code>CertificateFactory</code> to construct
138 * X509 certificates
139 * @throws org.apache.ws.security.WSSecurityException
140 */
141 public CertificateFactory getCertificateFactory() throws WSSecurityException {
142 String provider = getCryptoProvider();
143
144 //Try to find a CertificateFactory that generates certs that are fully
145 //compatible with the certs in the KeyStore (Sun -> Sun, BC -> BC, etc...)
146 CertificateFactory factory = null;
147 if (provider != null && provider.length() != 0) {
148 factory = certFactMap.get(provider);
149 } else {
150 factory = certFactMap.get("DEFAULT");
151 }
152 if (factory == null) {
153 try {
154 if (provider == null || provider.length() == 0) {
155 factory = CertificateFactory.getInstance("X.509");
156 certFactMap.put("DEFAULT", factory);
157 } else {
158 factory = CertificateFactory.getInstance("X.509", provider);
159 certFactMap.put(provider, factory);
160 }
161 certFactMap.put(factory.getProvider().getName(), factory);
162 } catch (CertificateException e) {
163 throw new WSSecurityException(
164 WSSecurityException.SECURITY_TOKEN_UNAVAILABLE, "unsupportedCertType",
165 null, e
166 );
167 } catch (NoSuchProviderException e) {
168 throw new WSSecurityException(
169 WSSecurityException.SECURITY_TOKEN_UNAVAILABLE, "noSecProvider",
170 null, e
171 );
172 }
173 }
174 return factory;
175 }
176
177 /**
178 * Load a X509Certificate from the input stream.
179 *
180 * @param in The <code>InputStream</code> containing the X509Certificate
181 * @return An X509 certificate
182 * @throws org.apache.ws.security.WSSecurityException
183 */
184 public X509Certificate loadCertificate(InputStream in) throws WSSecurityException {
185 try {
186 CertificateFactory certFactory = getCertificateFactory();
187 return (X509Certificate) certFactory.generateCertificate(in);
188 } catch (CertificateException e) {
189 throw new WSSecurityException(
190 WSSecurityException.SECURITY_TOKEN_UNAVAILABLE, "parseError",
191 null, e
192 );
193 }
194 }
195
196 /**
197 * Reads the SubjectKeyIdentifier information from the certificate.
198 * <p/>
199 * If the the certificate does not contain a SKI extension then
200 * try to compute the SKI according to RFC3280 using the
201 * SHA-1 hash value of the public key. The second method described
202 * in RFC3280 is not support. Also only RSA public keys are supported.
203 * If we cannot compute the SKI throw a WSSecurityException.
204 *
205 * @param cert The certificate to read SKI
206 * @return The byte array containing the binary SKI data
207 */
208 public byte[] getSKIBytesFromCert(X509Certificate cert) throws WSSecurityException {
209 //
210 // Gets the DER-encoded OCTET string for the extension value (extnValue)
211 // identified by the passed-in oid String. The oid string is represented
212 // by a set of positive whole numbers separated by periods.
213 //
214 byte[] derEncodedValue = cert.getExtensionValue(SKI_OID);
215
216 if (cert.getVersion() < 3 || derEncodedValue == null) {
217 X509SubjectPublicKeyInfo spki = new X509SubjectPublicKeyInfo(cert.getPublicKey());
218 byte[] value = spki.getSubjectPublicKey();
219 try {
220 return WSSecurityUtil.generateDigest(value);
221 } catch (WSSecurityException ex) {
222 throw new WSSecurityException(
223 WSSecurityException.UNSUPPORTED_SECURITY_TOKEN, "noSKIHandling",
224 new Object[]{"No SKI certificate extension and no SHA1 message digest available"},
225 ex
226 );
227 }
228 }
229
230 //
231 // Strip away first (four) bytes from the DerValue (tag and length of
232 // ExtensionValue OCTET STRING and KeyIdentifier OCTET STRING)
233 //
234 DERDecoder extVal = new DERDecoder(derEncodedValue);
235 extVal.expect(DERDecoder.TYPE_OCTET_STRING); // ExtensionValue OCTET STRING
236 extVal.getLength();
237 extVal.expect(DERDecoder.TYPE_OCTET_STRING); // KeyIdentifier OCTET STRING
238 int keyIDLen = extVal.getLength();
239 return extVal.getBytes(keyIDLen);
240 }
241
242 /**
243 * Get a byte array given an array of X509 certificates.
244 * <p/>
245 *
246 * @param certs The certificates to convert
247 * @return The byte array for the certificates
248 * @throws WSSecurityException
249 */
250 public byte[] getBytesFromCertificates(X509Certificate[] certs)
251 throws WSSecurityException {
252 try {
253 CertPath path = getCertificateFactory().generateCertPath(Arrays.asList(certs));
254 return path.getEncoded();
255 } catch (CertificateEncodingException e) {
256 throw new WSSecurityException(
257 WSSecurityException.SECURITY_TOKEN_UNAVAILABLE, "encodeError",
258 null, e
259 );
260 } catch (CertificateException e) {
261 throw new WSSecurityException(
262 WSSecurityException.SECURITY_TOKEN_UNAVAILABLE, "parseError",
263 null, e
264 );
265 }
266 }
267
268 /**
269 * Construct an array of X509Certificate's from the byte array.
270 * <p/>
271 *
272 * @param data The <code>byte</code> array containing the X509 data
273 * @return An array of X509 certificates
274 * @throws WSSecurityException
275 */
276 public X509Certificate[] getCertificatesFromBytes(byte[] data)
277 throws WSSecurityException {
278 InputStream in = new ByteArrayInputStream(data);
279 CertPath path = null;
280 try {
281 path = getCertificateFactory().generateCertPath(in);
282 } catch (CertificateException e) {
283 throw new WSSecurityException(
284 WSSecurityException.SECURITY_TOKEN_UNAVAILABLE, "parseError",
285 null, e
286 );
287 }
288 List<?> l = path.getCertificates();
289 X509Certificate[] certs = new X509Certificate[l.size()];
290 int i = 0;
291 for (Iterator<?> iterator = l.iterator(); iterator.hasNext(); ) {
292 certs[i++] = (X509Certificate) iterator.next();
293 }
294 return certs;
295 }
296
297 protected Object createBCX509Name(String s) {
298 if (BC_509CLASS_CONS != null) {
299 try {
300 return BC_509CLASS_CONS.newInstance(new Object[] {s});
301 } catch (Exception e) {
302 //ignore
303 }
304 }
305 return new X500Principal(s);
306 }
307
308 }