1 /*
   2  * Copyright (c) 1997, 2011, 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 package sun.security.provider;
  27 
  28 import java.security.Key;
  29 import java.security.PublicKey;
  30 import java.security.PrivateKey;
  31 import java.security.KeyFactorySpi;
  32 import java.security.InvalidKeyException;
  33 import java.security.AccessController;
  34 import java.security.interfaces.DSAParams;
  35 import java.security.spec.DSAPublicKeySpec;
  36 import java.security.spec.DSAPrivateKeySpec;
  37 import java.security.spec.KeySpec;
  38 import java.security.spec.InvalidKeySpecException;
  39 import java.security.spec.X509EncodedKeySpec;
  40 import java.security.spec.PKCS8EncodedKeySpec;
  41 
  42 import sun.security.action.GetPropertyAction;
  43 
  44 /**
  45  * This class implements the DSA key factory of the Sun provider.
  46  *
  47  * @author Jan Luehe
  48  *
  49  *
  50  * @since 1.2
  51  */
  52 
  53 public class DSAKeyFactory extends KeyFactorySpi {
  54 
  55     // package private for DSAKeyPairGenerator
  56     static final boolean SERIAL_INTEROP;
  57     private static final String SERIAL_PROP = "sun.security.key.serial.interop";
  58 
  59     static {
  60 
  61         /**
  62          * Check to see if we need to maintain interoperability for serialized
  63          * keys between JDK 5.0 -> JDK 1.4.  In other words, determine whether
  64          * a key object serialized in JDK 5.0 must be deserializable in
  65          * JDK 1.4.
  66          *
  67          * If true, then we generate sun.security.provider.DSAPublicKey.
  68          * If false, then we generate sun.security.provider.DSAPublicKeyImpl.
  69          *
  70          * By default this is false.
  71          * This incompatibility was introduced by 4532506.
  72          */
  73         String prop = AccessController.doPrivileged
  74                 (new GetPropertyAction(SERIAL_PROP, null));
  75         SERIAL_INTEROP = "true".equalsIgnoreCase(prop);
  76     }
  77 
  78     /**
  79      * Generates a public key object from the provided key specification
  80      * (key material).
  81      *
  82      * @param keySpec the specification (key material) of the public key
  83      *
  84      * @return the public key
  85      *
  86      * @exception InvalidKeySpecException if the given key specification
  87      * is inappropriate for this key factory to produce a public key.
  88      */
  89     protected PublicKey engineGeneratePublic(KeySpec keySpec)
  90     throws InvalidKeySpecException {
  91         try {
  92             if (keySpec instanceof DSAPublicKeySpec) {
  93                 DSAPublicKeySpec dsaPubKeySpec = (DSAPublicKeySpec)keySpec;
  94                 if (SERIAL_INTEROP) {
  95                     return new DSAPublicKey(dsaPubKeySpec.getY(),
  96                                         dsaPubKeySpec.getP(),
  97                                         dsaPubKeySpec.getQ(),
  98                                         dsaPubKeySpec.getG());
  99                 } else {
 100                     return new DSAPublicKeyImpl(dsaPubKeySpec.getY(),
 101                                         dsaPubKeySpec.getP(),
 102                                         dsaPubKeySpec.getQ(),
 103                                         dsaPubKeySpec.getG());
 104                 }
 105             } else if (keySpec instanceof X509EncodedKeySpec) {
 106                 if (SERIAL_INTEROP) {
 107                     return new DSAPublicKey
 108                         (((X509EncodedKeySpec)keySpec).getEncoded());
 109                 } else {
 110                     return new DSAPublicKeyImpl
 111                         (((X509EncodedKeySpec)keySpec).getEncoded());
 112                 }
 113             } else {
 114                 throw new InvalidKeySpecException
 115                     ("Inappropriate key specification");
 116             }
 117         } catch (InvalidKeyException e) {
 118             throw new InvalidKeySpecException
 119                 ("Inappropriate key specification: " + e.getMessage());
 120         }
 121     }
 122 
 123     /**
 124      * Generates a private key object from the provided key specification
 125      * (key material).
 126      *
 127      * @param keySpec the specification (key material) of the private key
 128      *
 129      * @return the private key
 130      *
 131      * @exception InvalidKeySpecException if the given key specification
 132      * is inappropriate for this key factory to produce a private key.
 133      */
 134     protected PrivateKey engineGeneratePrivate(KeySpec keySpec)
 135     throws InvalidKeySpecException {
 136         try {
 137             if (keySpec instanceof DSAPrivateKeySpec) {
 138                 DSAPrivateKeySpec dsaPrivKeySpec = (DSAPrivateKeySpec)keySpec;
 139                 return new DSAPrivateKey(dsaPrivKeySpec.getX(),
 140                                          dsaPrivKeySpec.getP(),
 141                                          dsaPrivKeySpec.getQ(),
 142                                          dsaPrivKeySpec.getG());
 143 
 144             } else if (keySpec instanceof PKCS8EncodedKeySpec) {
 145                 return new DSAPrivateKey
 146                     (((PKCS8EncodedKeySpec)keySpec).getEncoded());
 147 
 148             } else {
 149                 throw new InvalidKeySpecException
 150                     ("Inappropriate key specification");
 151             }
 152         } catch (InvalidKeyException e) {
 153             throw new InvalidKeySpecException
 154                 ("Inappropriate key specification: " + e.getMessage());
 155         }
 156     }
 157 
 158     /**
 159      * Returns a specification (key material) of the given key object
 160      * in the requested format.
 161      *
 162      * @param key the key
 163      *
 164      * @param keySpec the requested format in which the key material shall be
 165      * returned
 166      *
 167      * @return the underlying key specification (key material) in the
 168      * requested format
 169      *
 170      * @exception InvalidKeySpecException if the requested key specification is
 171      * inappropriate for the given key, or the given key cannot be processed
 172      * (e.g., the given key has an unrecognized algorithm or format).
 173      */
 174     protected <T extends KeySpec>
 175         T engineGetKeySpec(Key key, Class<T> keySpec)
 176     throws InvalidKeySpecException {
 177 
 178         DSAParams params;
 179 
 180         try {
 181 
 182             if (key instanceof java.security.interfaces.DSAPublicKey) {
 183 
 184                 // Determine valid key specs
 185                 Class<?> dsaPubKeySpec = Class.forName
 186                     ("java.security.spec.DSAPublicKeySpec");
 187                 Class<?> x509KeySpec = Class.forName
 188                     ("java.security.spec.X509EncodedKeySpec");
 189 
 190                 if (dsaPubKeySpec.isAssignableFrom(keySpec)) {
 191                     java.security.interfaces.DSAPublicKey dsaPubKey
 192                         = (java.security.interfaces.DSAPublicKey)key;
 193                     params = dsaPubKey.getParams();
 194                     return keySpec.cast(new DSAPublicKeySpec(dsaPubKey.getY(),
 195                                                              params.getP(),
 196                                                              params.getQ(),
 197                                                              params.getG()));
 198 
 199                 } else if (x509KeySpec.isAssignableFrom(keySpec)) {
 200                     return keySpec.cast(new X509EncodedKeySpec(key.getEncoded()));
 201 
 202                 } else {
 203                     throw new InvalidKeySpecException
 204                         ("Inappropriate key specification");
 205                 }
 206 
 207             } else if (key instanceof java.security.interfaces.DSAPrivateKey) {
 208 
 209                 // Determine valid key specs
 210                 Class<?> dsaPrivKeySpec = Class.forName
 211                     ("java.security.spec.DSAPrivateKeySpec");
 212                 Class<?> pkcs8KeySpec = Class.forName
 213                     ("java.security.spec.PKCS8EncodedKeySpec");
 214 
 215                 if (dsaPrivKeySpec.isAssignableFrom(keySpec)) {
 216                     java.security.interfaces.DSAPrivateKey dsaPrivKey
 217                         = (java.security.interfaces.DSAPrivateKey)key;
 218                     params = dsaPrivKey.getParams();
 219                     return keySpec.cast(new DSAPrivateKeySpec(dsaPrivKey.getX(),
 220                                                               params.getP(),
 221                                                               params.getQ(),
 222                                                               params.getG()));
 223 
 224                 } else if (pkcs8KeySpec.isAssignableFrom(keySpec)) {
 225                     return keySpec.cast(new PKCS8EncodedKeySpec(key.getEncoded()));
 226 
 227                 } else {
 228                     throw new InvalidKeySpecException
 229                         ("Inappropriate key specification");
 230                 }
 231 
 232             } else {
 233                 throw new InvalidKeySpecException("Inappropriate key type");
 234             }
 235 
 236         } catch (ClassNotFoundException e) {
 237             throw new InvalidKeySpecException
 238                 ("Unsupported key specification: " + e.getMessage());
 239         }
 240     }
 241 
 242     /**
 243      * Translates a key object, whose provider may be unknown or potentially
 244      * untrusted, into a corresponding key object of this key factory.
 245      *
 246      * @param key the key whose provider is unknown or untrusted
 247      *
 248      * @return the translated key
 249      *
 250      * @exception InvalidKeyException if the given key cannot be processed by
 251      * this key factory.
 252      */
 253     protected Key engineTranslateKey(Key key) throws InvalidKeyException {
 254 
 255         try {
 256 
 257             if (key instanceof java.security.interfaces.DSAPublicKey) {
 258                 // Check if key originates from this factory
 259                 if (key instanceof sun.security.provider.DSAPublicKey) {
 260                     return key;
 261                 }
 262                 // Convert key to spec
 263                 DSAPublicKeySpec dsaPubKeySpec
 264                     = engineGetKeySpec(key, DSAPublicKeySpec.class);
 265                 // Create key from spec, and return it
 266                 return engineGeneratePublic(dsaPubKeySpec);
 267 
 268             } else if (key instanceof java.security.interfaces.DSAPrivateKey) {
 269                 // Check if key originates from this factory
 270                 if (key instanceof sun.security.provider.DSAPrivateKey) {
 271                     return key;
 272                 }
 273                 // Convert key to spec
 274                 DSAPrivateKeySpec dsaPrivKeySpec
 275                     = engineGetKeySpec(key, DSAPrivateKeySpec.class);
 276                 // Create key from spec, and return it
 277                 return engineGeneratePrivate(dsaPrivKeySpec);
 278 
 279             } else {
 280                 throw new InvalidKeyException("Wrong algorithm type");
 281             }
 282 
 283         } catch (InvalidKeySpecException e) {
 284             throw new InvalidKeyException("Cannot translate key: "
 285                                           + e.getMessage());
 286         }
 287     }
 288 }