/* * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.security.ec; import java.security.*; import java.security.interfaces.*; import java.security.spec.*; import javax.crypto.*; import javax.crypto.spec.*; import sun.security.util.ECUtil; /** * KeyAgreement implementation for ECDH. * * @since 1.7 */ public final class ECDHKeyAgreement extends KeyAgreementSpi { // private key, if initialized private ECPrivateKey privateKey; // encoded public point, non-null between doPhase() & generateSecret() only private byte[] publicValue; // length of the secret to be derived private int secretLen; /** * Constructs a new ECDHKeyAgreement. */ public ECDHKeyAgreement() { } // see JCE spec @Override protected void engineInit(Key key, SecureRandom random) throws InvalidKeyException { if (!(key instanceof PrivateKey)) { throw new InvalidKeyException ("Key must be instance of PrivateKey"); } privateKey = (ECPrivateKey) ECKeyFactory.toECKey(key); publicValue = null; } // see JCE spec @Override protected void engineInit(Key key, AlgorithmParameterSpec params, SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { if (params != null) { throw new InvalidAlgorithmParameterException ("Parameters not supported"); } engineInit(key, random); } // see JCE spec @Override protected Key engineDoPhase(Key key, boolean lastPhase) throws InvalidKeyException, IllegalStateException { if (privateKey == null) { throw new IllegalStateException("Not initialized"); } if (publicValue != null) { throw new IllegalStateException("Phase already executed"); } if (!lastPhase) { throw new IllegalStateException ("Only two party agreement supported, lastPhase must be true"); } if (!(key instanceof ECPublicKey)) { throw new InvalidKeyException ("Key must be a PublicKey with algorithm EC"); } ECPublicKey ecKey = (ECPublicKey)key; ECParameterSpec params = ecKey.getParams(); if (ecKey instanceof ECPublicKeyImpl) { publicValue = ((ECPublicKeyImpl)ecKey).getEncodedPublicValue(); } else { // instanceof ECPublicKey publicValue = ECUtil.encodePoint(ecKey.getW(), params.getCurve()); } int keyLenBits = params.getCurve().getField().getFieldSize(); secretLen = (keyLenBits + 7) >> 3; return null; } // see JCE spec @Override protected byte[] engineGenerateSecret() throws IllegalStateException { if ((privateKey == null) || (publicValue == null)) { throw new IllegalStateException("Not initialized correctly"); } byte[] s = privateKey.getS().toByteArray(); byte[] encodedParams = // DER OID ECUtil.encodeECParameterSpec(null, privateKey.getParams()); try { return deriveKey(s, publicValue, encodedParams); } catch (GeneralSecurityException e) { throw new ProviderException("Could not derive key", e); } } // see JCE spec @Override protected int engineGenerateSecret(byte[] sharedSecret, int offset) throws IllegalStateException, ShortBufferException { if (offset + secretLen > sharedSecret.length) { throw new ShortBufferException("Need " + secretLen + " bytes, only " + (sharedSecret.length - offset) + " available"); } byte[] secret = engineGenerateSecret(); System.arraycopy(secret, 0, sharedSecret, offset, secret.length); return secret.length; } // see JCE spec @Override protected SecretKey engineGenerateSecret(String algorithm) throws IllegalStateException, NoSuchAlgorithmException, InvalidKeyException { if (algorithm == null) { throw new NoSuchAlgorithmException("Algorithm must not be null"); } if (!(algorithm.equals("TlsPremasterSecret"))) { throw new NoSuchAlgorithmException ("Only supported for algorithm TlsPremasterSecret"); } return new SecretKeySpec(engineGenerateSecret(), "TlsPremasterSecret"); } /** * Generates a secret key using the public and private keys. * * @param s the private key's S value. * @param w the public key's W point (in uncompressed form). * @param encodedParams the curve's DER encoded object identifier. * * @return byte[] the secret key. */ private static native byte[] deriveKey(byte[] s, byte[] w, byte[] encodedParams) throws GeneralSecurityException; }