001    /* KeyAgreement.java -- Engine for key agreement methods.
002       Copyright (C) 2004  Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package javax.crypto;
040    
041    import gnu.java.security.Engine;
042    
043    import java.lang.reflect.InvocationTargetException;
044    import java.security.InvalidAlgorithmParameterException;
045    import java.security.InvalidKeyException;
046    import java.security.Key;
047    import java.security.NoSuchAlgorithmException;
048    import java.security.NoSuchProviderException;
049    import java.security.Provider;
050    import java.security.SecureRandom;
051    import java.security.Security;
052    import java.security.spec.AlgorithmParameterSpec;
053    
054    /**
055     * Key agreement is a method in which two or more parties may agree on a
056     * secret key for symmetric cryptography or message authentication
057     * without transmitting any secrets in the clear. Key agreement
058     * algorithms typically use a public/private <i>key pair</i>, and the
059     * public key (along with some additional information) is sent across
060     * untrusted networks.
061     *
062     * <p>The most common form of key agreement used today is the
063     * <i>Diffie-Hellman key exchange algorithm</i>, described in <a
064     * href="http://www.rsasecurity.com/rsalabs/pkcs/pkcs-3/">PKCS #3 -
065     * Diffie Hellman Key Agreement Standard</a>.
066     *
067     * @author Casey Marshall (csm@gnu.org)
068     * @since 1.4
069     * @see KeyGenerator
070     * @see SecretKey
071     */
072    public class KeyAgreement
073    {
074    
075      // Fields.
076      // ------------------------------------------------------------------------
077    
078      private static final String SERVICE = "KeyAgreement";
079    
080      /** The underlying key agreement implementation. */
081      private KeyAgreementSpi kaSpi;
082    
083      /** The provider of this implementation. */
084      private Provider provider;
085    
086      /** The name of this instance's algorithm. */
087      private String algorithm;
088    
089      /** Singnals whether or not this instance has been initialized. */
090      private boolean virgin;
091    
092      // Constructor.
093      // ------------------------------------------------------------------------
094    
095      protected KeyAgreement(KeyAgreementSpi kaSpi, Provider provider,
096                             String algorithm)
097      {
098        this.kaSpi = kaSpi;
099        this.provider = provider;
100        this.algorithm = algorithm;
101        virgin = true;
102      }
103    
104      /**
105       * Get an implementation of an algorithm from the first provider that
106       * implements it.
107       *
108       * @param algorithm The name of the algorithm to get.
109       * @return The proper KeyAgreement instacne, if found.
110       * @throws NoSuchAlgorithmException If the specified algorithm is not
111       *           implemented by any installed provider.
112       * @throws IllegalArgumentException if <code>algorithm</code> is
113       *           <code>null</code> or is an empty string.
114       */
115      public static final KeyAgreement getInstance(String algorithm)
116          throws NoSuchAlgorithmException
117      {
118        Provider[] p = Security.getProviders();
119        NoSuchAlgorithmException lastException = null;
120        for (int i = 0; i < p.length; i++)
121          try
122            {
123              return getInstance(algorithm, p[i]);
124            }
125          catch (NoSuchAlgorithmException x)
126            {
127              lastException = x;
128            }
129        if (lastException != null)
130          throw lastException;
131        throw new NoSuchAlgorithmException(algorithm);
132      }
133    
134      /**
135       * Return an implementation of an algorithm from a named provider.
136       *
137       * @param algorithm The name of the algorithm to create.
138       * @param provider The name of the provider from which to get the
139       *          implementation.
140       * @return The proper KeyAgreement instance, if found.
141       * @throws NoSuchAlgorithmException If the named provider does not implement
142       *           the algorithm.
143       * @throws NoSuchProviderException If the named provider does not exist.
144       * @throws IllegalArgumentException if either <code>algorithm</code> or
145       *           <code>provider</code> is <code>null</code>, or if
146       *           <code>algorithm</code> is an empty string.
147       */
148      public static final KeyAgreement getInstance(String algorithm, String provider)
149          throws NoSuchAlgorithmException, NoSuchProviderException
150      {
151        if (provider == null)
152          throw new IllegalArgumentException("provider MUST NOT be null");
153        Provider p = Security.getProvider(provider);
154        if (p == null)
155          throw new NoSuchProviderException(provider);
156        return getInstance(algorithm, p);
157      }
158    
159      /**
160       * Return an implementation of an algorithm from a specific provider.
161       *
162       * @param algorithm The name of the algorithm to get.
163       * @param provider The provider from which to get the implementation.
164       * @return The proper KeyAgreement instance, if found.
165       * @throws NoSuchAlgorithmException If this provider does not implement the
166       *           algorithm.
167       * @throws IllegalArgumentException if either <code>algorithm</code> or
168       *           <code>provider</code> is <code>null</code>, or if
169       *           <code>algorithm</code> is an empty string.
170       */
171      public static final KeyAgreement getInstance(String algorithm,
172                                                   Provider provider)
173        throws NoSuchAlgorithmException
174      {
175        StringBuilder sb = new StringBuilder("KeyAgreement algorithm [")
176            .append(algorithm).append("] from provider[")
177            .append(provider).append("] could not be created");
178        Throwable cause;
179        try
180          {
181            Object spi = Engine.getInstance(SERVICE, algorithm, provider);
182            return new KeyAgreement((KeyAgreementSpi) spi, provider, algorithm);
183          }
184        catch (InvocationTargetException x)
185          {
186            cause = x.getCause();
187            if (cause instanceof NoSuchAlgorithmException)
188              throw (NoSuchAlgorithmException) cause;
189            if (cause == null)
190              cause = x;
191          }
192        catch (ClassCastException x)
193          {
194            cause = x;
195          }
196        NoSuchAlgorithmException x = new NoSuchAlgorithmException(sb.toString());
197        x.initCause(cause);
198        throw x;
199      }
200    
201      /**
202       * Do a phase in the key agreement. The number of times this method is
203       * called depends upon the algorithm and the number of parties
204       * involved, but must be called at least once with the
205       * <code>lastPhase</code> flag set to <code>true</code>.
206       *
207       * @param key       The key for this phase.
208       * @param lastPhase Should be <code>true</code> if this will be the
209       *        last phase before generating the shared secret.
210       * @return The intermediate result, or <code>null</code> if there is
211       *         no intermediate result.
212       * @throws java.lang.IllegalStateException If this instance has not
213       *         been initialized.
214       * @throws java.security.InvalidKeyException If the key is
215       *         inappropriate for this algorithm.
216       */
217      public final Key doPhase(Key key, boolean lastPhase)
218        throws IllegalStateException, InvalidKeyException
219      {
220        if (virgin)
221          {
222            throw new IllegalStateException("not initialized");
223          }
224        return kaSpi.engineDoPhase(key, lastPhase);
225      }
226    
227      /**
228       * Generate the shared secret in a new byte array.
229       *
230       * @return The shared secret.
231       * @throws java.lang.IllegalStateException If this instnace has not
232       *         been initialized, or if not enough calls to
233       *         <code>doPhase</code> have been made.
234       */
235      public final byte[] generateSecret() throws IllegalStateException
236      {
237        if (virgin)
238          {
239            throw new IllegalStateException("not initialized");
240          }
241        return kaSpi.engineGenerateSecret();
242      }
243    
244      /**
245       * Generate the shared secret and store it into the supplied array.
246       *
247       * @param sharedSecret The array in which to store the secret.
248       * @param offset       The index in <code>sharedSecret</code> to start
249       *                     storing data.
250       * @return The length of the shared secret, in bytes.
251       * @throws java.lang.IllegalStateException If this instnace has not
252       *         been initialized, or if not enough calls to
253       *         <code>doPhase</code> have been made.
254       * @throws javax.crypto.ShortBufferException If the supplied array is
255       *         not large enough to store the result.
256       */
257      public final int generateSecret(byte[] sharedSecret, int offset)
258      throws IllegalStateException, ShortBufferException
259      {
260        if (virgin)
261          {
262            throw new IllegalStateException("not initialized");
263          }
264        return kaSpi.engineGenerateSecret(sharedSecret, offset);
265      }
266    
267      /**
268       * Generate the shared secret and return it as an appropriate {@link
269       * SecretKey}.
270       *
271       * @param algorithm The secret key's algorithm.
272       * @return The shared secret as a secret key.
273       * @throws java.lang.IllegalStateException If this instnace has not
274       *         been initialized, or if not enough calls to
275       *         <code>doPhase</code> have been made.
276       * @throws java.security.InvalidKeyException If the shared secret
277       *         cannot be used to make a {@link SecretKey}.
278       * @throws java.security.NoSuchAlgorithmException If the specified
279       *         algorithm does not exist.
280       */
281      public final SecretKey generateSecret(String algorithm)
282      throws IllegalStateException, InvalidKeyException, NoSuchAlgorithmException
283      {
284        if (virgin)
285          {
286            throw new IllegalStateException("not initialized");
287          }
288        return kaSpi.engineGenerateSecret(algorithm);
289      }
290    
291      /**
292       * Return the name of this key-agreement algorithm.
293       *
294       * @return The algorithm name.
295       */
296      public final String getAlgorithm()
297      {
298        return algorithm;
299      }
300    
301      /**
302       * Return the provider of the underlying implementation.
303       *
304       * @return The provider.
305       */
306      public final Provider getProvider()
307      {
308        return provider;
309      }
310    
311      /**
312       * Initialize this key agreement with a key. This method will use the
313       * highest-priority {@link java.security.SecureRandom} as its source
314       * of randomness.
315       *
316       * @param key The key, usually the user's private key.
317       * @throws java.security.InvalidKeyException If the supplied key is
318       *         not appropriate.
319       */
320      public final void init(Key key) throws InvalidKeyException
321      {
322        init(key, new SecureRandom());
323      }
324    
325      /**
326       * Initialize this key agreement with a key and a source of
327       * randomness.
328       *
329       * @param key    The key, usually the user's private key.
330       * @param random The source of randomness.
331       * @throws java.security.InvalidKeyException If the supplied key is
332       *         not appropriate.
333       */
334      public final void init(Key key, SecureRandom random)
335        throws InvalidKeyException
336      {
337        kaSpi.engineInit(key, random);
338        virgin = false; // w00t!
339      }
340    
341      /**
342       * Initialize this key agreement with a key and parameters. This
343       * method will use the highest-priority {@link
344       * java.security.SecureRandom} as its source of randomness.
345       *
346       * @param key    The key, usually the user's private key.
347       * @param params The algorithm parameters.
348       * @throws java.security.InvalidAlgorithmParameterException If the
349       *         supplied parameters are not appropriate.
350       * @throws java.security.InvalidKeyException If the supplied key is
351       *         not appropriate.
352       */
353      public final void init(Key key, AlgorithmParameterSpec params)
354        throws InvalidAlgorithmParameterException, InvalidKeyException
355      {
356        init(key, params, new SecureRandom());
357      }
358    
359      /**
360       * Initialize this key agreement with a key, parameters, and source of
361       * randomness.
362       *
363       * @param key    The key, usually the user's private key.
364       * @param params The algorithm parameters.
365       * @param random The source of randomness.
366       * @throws java.security.InvalidAlgorithmParameterException If the
367       *         supplied parameters are not appropriate.
368       * @throws java.security.InvalidKeyException If the supplied key is
369       *         not appropriate.
370       */
371      public final void init(Key key, AlgorithmParameterSpec params,
372                             SecureRandom random)
373        throws InvalidAlgorithmParameterException, InvalidKeyException
374      {
375        kaSpi.engineInit(key, params, random);
376        virgin = false; // w00t!
377      }
378    }