Home | History | Annotate | Download | only in ec
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
      4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      5  *
      6  * This code is free software; you can redistribute it and/or modify it
      7  * under the terms of the GNU General Public License version 2 only, as
      8  * published by the Free Software Foundation.  Oracle designates this
      9  * particular file as subject to the "Classpath" exception as provided
     10  * by Oracle in the LICENSE file that accompanied this code.
     11  *
     12  * This code is distributed in the hope that it will be useful, but WITHOUT
     13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     15  * version 2 for more details (a copy is included in the LICENSE file that
     16  * accompanied this code).
     17  *
     18  * You should have received a copy of the GNU General Public License version
     19  * 2 along with this work; if not, write to the Free Software Foundation,
     20  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
     21  *
     22  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
     23  * or visit www.oracle.com if you need additional information or have any
     24  * questions.
     25  */
     26 
     27 package sun.security.ec;
     28 
     29 import java.security.*;
     30 import java.security.interfaces.*;
     31 import java.security.spec.*;
     32 
     33 /**
     34  * KeyFactory for EC keys. Keys must be instances of PublicKey or PrivateKey
     35  * and getAlgorithm() must return "EC". For such keys, it supports conversion
     36  * between the following:
     37  *
     38  * For public keys:
     39  *  . PublicKey with an X.509 encoding
     40  *  . ECPublicKey
     41  *  . ECPublicKeySpec
     42  *  . X509EncodedKeySpec
     43  *
     44  * For private keys:
     45  *  . PrivateKey with a PKCS#8 encoding
     46  *  . ECPrivateKey
     47  *  . ECPrivateKeySpec
     48  *  . PKCS8EncodedKeySpec
     49  *
     50  * @since   1.6
     51  * @author  Andreas Sterbenz
     52  */
     53 public final class ECKeyFactory extends KeyFactorySpi {
     54 
     55     // Used by translateKey() and the SunPKCS11 provider
     56     public final static KeyFactory INSTANCE;
     57 
     58     // Internal provider object we can obtain the KeyFactory and
     59     // AlgorithmParameters from. Used by ECParameters and AlgorithmId.
     60     // This can go away once we have EC always available in the SUN provider.
     61     // Used by ECParameters and AlgorithmId.
     62     public final static Provider ecInternalProvider;
     63 
     64     static {
     65         final Provider p = new Provider("SunEC-Internal", 1.0d, null) {};
     66         AccessController.doPrivileged(new PrivilegedAction<Void>() {
     67             public Void run() {
     68                 // Android changed : replace string reference with class name. It
     69                 // makes no difference in this case since these classes are already
     70                 // in direct use here.
     71                 p.put("KeyFactory.EC", ECKeyFactory.class.getName());
     72                 p.put("AlgorithmParameters.EC", ECParameters.class.getName());
     73                 p.put("Alg.Alias.AlgorithmParameters.1.2.840.10045.2.1", "EC");
     74                 return null;
     75             }
     76         });
     77         try {
     78             INSTANCE = KeyFactory.getInstance("EC", p);
     79         } catch (NoSuchAlgorithmException e) {
     80             throw new RuntimeException(e);
     81         }
     82         ecInternalProvider = p;
     83     }
     84 
     85     public ECKeyFactory() {
     86         // empty
     87     }
     88 
     89     /**
     90      * Static method to convert Key into a useable instance of
     91      * ECPublicKey or ECPrivateKey. Check the key and convert it
     92      * to a Sun key if necessary. If the key is not an EC key
     93      * or cannot be used, throw an InvalidKeyException.
     94      *
     95      * The difference between this method and engineTranslateKey() is that
     96      * we do not convert keys of other providers that are already an
     97      * instance of ECPublicKey or ECPrivateKey.
     98      *
     99      * To be used by future Java ECDSA and ECDH implementations.
    100      */
    101     public static ECKey toECKey(Key key) throws InvalidKeyException {
    102         if (key instanceof ECKey) {
    103             ECKey ecKey = (ECKey)key;
    104             checkKey(ecKey);
    105             return ecKey;
    106         } else {
    107             return (ECKey)INSTANCE.translateKey(key);
    108         }
    109     }
    110 
    111     /**
    112      * Check that the given EC key is valid.
    113      */
    114     private static void checkKey(ECKey key) throws InvalidKeyException {
    115         // check for subinterfaces, omit additional checks for our keys
    116         if (key instanceof ECPublicKey) {
    117             if (key instanceof ECPublicKeyImpl) {
    118                 return;
    119             }
    120         } else if (key instanceof ECPrivateKey) {
    121             if (key instanceof ECPrivateKeyImpl) {
    122                 return;
    123             }
    124         } else {
    125             throw new InvalidKeyException("Neither a public nor a private key");
    126         }
    127         // ECKey does not extend Key, so we need to do a cast
    128         String keyAlg = ((Key)key).getAlgorithm();
    129         if (keyAlg.equals("EC") == false) {
    130             throw new InvalidKeyException("Not an EC key: " + keyAlg);
    131         }
    132         // XXX further sanity checks about whether this key uses supported
    133         // fields, point formats, etc. would go here
    134     }
    135 
    136     /**
    137      * Translate an EC key into a Sun EC key. If conversion is
    138      * not possible, throw an InvalidKeyException.
    139      * See also JCA doc.
    140      */
    141     protected Key engineTranslateKey(Key key) throws InvalidKeyException {
    142         if (key == null) {
    143             throw new InvalidKeyException("Key must not be null");
    144         }
    145         String keyAlg = key.getAlgorithm();
    146         if (keyAlg.equals("EC") == false) {
    147             throw new InvalidKeyException("Not an EC key: " + keyAlg);
    148         }
    149         if (key instanceof PublicKey) {
    150             return implTranslatePublicKey((PublicKey)key);
    151         } else if (key instanceof PrivateKey) {
    152             return implTranslatePrivateKey((PrivateKey)key);
    153         } else {
    154             throw new InvalidKeyException("Neither a public nor a private key");
    155         }
    156     }
    157 
    158     // see JCA doc
    159     protected PublicKey engineGeneratePublic(KeySpec keySpec)
    160             throws InvalidKeySpecException {
    161         try {
    162             return implGeneratePublic(keySpec);
    163         } catch (InvalidKeySpecException e) {
    164             throw e;
    165         } catch (GeneralSecurityException e) {
    166             throw new InvalidKeySpecException(e);
    167         }
    168     }
    169 
    170     // see JCA doc
    171     protected PrivateKey engineGeneratePrivate(KeySpec keySpec)
    172             throws InvalidKeySpecException {
    173         try {
    174             return implGeneratePrivate(keySpec);
    175         } catch (InvalidKeySpecException e) {
    176             throw e;
    177         } catch (GeneralSecurityException e) {
    178             throw new InvalidKeySpecException(e);
    179         }
    180     }
    181 
    182     // internal implementation of translateKey() for public keys. See JCA doc
    183     private PublicKey implTranslatePublicKey(PublicKey key)
    184             throws InvalidKeyException {
    185         if (key instanceof ECPublicKey) {
    186             if (key instanceof ECPublicKeyImpl) {
    187                 return key;
    188             }
    189             ECPublicKey ecKey = (ECPublicKey)key;
    190             return new ECPublicKeyImpl(
    191                 ecKey.getW(),
    192                 ecKey.getParams()
    193             );
    194         } else if ("X.509".equals(key.getFormat())) {
    195             byte[] encoded = key.getEncoded();
    196             return new ECPublicKeyImpl(encoded);
    197         } else {
    198             throw new InvalidKeyException("Public keys must be instance "
    199                 + "of ECPublicKey or have X.509 encoding");
    200         }
    201     }
    202 
    203     // internal implementation of translateKey() for private keys. See JCA doc
    204     private PrivateKey implTranslatePrivateKey(PrivateKey key)
    205             throws InvalidKeyException {
    206         if (key instanceof ECPrivateKey) {
    207             if (key instanceof ECPrivateKeyImpl) {
    208                 return key;
    209             }
    210             ECPrivateKey ecKey = (ECPrivateKey)key;
    211             return new ECPrivateKeyImpl(
    212                 ecKey.getS(),
    213                 ecKey.getParams()
    214             );
    215         } else if ("PKCS#8".equals(key.getFormat())) {
    216             return new ECPrivateKeyImpl(key.getEncoded());
    217         } else {
    218             throw new InvalidKeyException("Private keys must be instance "
    219                 + "of ECPrivateKey or have PKCS#8 encoding");
    220         }
    221     }
    222 
    223     // internal implementation of generatePublic. See JCA doc
    224     private PublicKey implGeneratePublic(KeySpec keySpec)
    225             throws GeneralSecurityException {
    226         if (keySpec instanceof X509EncodedKeySpec) {
    227             X509EncodedKeySpec x509Spec = (X509EncodedKeySpec)keySpec;
    228             return new ECPublicKeyImpl(x509Spec.getEncoded());
    229         } else if (keySpec instanceof ECPublicKeySpec) {
    230             ECPublicKeySpec ecSpec = (ECPublicKeySpec)keySpec;
    231             return new ECPublicKeyImpl(
    232                 ecSpec.getW(),
    233                 ecSpec.getParams()
    234             );
    235         } else {
    236             throw new InvalidKeySpecException("Only ECPublicKeySpec "
    237                 + "and X509EncodedKeySpec supported for EC public keys");
    238         }
    239     }
    240 
    241     // internal implementation of generatePrivate. See JCA doc
    242     private PrivateKey implGeneratePrivate(KeySpec keySpec)
    243             throws GeneralSecurityException {
    244         if (keySpec instanceof PKCS8EncodedKeySpec) {
    245             PKCS8EncodedKeySpec pkcsSpec = (PKCS8EncodedKeySpec)keySpec;
    246             return new ECPrivateKeyImpl(pkcsSpec.getEncoded());
    247         } else if (keySpec instanceof ECPrivateKeySpec) {
    248             ECPrivateKeySpec ecSpec = (ECPrivateKeySpec)keySpec;
    249             return new ECPrivateKeyImpl(ecSpec.getS(), ecSpec.getParams());
    250         } else {
    251             throw new InvalidKeySpecException("Only ECPrivateKeySpec "
    252                 + "and PKCS8EncodedKeySpec supported for EC private keys");
    253         }
    254     }
    255 
    256     protected <T extends KeySpec> T engineGetKeySpec(Key key, Class<T> keySpec)
    257             throws InvalidKeySpecException {
    258         try {
    259             // convert key to one of our keys
    260             // this also verifies that the key is a valid EC key and ensures
    261             // that the encoding is X.509/PKCS#8 for public/private keys
    262             key = engineTranslateKey(key);
    263         } catch (InvalidKeyException e) {
    264             throw new InvalidKeySpecException(e);
    265         }
    266         if (key instanceof ECPublicKey) {
    267             ECPublicKey ecKey = (ECPublicKey)key;
    268             if (ECPublicKeySpec.class.isAssignableFrom(keySpec)) {
    269                 return (T) new ECPublicKeySpec(
    270                     ecKey.getW(),
    271                     ecKey.getParams()
    272                 );
    273             } else if (X509EncodedKeySpec.class.isAssignableFrom(keySpec)) {
    274                 return (T) new X509EncodedKeySpec(key.getEncoded());
    275             } else {
    276                 throw new InvalidKeySpecException
    277                         ("KeySpec must be ECPublicKeySpec or "
    278                         + "X509EncodedKeySpec for EC public keys");
    279             }
    280         } else if (key instanceof ECPrivateKey) {
    281             if (PKCS8EncodedKeySpec.class.isAssignableFrom(keySpec)) {
    282                 return (T) new PKCS8EncodedKeySpec(key.getEncoded());
    283             } else if (ECPrivateKeySpec.class.isAssignableFrom(keySpec)) {
    284                 ECPrivateKey ecKey = (ECPrivateKey)key;
    285                 return (T) new ECPrivateKeySpec(
    286                     ecKey.getS(),
    287                     ecKey.getParams()
    288                 );
    289             } else {
    290                 throw new InvalidKeySpecException
    291                         ("KeySpec must be ECPrivateKeySpec or "
    292                         + "PKCS8EncodedKeySpec for EC private keys");
    293             }
    294         } else {
    295             // should not occur, caught in engineTranslateKey()
    296             throw new InvalidKeySpecException("Neither public nor private key");
    297         }
    298     }
    299 }
    300