Package freenet.crypt

Source Code of freenet.crypt.ECDH

package freenet.crypt;

import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.PublicKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;

import javax.crypto.KeyAgreement;

import freenet.crypt.JceLoader;
import freenet.support.Logger;

public class ECDH {

    public final Curves curve;
    private final KeyPair key;
   
    public enum Curves {
        // rfc5903 or rfc6460: it's NIST's random/prime curves : suite B
        // Order matters. Append to the list, do not re-order.
        P256("secp256r1", 91, 32),
        P384("secp384r1", 120, 48),
        P521("secp521r1", 158, 66);
       
        public final ECGenParameterSpec spec;
        private KeyPairGenerator keygenCached;
        protected final Provider kgProvider;
        protected final Provider kfProvider;
        protected final Provider kaProvider;
        /** Expected size of a pubkey */
        public final int modulusSize;
        /** Expected size of the derived secret (in bytes) */
        public final int derivedSecretSize;
       
        /** Verify KeyPairGenerator and KeyFactory work correctly */
        static private KeyPair selftest(KeyPairGenerator kg, KeyFactory kf, int modulusSize)
            throws InvalidKeySpecException
        {
            KeyPair key = kg.generateKeyPair();
            PublicKey pub = key.getPublic();
            PrivateKey pk = key.getPrivate();
            byte [] pubkey = pub.getEncoded();
            byte [] pkey = pk.getEncoded();
      if(pubkey.length > modulusSize || pubkey.length == 0)
        throw new Error("Unexpected pubkey length: "+pubkey.length+"!="+modulusSize);
            PublicKey pub2 = kf.generatePublic(
                    new X509EncodedKeySpec(pubkey)
                    );
            if(!Arrays.equals(pub2.getEncoded(), pubkey))
                throw new Error("Pubkey encoding mismatch");
            PrivateKey pk2 = kf.generatePrivate(
                    new PKCS8EncodedKeySpec(pkey)
                    );
      /*
            if(!Arrays.equals(pk2.getEncoded(), pkey))
                throw new Error("Pubkey encoding mismatch");
      */
            return key;
        }

    static private void selftest_genSecret(KeyPair key, KeyAgreement ka)
      throws InvalidKeyException
    {
      ka.init(key.getPrivate());
            ka.doPhase(key.getPublic(), true);
            ka.generateSecret();
    }

        private Curves(String name, int modulusSize, int derivedSecretSize) {
            this.spec = new ECGenParameterSpec(name);
            KeyAgreement ka = null;
      KeyFactory kf = null;
            KeyPairGenerator kg = null;
      // Ensure providers loaded
      JceLoader.BouncyCastle.toString();
      try {
        KeyPair key = null;
        try {
          /* check if default EC keys work correctly */
          kg = KeyPairGenerator.getInstance("EC");
          kf = KeyFactory.getInstance("EC");
          kg.initialize(this.spec);
          key = selftest(kg, kf, modulusSize);
        } catch(Throwable e) {
          /* we don't care why we fail, just fallback */
          Logger.warning(this, "default KeyPairGenerator provider ("+(kg != null ? kg.getProvider() : null)+") is broken, falling back to BouncyCastle", e);
          kg = KeyPairGenerator.getInstance("EC", JceLoader.BouncyCastle);
          kf = KeyFactory.getInstance("EC", JceLoader.BouncyCastle);
          kg.initialize(this.spec);
          key = selftest(kg, kf, modulusSize);
        }
        try {
          /* check default KeyAgreement compatible with kf/kg */
          ka = KeyAgreement.getInstance("ECDH");
          selftest_genSecret(key, ka);
        } catch(Throwable e) {
          /* we don't care why we fail, just fallback */
          Logger.warning(this, "default KeyAgreement provider ("+(ka != null ? ka.getProvider() : null)+") is broken or incompatible with KeyPairGenerator, falling back to BouncyCastle", e);
          kg = KeyPairGenerator.getInstance("EC", JceLoader.BouncyCastle);
          kf = KeyFactory.getInstance("EC", JceLoader.BouncyCastle);
          kg.initialize(this.spec);
          ka = KeyAgreement.getInstance("ECDH", JceLoader.BouncyCastle);
          selftest_genSecret(key, ka);
        }
      } catch(NoSuchAlgorithmException e) {
        System.out.println(e);
        e.printStackTrace(System.out);
      } catch(InvalidKeySpecException e) {
        System.out.println(e);
        e.printStackTrace(System.out);
      } catch(InvalidKeyException e) {
        System.out.println(e);
        e.printStackTrace(System.out);
      } catch(InvalidAlgorithmParameterException e) {
        System.out.println(e);
        e.printStackTrace(System.out);
      }
      this.modulusSize = modulusSize;
      this.derivedSecretSize = derivedSecretSize;

      this.kgProvider = kg.getProvider();
      this.kfProvider = kf.getProvider();
      this.kaProvider = ka.getProvider();
      Logger.normal(this, name +": using "+kgProvider+" for KeyPairGenerator(EC)");
      Logger.normal(this, name +": using "+kfProvider+" for KeyFactory(EC)");
      Logger.normal(this, name +": using "+kaProvider+" for KeyAgreement(ECDH)");
    }
       
