1 /*
   2  * Copyright (c) 2006, 2013, 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.pkcs11;
  27 
  28 import java.io.IOException;
  29 import java.math.BigInteger;
  30 
  31 import java.security.*;
  32 import java.security.interfaces.*;
  33 import java.security.spec.*;
  34 
  35 import static sun.security.pkcs11.TemplateManager.*;
  36 import sun.security.pkcs11.wrapper.*;
  37 import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
  38 
  39 import sun.security.util.DerValue;
  40 import sun.security.util.ECUtil;
  41 
  42 /**
  43  * EC KeyFactory implementation.
  44  *
  45  * @author  Andreas Sterbenz
  46  * @since   1.6
  47  */
  48 final class P11ECKeyFactory extends P11KeyFactory {
  49     private static Provider sunECprovider;
  50 
  51     private static Provider getSunECProvider() {
  52         if (sunECprovider == null) {
  53             sunECprovider = Security.getProvider("SunEC");
  54             if (sunECprovider == null) {
  55                 throw new RuntimeException("Cannot load SunEC provider");
  56             }
  57         }
  58 
  59         return sunECprovider;
  60     }
  61 
  62     P11ECKeyFactory(Token token, String algorithm) {
  63         super(token, algorithm);
  64     }
  65 
  66     static ECParameterSpec getECParameterSpec(String name) {
  67         return ECUtil.getECParameterSpec(getSunECProvider(), name);
  68     }
  69 
  70     static ECParameterSpec getECParameterSpec(int keySize) {
  71         return ECUtil.getECParameterSpec(getSunECProvider(), keySize);
  72     }
  73 
  74     // Check that spec is a known supported curve and convert it to our
  75     // ECParameterSpec subclass. If not possible, return null.
  76     static ECParameterSpec getECParameterSpec(ECParameterSpec spec) {
  77         return ECUtil.getECParameterSpec(getSunECProvider(), spec);
  78     }
  79 
  80     static ECParameterSpec decodeParameters(byte[] params) throws IOException {
  81         return ECUtil.getECParameterSpec(getSunECProvider(), params);
  82     }
  83 
  84     static byte[] encodeParameters(ECParameterSpec params) {
  85         return ECUtil.encodeECParameterSpec(getSunECProvider(), params);
  86     }
  87 
  88     static ECPoint decodePoint(byte[] encoded, EllipticCurve curve) throws IOException {
  89         return ECUtil.decodePoint(encoded, curve);
  90     }
  91 
  92     // Used by ECDH KeyAgreement
  93     static byte[] getEncodedPublicValue(PublicKey key) throws InvalidKeyException {
  94         if (key instanceof ECPublicKey) {
  95             ECPublicKey ecKey = (ECPublicKey)key;
  96             ECPoint w = ecKey.getW();
  97             ECParameterSpec params = ecKey.getParams();
  98             return ECUtil.encodePoint(w, params.getCurve());
  99         } else {
 100             // should never occur
 101             throw new InvalidKeyException
 102                 ("Key class not yet supported: " + key.getClass().getName());
 103         }
 104     }
 105 
 106     PublicKey implTranslatePublicKey(PublicKey key) throws InvalidKeyException {
 107         try {
 108             if (key instanceof ECPublicKey) {
 109                 ECPublicKey ecKey = (ECPublicKey)key;
 110                 return generatePublic(
 111                     ecKey.getW(),
 112                     ecKey.getParams()
 113                 );
 114             } else if ("X.509".equals(key.getFormat())) {
 115                 // let Sun provider parse for us, then recurse
 116                 byte[] encoded = key.getEncoded();
 117 
 118                 try {
 119                     key = ECUtil.decodeX509ECPublicKey(encoded);
 120                 } catch (InvalidKeySpecException ikse) {
 121                     throw new InvalidKeyException(ikse);
 122                 }
 123 
 124                 return implTranslatePublicKey(key);
 125             } else {
 126                 throw new InvalidKeyException("PublicKey must be instance "
 127                         + "of ECPublicKey or have X.509 encoding");
 128             }
 129         } catch (PKCS11Exception e) {
 130             throw new InvalidKeyException("Could not create EC public key", e);
 131         }
 132     }
 133 
 134     PrivateKey implTranslatePrivateKey(PrivateKey key)
 135             throws InvalidKeyException {
 136         try {
 137             if (key instanceof ECPrivateKey) {
 138                 ECPrivateKey ecKey = (ECPrivateKey)key;
 139                 return generatePrivate(
 140                     ecKey.getS(),
 141                     ecKey.getParams()
 142                 );
 143             } else if ("PKCS#8".equals(key.getFormat())) {
 144                 // let Sun provider parse for us, then recurse
 145                 byte[] encoded = key.getEncoded();
 146 
 147                 try {
 148                     key = ECUtil.decodePKCS8ECPrivateKey(encoded);
 149                 } catch (InvalidKeySpecException ikse) {
 150                     throw new InvalidKeyException(ikse);
 151                 }
 152 
 153                 return implTranslatePrivateKey(key);
 154             } else {
 155                 throw new InvalidKeyException("PrivateKey must be instance "
 156                         + "of ECPrivateKey or have PKCS#8 encoding");
 157             }
 158         } catch (PKCS11Exception e) {
 159             throw new InvalidKeyException("Could not create EC private key", e);
 160         }
 161     }
 162 
 163     // see JCA spec
 164     protected PublicKey engineGeneratePublic(KeySpec keySpec)
 165             throws InvalidKeySpecException {
 166         token.ensureValid();
 167         if (keySpec instanceof X509EncodedKeySpec) {
 168             try {
 169                 byte[] encoded = ((X509EncodedKeySpec)keySpec).getEncoded();
 170                 PublicKey key = ECUtil.decodeX509ECPublicKey(encoded);
 171                 return implTranslatePublicKey(key);
 172             } catch (InvalidKeyException e) {
 173                 throw new InvalidKeySpecException
 174                         ("Could not create EC public key", e);
 175             }
 176         }
 177         if (keySpec instanceof ECPublicKeySpec == false) {
 178             throw new InvalidKeySpecException("Only ECPublicKeySpec and "
 179                 + "X509EncodedKeySpec supported for EC public keys");
 180         }
 181         try {
 182             ECPublicKeySpec ec = (ECPublicKeySpec)keySpec;
 183             return generatePublic(
 184                 ec.getW(),
 185                 ec.getParams()
 186             );
 187         } catch (PKCS11Exception e) {
 188             throw new InvalidKeySpecException
 189                 ("Could not create EC public key", e);
 190         }
 191     }
 192 
 193     // see JCA spec
 194     protected PrivateKey engineGeneratePrivate(KeySpec keySpec)
 195             throws InvalidKeySpecException {
 196         token.ensureValid();
 197         if (keySpec instanceof PKCS8EncodedKeySpec) {
 198             try {
 199                 byte[] encoded = ((PKCS8EncodedKeySpec)keySpec).getEncoded();
 200                 PrivateKey key = ECUtil.decodePKCS8ECPrivateKey(encoded);
 201                 return implTranslatePrivateKey(key);
 202             } catch (GeneralSecurityException e) {
 203                 throw new InvalidKeySpecException
 204                         ("Could not create EC private key", e);
 205             }
 206         }
 207         if (keySpec instanceof ECPrivateKeySpec == false) {
 208             throw new InvalidKeySpecException("Only ECPrivateKeySpec and "
 209                 + "PKCS8EncodedKeySpec supported for EC private keys");
 210         }
 211         try {
 212             ECPrivateKeySpec ec = (ECPrivateKeySpec)keySpec;
 213             return generatePrivate(
 214                 ec.getS(),
 215                 ec.getParams()
 216             );
 217         } catch (PKCS11Exception e) {
 218             throw new InvalidKeySpecException
 219                 ("Could not create EC private key", e);
 220         }
 221     }
 222 
 223     private PublicKey generatePublic(ECPoint point, ECParameterSpec params)
 224             throws PKCS11Exception {
 225         byte[] encodedParams =
 226             ECUtil.encodeECParameterSpec(getSunECProvider(), params);
 227         byte[] encodedPoint =
 228             ECUtil.encodePoint(point, params.getCurve());
 229 
 230         // Check whether the X9.63 encoding of an EC point shall be wrapped
 231         // in an ASN.1 OCTET STRING
 232         if (!token.config.getUseEcX963Encoding()) {
 233             try {
 234                 encodedPoint =
 235                     new DerValue(DerValue.tag_OctetString, encodedPoint)
 236                         .toByteArray();
 237             } catch (IOException e) {
 238                 throw new
 239                     IllegalArgumentException("Could not DER encode point", e);
 240             }
 241         }
 242 
 243         CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
 244             new CK_ATTRIBUTE(CKA_CLASS, CKO_PUBLIC_KEY),
 245             new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_EC),
 246             new CK_ATTRIBUTE(CKA_EC_POINT, encodedPoint),
 247             new CK_ATTRIBUTE(CKA_EC_PARAMS, encodedParams),
 248         };
 249         attributes = token.getAttributes
 250                 (O_IMPORT, CKO_PUBLIC_KEY, CKK_EC, attributes);
 251         Session session = null;
 252         try {
 253             session = token.getObjSession();
 254             long keyID = token.p11.C_CreateObject(session.id(), attributes);
 255             return P11Key.publicKey
 256                 (session, keyID, "EC", params.getCurve().getField().getFieldSize(), attributes, true);
 257         } finally {
 258             token.releaseSession(session);
 259         }
 260     }
 261 
 262     private PrivateKey generatePrivate(BigInteger s, ECParameterSpec params)
 263             throws PKCS11Exception {
 264         byte[] encodedParams =
 265             ECUtil.encodeECParameterSpec(getSunECProvider(), params);
 266         CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
 267             new CK_ATTRIBUTE(CKA_CLASS, CKO_PRIVATE_KEY),
 268             new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_EC),
 269             new CK_ATTRIBUTE(CKA_VALUE, s),
 270             new CK_ATTRIBUTE(CKA_EC_PARAMS, encodedParams),
 271         };
 272         attributes = token.getAttributes
 273                 (O_IMPORT, CKO_PRIVATE_KEY, CKK_EC, attributes);
 274         Session session = null;
 275         try {
 276             session = token.getObjSession();
 277             long keyID = token.p11.C_CreateObject(session.id(), attributes);
 278             return P11Key.privateKey
 279                 (session, keyID, "EC", params.getCurve().getField().getFieldSize(), attributes, true);
 280         } finally {
 281             token.releaseSession(session);
 282         }
 283     }
 284 
 285     <T extends KeySpec> T implGetPublicKeySpec(P11Key key, Class<T> keySpec,
 286             Session[] session) throws PKCS11Exception, InvalidKeySpecException {
 287         if (ECPublicKeySpec.class.isAssignableFrom(keySpec)) {
 288             session[0] = token.getObjSession();
 289             CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
 290                 new CK_ATTRIBUTE(CKA_EC_POINT),
 291                 new CK_ATTRIBUTE(CKA_EC_PARAMS),
 292             };
 293             key.incNativeKeyRef();
 294             try {
 295                 token.p11.C_GetAttributeValue(session[0].id(), key.keyID, attributes);
 296             } finally {
 297                 key.decNativeKeyRef();
 298             }
 299             try {
 300                 ECParameterSpec params = decodeParameters(attributes[1].getByteArray());
 301                 ECPoint point = decodePoint(attributes[0].getByteArray(), params.getCurve());
 302                 return keySpec.cast(new ECPublicKeySpec(point, params));
 303             } catch (IOException e) {
 304                 throw new InvalidKeySpecException("Could not parse key", e);
 305             }
 306         } else { // X.509 handled in superclass
 307             throw new InvalidKeySpecException("Only ECPublicKeySpec and "
 308                 + "X509EncodedKeySpec supported for EC public keys");
 309         }
 310     }
 311 
 312     <T extends KeySpec> T implGetPrivateKeySpec(P11Key key, Class<T> keySpec,
 313             Session[] session) throws PKCS11Exception, InvalidKeySpecException {
 314         if (ECPrivateKeySpec.class.isAssignableFrom(keySpec)) {
 315             session[0] = token.getObjSession();
 316             CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
 317                 new CK_ATTRIBUTE(CKA_VALUE),
 318                 new CK_ATTRIBUTE(CKA_EC_PARAMS),
 319             };
 320             key.incNativeKeyRef();
 321             try {
 322                 token.p11.C_GetAttributeValue(session[0].id(), key.keyID, attributes);
 323             } finally {
 324                 key.decNativeKeyRef();
 325             }
 326             try {
 327                 ECParameterSpec params = decodeParameters(attributes[1].getByteArray());
 328                 return keySpec.cast(
 329                     new ECPrivateKeySpec(attributes[0].getBigInteger(), params));
 330             } catch (IOException e) {
 331                 throw new InvalidKeySpecException("Could not parse key", e);
 332             }
 333         } else { // PKCS#8 handled in superclass
 334             throw new InvalidKeySpecException("Only ECPrivateKeySpec "
 335                 + "and PKCS8EncodedKeySpec supported for EC private keys");
 336         }
 337     }
 338 
 339     KeyFactory implGetSoftwareFactory() throws GeneralSecurityException {
 340         return KeyFactory.getInstance("EC", getSunECProvider());
 341     }
 342 
 343 }