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 = GetPropertyAction.getProperty(SERIAL_PROP);
  74         SERIAL_INTEROP = "true".equalsIgnoreCase(prop);
  75     }
  76 
  77     /**
  78      * Generates a public key object from the provided key specification
  79      * (key material).
  80      *
  81      * @param keySpec the specification (key material) of the public key
  82      *
  83      * @return the public key
  84      *
  85      * @exception InvalidKeySpecException if the given key specification
  86      * is inappropriate for this key factory to produce a public key.
  87      */
  88     protected PublicKey engineGeneratePublic(KeySpec keySpec)
  89     throws InvalidKeySpecException {
  90         try {
  91             if (keySpec instanceof DSAPublicKeySpec) {
  92                 DSAPublicKeySpec dsaPubKeySpec = (DSAPublicKeySpec)keySpec;
  93                 if (SERIAL_INTEROP) {
  94                     return new DSAPublicKey(dsaPubKeySpec.getY(),
  95                                         dsaPubKeySpec.getP(),
  96                                         dsaPubKeySpec.getQ(),
  97                                         dsaPubKeySpec.getG());
  98                 } else {
  99                     return new DSAPublicKeyImpl(dsaPubKeySpec.getY(),
 100                                         dsaPubKeySpec.getP(),
 101                                         dsaPubKeySpec.getQ(),
 102                                         dsaPubKeySpec.getG());
 103                 }
 104             } else if (keySpec instanceof X509EncodedKeySpec) {
 105                 if (SERIAL_INTEROP) {
 106                     return new DSAPublicKey
 107                         (((X509EncodedKeySpec)keySpec).getEncoded());
 108                 } else {
 109                     return new DSAPublicKeyImpl
 110                         (((X509EncodedKeySpec)keySpec).getEncoded());
 111                 }
 112             } else {
 113                 throw new InvalidKeySpecException
 114                     ("Inappropriate key specification");
 115             }
 116         } catch (InvalidKeyException e) {
 117             throw new InvalidKeySpecException
 118                 ("Inappropriate key specification: " + e.getMessage());
 119         }
 120     }
 121 
 122     /**
 123      * Generates a private key object from the provided key specification
 124      * (key material).
 125      *
 126      * @param keySpec the specification (key material) of the private key
 127      *
 128      * @return the private key
 129      *
 130      * @exception InvalidKeySpecException if the given key specification
 131      * is inappropriate for this key factory to produce a private key.
 132      */
 133     protected PrivateKey engineGeneratePrivate(KeySpec keySpec)
 134     throws InvalidKeySpecException {
 135         try {
 136             if (keySpec instanceof DSAPrivateKeySpec) {
 137                 DSAPrivateKeySpec dsaPrivKeySpec = (DSAPrivateKeySpec)keySpec;
 138                 return new DSAPrivateKey(dsaPrivKeySpec.getX(),
 139                                          dsaPrivKeySpec.getP(),
 140                                          dsaPrivKeySpec.getQ(),
 141                                          dsaPrivKeySpec.getG());
 142 
 143             } else if (keySpec instanceof PKCS8EncodedKeySpec) {
 144                 return new DSAPrivateKey
 145                     (((PKCS8EncodedKeySpec)keySpec).getEncoded());
 146 
 147             } else {
 148                 throw new InvalidKeySpecException
 149                     ("Inappropriate key specification");
 150             }
 151         } catch (InvalidKeyException e) {
 152             throw new InvalidKeySpecException
 153                 ("Inappropriate key specification: " + e.getMessage());
 154         }
 155     }
 156 
 157     /**
 158      * Returns a specification (key material) of the given key object
 159      * in the requested format.
 160      *
 161      * @param key the key
 162      *
 163      * @param keySpec the requested format in which the key material shall be
 164      * returned
 165      *
 166      * @return the underlying key specification (key material) in the
 167      * requested format
 168      *
 169      * @exception InvalidKeySpecException if the requested key specification is
 170      * inappropriate for the given key, or the given key cannot be processed
 171      * (e.g., the given key has an unrecognized algorithm or format).
 172      */
 173     protected <T extends KeySpec>
 174         T engineGetKeySpec(Key key, Class<T> keySpec)
 175     throws InvalidKeySpecException {
 176 
 177         DSAParams params;
 178 
 179         try {
 180 
 181             if (key instanceof java.security.interfaces.DSAPublicKey) {
 182 
 183                 // Determine valid key specs
 184                 Class<?> dsaPubKeySpec = Class.forName
 185                     ("java.security.spec.DSAPublicKeySpec");
 186                 Class<?> x509KeySpec = Class.forName
 187                     ("java.security.spec.X509EncodedKeySpec");
 188 
 189                 if (dsaPubKeySpec.isAssignableFrom(keySpec)) {
 190                     java.security.interfaces.DSAPublicKey dsaPubKey
 191                         = (java.security.interfaces.DSAPublicKey)key;
 192                     params = dsaPubKey.getParams();
 193                     return keySpec.cast(new DSAPublicKeySpec(dsaPubKey.getY(),
 194                                                              params.getP(),
 195                                                              params.getQ(),
 196                                                              params.getG()));
 197 
 198                 } else if (x509KeySpec.isAssignableFrom(keySpec)) {
 199                     return keySpec.cast(new X509EncodedKeySpec(key.getEncoded()));
 200 
 201                 } else {
 202                     throw new InvalidKeySpecException
 203                         ("Inappropriate key specification");
 204                 }
 205 
 206             } else if (key instanceof java.security.interfaces.DSAPrivateKey) {
 207 
 208                 // Determine valid key specs
 209                 Class<?> dsaPrivKeySpec = Class.forName
 210                     ("java.security.spec.DSAPrivateKeySpec");
 211                 Class<?> pkcs8KeySpec = Class.forName
 212                     ("java.security.spec.PKCS8EncodedKeySpec");
 213 
 214                 if (dsaPrivKeySpec.isAssignableFrom(keySpec)) {
 215                     java.security.interfaces.DSAPrivateKey dsaPrivKey
 216                         = (java.security.interfaces.DSAPrivateKey)key;
 217                     params = dsaPrivKey.getParams();
 218                     return keySpec.cast(new DSAPrivateKeySpec(dsaPrivKey.getX(),
 219                                                               params.getP(),
 220                                                               params.getQ(),
 221                                                               params.getG()));
 222 
 223                 } else if (pkcs8KeySpec.isAssignableFrom(keySpec)) {
 224                     return keySpec.cast(new PKCS8EncodedKeySpec(key.getEncoded()));
 225 
 226                 } else {
 227                     throw new InvalidKeySpecException
 228                         ("Inappropriate key specification");
 229                 }
 230 
 231             } else {
 232                 throw new InvalidKeySpecException("Inappropriate key type");
 233             }
 234 
 235         } catch (ClassNotFoundException e) {
 236             throw new InvalidKeySpecException
 237                 ("Unsupported key specification: " + e.getMessage());
 238         }
 239     }
 240 
 241     /**
 242      * Translates a key object, whose provider may be unknown or potentially
 243      * untrusted, into a corresponding key object of this key factory.
 244      *
 245      * @param key the key whose provider is unknown or untrusted
 246      *
 247      * @return the translated key
 248      *
 249      * @exception InvalidKeyException if the given key cannot be processed by
 250      * this key factory.
 251      */
 252     protected Key engineTranslateKey(Key key) throws InvalidKeyException {
 253 
 254         try {
 255 
 256             if (key instanceof java.security.interfaces.DSAPublicKey) {
 257                 // Check if key originates from this factory
 258                 if (key instanceof sun.security.provider.DSAPublicKey) {
 259                     return key;
 260                 }
 261                 // Convert key to spec
 262                 DSAPublicKeySpec dsaPubKeySpec
 263                     = engineGetKeySpec(key, DSAPublicKeySpec.class);
 264                 // Create key from spec, and return it
 265                 return engineGeneratePublic(dsaPubKeySpec);
 266 
 267             } else if (key instanceof java.security.interfaces.DSAPrivateKey) {
 268                 // Check if key originates from this factory
 269                 if (key instanceof sun.security.provider.DSAPrivateKey) {
 270                     return key;
 271                 }
 272                 // Convert key to spec
 273                 DSAPrivateKeySpec dsaPrivKeySpec
 274                     = engineGetKeySpec(key, DSAPrivateKeySpec.class);
 275                 // Create key from spec, and return it
 276                 return engineGeneratePrivate(dsaPrivKeySpec);
 277 
 278             } else {
 279                 throw new InvalidKeyException("Wrong algorithm type");
 280             }
 281 
 282         } catch (InvalidKeySpecException e) {
 283             throw new InvalidKeyException("Cannot translate key: "
 284                                           + e.getMessage());
 285         }
 286     }
 287 }