        private synchronized KeyPairGenerator getKeyPairGenerator() {
          if(keygenCached != null) return keygenCached;
            KeyPairGenerator kg = null;
            try {
                kg = KeyPairGenerator.getInstance("EC", kgProvider);
                kg.initialize(spec);
            } catch (NoSuchAlgorithmException e) {
                Logger.error(ECDH.class, "NoSuchAlgorithmException : "+e.getMessage(),e);
                e.printStackTrace();
            } catch (InvalidAlgorithmParameterException e) {
                Logger.error(ECDH.class, "InvalidAlgorithmParameterException : "+e.getMessage(),e);
                e.printStackTrace();
            }
            keygenCached = kg;
            return kg;
        }
       
        public synchronized KeyPair generateKeyPair() {
            return getKeyPairGenerator().generateKeyPair();
        }
       
        public String toString() {
            return spec.getName();
        }
    }
   
    /**
     * Initialize the ECDH Exchange: this will draw some entropy
     * @param curve
     */
    public ECDH(Curves curve) {
        this.curve = curve;
        this.key = curve.generateKeyPair();
    }
   
    /**
     * Completes the ECDH exchange: this is CPU intensive
     * @param pubkey
     * @return a SecretKey or null if it fails
     *
     * **THE OUTPUT SHOULD ALWAYS GO THROUGH A KDF**
     */
    public byte[] getAgreedSecret(ECPublicKey pubkey) {
        try {
            KeyAgreement ka = null;
            ka = KeyAgreement.getInstance("ECDH", curve.kaProvider);
            ka.init(key.getPrivate());
            ka.doPhase(pubkey, true);

            return ka.generateSecret();
        } catch (InvalidKeyException e) {
            Logger.error(this, "InvalidKeyException : "+e.getMessage(),e);
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            Logger.error(this, "NoSuchAlgorithmException : "+e.getMessage(),e);
            e.printStackTrace();
        }
        return null;
    }
   
    public ECPublicKey getPublicKey() {
        return (ECPublicKey) key.getPublic();
    }
   
    /**
     * Returns an ECPublicKey from bytes obtained using ECPublicKey.getEncoded()
     * @param data
     * @return ECPublicKey or null if it fails
     */
    public static ECPublicKey getPublicKey(byte[] data, Curves curve) {
        ECPublicKey remotePublicKey = null;
        try {
            X509EncodedKeySpec ks = new X509EncodedKeySpec(data);
            KeyFactory kf = KeyFactory.getInstance("EC", curve.kfProvider);
            remotePublicKey = (ECPublicKey)kf.generatePublic(ks);
           
        } catch (NoSuchAlgorithmException e) {
            Logger.error(ECDH.class, "NoSuchAlgorithmException : "+e.getMessage(),e);
            e.printStackTrace();
        } catch (InvalidKeySpecException e) {
            Logger.error(ECDH.class, "InvalidKeySpecException : "+e.getMessage(), e);
            e.printStackTrace();
        }
       
        return remotePublicKey;
    }

    /** Initialize the key pair generators, which in turn will create the
     * global SecureRandom, which may block waiting for entropy from
     * /dev/random on unix-like systems. So this should be called on startup
     * during the "may block for entropy" stage. Note that because this can
     * block, we still have to do lazy initialisation: We do NOT want to
     * have it blocking *at time of loading the classes*, as this will
     * likely appear as the node completely failing to load. Running this
     * after fproxy has started, with a warning timer, allows us to tell
     * the user what is going on if it takes a while. */
    public static void blockingInit() {
      Curves.P256.getKeyPairGenerator();
      // Not used at present.
      // Anyway Bouncycastle uses a single PRNG.
      // If these use separate PRNGs, we need to init them explicitly.
      //Curves.P384.getKeyPairGenerator();
      //Curves.P521.getKeyPairGenerator();
    }

    /** Return the public key as a byte[] in network format */
  public byte[] getPublicKeyNetworkFormat() {
        byte[] ret = getPublicKey().getEncoded();
        if(ret.length == curve.modulusSize) {
          return ret;
        } else if(ret.length > curve.modulusSize) {
          throw new IllegalStateException("Encoded public key too long: should be "+curve.modulusSize+" bytes but is "+ret.length);
        } else {
          Logger.warning(this, "Padding public key from "+ret.length+" to "+curve.modulusSize+" bytes");
          byte[] out = new byte[curve.modulusSize];
          System.arraycopy(ret, 0, out, 0, ret.length);
          return ret;
        }
  }
}
TOP

Related Classes of freenet.crypt.ECDH

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.