1 /*
2  * Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 /*
26  * $Id: TransformService.java,v 1.6.4.1 2005/09/15 12:42:11 mullan Exp $
27  */
28 package javax.xml.crypto.dsig;
29 
30 import java.security.InvalidAlgorithmParameterException;
31 import java.security.NoSuchAlgorithmException;
32 import java.security.NoSuchProviderException;
33 import java.security.Provider;
34 import java.security.Provider.Service;
35 import java.security.Security;
36 import java.util.*;
37 import javax.xml.crypto.MarshalException;
38 import javax.xml.crypto.XMLStructure;
39 import javax.xml.crypto.XMLCryptoContext;
40 import javax.xml.crypto.dsig.spec.TransformParameterSpec;
41 
42 
43 /**
44  * A Service Provider Interface for transform and canonicalization algorithms.
45  *
46  * <p>Each instance of <code>TransformService</code> supports a specific
47  * transform or canonicalization algorithm and XML mechanism type. To create a
48  * <code>TransformService</code>, call one of the static
49  * {@link #getInstance getInstance} methods, passing in the algorithm URI and
50  * XML mechanism type desired, for example:
51  *
52  * <blockquote><code>
53  * TransformService ts = TransformService.getInstance(Transform.XPATH2, "DOM");
54  * </code></blockquote>
55  *
56  * <p><code>TransformService</code> implementations are registered and loaded
57  * using the {@link java.security.Provider} mechanism.  Each
58  * <code>TransformService</code> service provider implementation should include
59  * a <code>MechanismType</code> service attribute that identifies the XML
60  * mechanism type that it supports. If the attribute is not specified,
61  * "DOM" is assumed. For example, a service provider that supports the
62  * XPath Filter 2 Transform and DOM mechanism would be specified in the
63  * <code>Provider</code> subclass as:
64  * <pre>
65  *     put("TransformService." + Transform.XPATH2,
66  *         "org.example.XPath2TransformService");
67  *     put("TransformService." + Transform.XPATH2 + " MechanismType", "DOM");
68  * </pre>
69  * <code>TransformService</code> implementations that support the DOM
70  * mechanism type must abide by the DOM interoperability requirements defined
71  * in the <a href="package-summary.html#dom_req">DOM Mechanism
72  * Requirements</a>. See the {@code TransformService} section in the <a href=
73  * "{@docRoot}/../specs/security/standard-names.html#xml-signature-xmlsignaturefactorykeyinfofactorytransformservice-mechanisms">
74  * Java Security Standard Algorithm Names Specification</a> for a list of
75  * standard algorithm URIs and mechanism types.
76  * <p>
77  * Once a <code>TransformService</code> has been created, it can be used
78  * to process <code>Transform</code> or <code>CanonicalizationMethod</code>
79  * objects. If the <code>Transform</code> or <code>CanonicalizationMethod</code>
80  * exists in XML form (for example, when validating an existing
81  * <code>XMLSignature</code>), the {@link #init(XMLStructure, XMLCryptoContext)}
82  * method must be first called to initialize the transform and provide document
83  * context (even if there are no parameters). Alternatively, if the
84  * <code>Transform</code> or <code>CanonicalizationMethod</code> is being
85  * created from scratch, the {@link #init(TransformParameterSpec)} method
86  * is called to initialize the transform with parameters and the
87  * {@link #marshalParams marshalParams} method is called to marshal the
88  * parameters to XML and provide the transform with document context. Finally,
89  * the {@link #transform transform} method is called to perform the
90  * transformation.
91  * <p>
92  * <b>Concurrent Access</b>
93  * <p>The static methods of this class are guaranteed to be thread-safe.
94  * Multiple threads may concurrently invoke the static methods defined in this
95  * class with no ill effects.
96  *
97  * <p>However, this is not true for the non-static methods defined by this
98  * class. Unless otherwise documented by a specific provider, threads that
99  * need to access a single <code>TransformService</code> instance
100  * concurrently should synchronize amongst themselves and provide the
101  * necessary locking. Multiple threads each manipulating a different
102  * <code>TransformService</code> instance need not synchronize.
103  *
104  * @author Sean Mullan
105  * @author JSR 105 Expert Group
106  * @since 1.6
107  */
108 public abstract class TransformService implements Transform {
109 
110     private String algorithm;
111     private String mechanism;
112     private Provider provider;
113 
114     /**
115      * Default constructor, for invocation by subclasses.
116      */
TransformService()117     protected TransformService() {}
118 
119     /**
120      * Returns a <code>TransformService</code> that supports the specified
121      * algorithm URI (ex: {@link Transform#XPATH2}) and mechanism type
122      * (ex: DOM).
123      *
124      * <p>This method uses the standard JCA provider lookup mechanism to
125      * locate and instantiate a <code>TransformService</code> implementation
126      * of the desired algorithm and <code>MechanismType</code> service
127      * attribute. It traverses the list of registered security
128      * <code>Provider</code>s, starting with the most preferred
129      * <code>Provider</code>. A new <code>TransformService</code> object
130      * from the first <code>Provider</code> that supports the specified
131      * algorithm and mechanism type is returned.
132      *
133      * <p> Note that the list of registered providers may be retrieved via
134      * the {@link Security#getProviders() Security.getProviders()} method.
135      *
136      * @implNote
137      * The JDK Reference Implementation additionally uses the
138      * {@code jdk.security.provider.preferred}
139      * {@link Security#getProperty(String) Security} property to determine
140      * the preferred provider order for the specified algorithm. This
141      * may be different than the order of providers returned by
142      * {@link Security#getProviders() Security.getProviders()}.
143      *
144      * @param algorithm the URI of the algorithm. See the
145      *    {@code TransformService} section in the
146      *    <a href=
147      *    "{@docRoot}/../specs/security/standard-names.html#xml-signature-transform-transformservice-algorithms">
148      *    Java Security Standard Algorithm Names Specification</a> for a list of
149      *    standard transform algorithms.
150      * @param mechanismType the type of the XML processing mechanism and
151      *    representation. See the {@code TransformService} section in the
152      *    <a href=
153      *    "{@docRoot}/../specs/security/standard-names.html#xml-signature-xmlsignaturefactorykeyinfofactorytransformservice-mechanisms">
154      *    Java Security Standard Algorithm Names Specification</a> for a list of
155      *    standard mechanism types.
156      * @return a new <code>TransformService</code>
157      * @throws NullPointerException if <code>algorithm</code> or
158      *   <code>mechanismType</code> is  <code>null</code>
159      * @throws NoSuchAlgorithmException if no <code>Provider</code> supports a
160      *   <code>TransformService</code> implementation for the specified
161      *   algorithm and mechanism type
162      * @see Provider
163      */
getInstance(String algorithm, String mechanismType)164     public static TransformService getInstance
165         (String algorithm, String mechanismType)
166         throws NoSuchAlgorithmException {
167         if (mechanismType == null || algorithm == null) {
168             throw new NullPointerException();
169         }
170         boolean dom = false;
171         if (mechanismType.equals("DOM")) {
172             dom = true;
173         }
174 
175         Provider[] provs = Security.getProviders();
176         for (Provider p : provs) {
177             Service s = p.getService("TransformService", algorithm);
178             if (s != null) {
179                 String value = s.getAttribute("MechanismType");
180                 if ((value == null && dom) ||
181                     (value != null && value.equals(mechanismType))) {
182                     Object obj = s.newInstance(null);
183                     if (obj instanceof TransformService) {
184                         TransformService ts = (TransformService) obj;
185                         ts.algorithm = algorithm;
186                         ts.mechanism = mechanismType;
187                         ts.provider = p;
188                         return ts;
189                     }
190                 }
191             }
192         }
193         throw new NoSuchAlgorithmException
194             (algorithm + " algorithm and " + mechanismType
195                  + " mechanism not available");
196     }
197 
198     /**
199      * Returns a <code>TransformService</code> that supports the specified
200      * algorithm URI (ex: {@link Transform#XPATH2}) and mechanism type
201      * (ex: DOM) as supplied by the specified provider. Note that the specified
202      * <code>Provider</code> object does not have to be registered in the
203      * provider list.
204      *
205      * @param algorithm the URI of the algorithm. See the
206      *    {@code TransformService} section in the
207      *    <a href=
208      *    "{@docRoot}/../specs/security/standard-names.html#xml-signature-transform-transformservice-algorithms">
209      *    Java Security Standard Algorithm Names Specification</a> for a list of
210      *    standard transform algorithms.
211      * @param mechanismType the type of the XML processing mechanism and
212      *    representation. See the {@code TransformService} section in the
213      *    <a href=
214      *    "{@docRoot}/../specs/security/standard-names.html#xml-signature-xmlsignaturefactorykeyinfofactorytransformservice-mechanisms">
215      *    Java Security Standard Algorithm Names Specification</a> for a list of
216      *    standard mechanism types.
217      * @param provider the <code>Provider</code> object
218      * @return a new <code>TransformService</code>
219      * @throws NullPointerException if <code>provider</code>,
220      *   <code>algorithm</code>, or <code>mechanismType</code> is
221      *   <code>null</code>
222      * @throws NoSuchAlgorithmException if a <code>TransformService</code>
223      *   implementation for the specified algorithm and mechanism type is not
224      *   available from the specified <code>Provider</code> object
225      * @see Provider
226      */
getInstance(String algorithm, String mechanismType, Provider provider)227     public static TransformService getInstance
228         (String algorithm, String mechanismType, Provider provider)
229         throws NoSuchAlgorithmException {
230         if (mechanismType == null || algorithm == null || provider == null) {
231             throw new NullPointerException();
232         }
233 
234         boolean dom = false;
235         if (mechanismType.equals("DOM")) {
236             dom = true;
237         }
238         Service s = provider.getService("TransformService", algorithm);
239         if (s != null) {
240             String value = s.getAttribute("MechanismType");
241             if ((value == null && dom) ||
242                 (value != null && value.equals(mechanismType))) {
243                 Object obj = s.newInstance(null);
244                 if (obj instanceof TransformService) {
245                     TransformService ts = (TransformService) obj;
246                     ts.algorithm = algorithm;
247                     ts.mechanism = mechanismType;
248                     ts.provider = provider;
249                     return ts;
250                 }
251             }
252         }
253         throw new NoSuchAlgorithmException
254             (algorithm + " algorithm and " + mechanismType
255                  + " mechanism not available from " + provider.getName());
256     }
257 
258     /**
259      * Returns a <code>TransformService</code> that supports the specified
260      * algorithm URI (ex: {@link Transform#XPATH2}) and mechanism type
261      * (ex: DOM) as supplied by the specified provider. The specified provider
262      * must be registered in the security provider list.
263      *
264      * <p>Note that the list of registered providers may be retrieved via
265      * the {@link Security#getProviders() Security.getProviders()} method.
266      *
267      * @param algorithm the URI of the algorithm. See the
268      *    {@code TransformService} section in the
269      *    <a href=
270      *    "{@docRoot}/../specs/security/standard-names.html#xml-signature-transform-transformservice-algorithms">
271      *    Java Security Standard Algorithm Names Specification</a> for a list of
272      *    standard transform algorithms.
273      * @param mechanismType the type of the XML processing mechanism and
274      *    representation. See the {@code TransformService} section in the
275      *    <a href=
276      *    "{@docRoot}/../specs/security/standard-names.html#xml-signature-xmlsignaturefactorykeyinfofactorytransformservice-mechanisms">
277      *    Java Security Standard Algorithm Names Specification</a> for a list of
278      *    standard mechanism types.
279      * @param provider the string name of the provider
280      * @return a new <code>TransformService</code>
281      * @throws NoSuchProviderException if the specified provider is not
282      *   registered in the security provider list
283      * @throws NullPointerException if <code>provider</code>,
284      *   <code>mechanismType</code>, or <code>algorithm</code> is
285      *   <code>null</code>
286      * @throws NoSuchAlgorithmException if a <code>TransformService</code>
287      *   implementation for the specified algorithm and mechanism type is not
288      *   available from the specified provider
289      * @see Provider
290      */
getInstance(String algorithm, String mechanismType, String provider)291     public static TransformService getInstance
292         (String algorithm, String mechanismType, String provider)
293         throws NoSuchAlgorithmException, NoSuchProviderException {
294         if (mechanismType == null || algorithm == null || provider == null) {
295             throw new NullPointerException();
296         } else if (provider.length() == 0) {
297             throw new NoSuchProviderException();
298         }
299         boolean dom = false;
300         if (mechanismType.equals("DOM")) {
301             dom = true;
302         }
303         Provider p = Security.getProvider(provider);
304         if (p == null) {
305             throw new NoSuchProviderException("No such provider: " +
306                                               provider);
307         }
308         Service s = p.getService("TransformService", algorithm);
309         if (s != null) {
310             String value = s.getAttribute("MechanismType");
311             if ((value == null && dom) ||
312                 (value != null && value.equals(mechanismType))) {
313                 Object obj = s.newInstance(null);
314                 if (obj instanceof TransformService) {
315                     TransformService ts = (TransformService) obj;
316                     ts.algorithm = algorithm;
317                     ts.mechanism = mechanismType;
318                     ts.provider = p;
319                     return ts;
320                 }
321             }
322         }
323         throw new NoSuchAlgorithmException
324             (algorithm + " algorithm and " + mechanismType
325                  + " mechanism not available from " + provider);
326     }
327 
328     private static class MechanismMapEntry implements Map.Entry<String,String> {
329         private final String mechanism;
330         private final String algorithm;
331         private final String key;
MechanismMapEntry(String algorithm, String mechanism)332         MechanismMapEntry(String algorithm, String mechanism) {
333             this.algorithm = algorithm;
334             this.mechanism = mechanism;
335             this.key = "TransformService." + algorithm + " MechanismType";
336         }
equals(Object o)337         public boolean equals(Object o) {
338             if (!(o instanceof Map.Entry)) {
339                 return false;
340             }
341             Map.Entry<?,?> e = (Map.Entry<?,?>) o;
342             return (getKey()==null ?
343                     e.getKey()==null : getKey().equals(e.getKey())) &&
344                    (getValue()==null ?
345                     e.getValue()==null : getValue().equals(e.getValue()));
346         }
getKey()347         public String getKey() {
348             return key;
349         }
getValue()350         public String getValue() {
351             return mechanism;
352         }
setValue(String value)353         public String setValue(String value) {
354             throw new UnsupportedOperationException();
355         }
hashCode()356         public int hashCode() {
357             return (getKey()==null ? 0 : getKey().hashCode()) ^
358                    (getValue()==null ? 0 : getValue().hashCode());
359         }
360     }
361 
362     /**
363      * Returns the mechanism type supported by this <code>TransformService</code>.
364      *
365      * @return the mechanism type
366      */
getMechanismType()367     public final String getMechanismType() {
368         return mechanism;
369     }
370 
371     /**
372      * Returns the URI of the algorithm supported by this
373      * <code>TransformService</code>.
374      *
375      * @return the algorithm URI
376      */
getAlgorithm()377     public final String getAlgorithm() {
378         return algorithm;
379     }
380 
381     /**
382      * Returns the provider of this <code>TransformService</code>.
383      *
384      * @return the provider
385      */
getProvider()386     public final Provider getProvider() {
387         return provider;
388     }
389 
390     /**
391      * Initializes this <code>TransformService</code> with the specified
392      * parameters.
393      *
394      * <p>If the parameters exist in XML form, the
395      * {@link #init(XMLStructure, XMLCryptoContext)} method should be used to
396      * initialize the <code>TransformService</code>.
397      *
398      * @param params the algorithm parameters (may be <code>null</code> if
399      *   not required or optional)
400      * @throws InvalidAlgorithmParameterException if the specified parameters
401      *   are invalid for this algorithm
402      */
init(TransformParameterSpec params)403     public abstract void init(TransformParameterSpec params)
404         throws InvalidAlgorithmParameterException;
405 
406     /**
407      * Marshals the algorithm-specific parameters. If there are no parameters
408      * to be marshalled, this method returns without throwing an exception.
409      *
410      * @param parent a mechanism-specific structure containing the parent
411      *    node that the marshalled parameters should be appended to
412      * @param context the <code>XMLCryptoContext</code> containing
413      *    additional context (may be <code>null</code> if not applicable)
414      * @throws ClassCastException if the type of <code>parent</code> or
415      *    <code>context</code> is not compatible with this
416      *    <code>TransformService</code>
417      * @throws NullPointerException if <code>parent</code> is <code>null</code>
418      * @throws MarshalException if the parameters cannot be marshalled
419      */
marshalParams(XMLStructure parent, XMLCryptoContext context)420     public abstract void marshalParams
421         (XMLStructure parent, XMLCryptoContext context)
422         throws MarshalException;
423 
424     /**
425      * Initializes this <code>TransformService</code> with the specified
426      * parameters and document context.
427      *
428      * @param parent a mechanism-specific structure containing the parent
429      *    structure
430      * @param context the <code>XMLCryptoContext</code> containing
431      *    additional context (may be <code>null</code> if not applicable)
432      * @throws ClassCastException if the type of <code>parent</code> or
433      *    <code>context</code> is not compatible with this
434      *    <code>TransformService</code>
435      * @throws NullPointerException if <code>parent</code> is <code>null</code>
436      * @throws InvalidAlgorithmParameterException if the specified parameters
437      *   are invalid for this algorithm
438      */
init(XMLStructure parent, XMLCryptoContext context)439     public abstract void init(XMLStructure parent, XMLCryptoContext context)
440         throws InvalidAlgorithmParameterException;
441 }
442