View Javadoc

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 }