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.util;
  27 
  28 import java.io.IOException;
  29 
  30 import java.math.BigInteger;
  31 
  32 import java.security.*;
  33 
  34 import java.security.interfaces.*;
  35 
  36 import java.security.spec.*;
  37 
  38 import java.util.Arrays;
  39 
  40 import sun.security.x509.X509Key;
  41 
  42 public class ECUtil {
  43 
  44     // Used by SunPKCS11 and SunJSSE.
  45     public static ECPoint decodePoint(byte[] data, EllipticCurve curve)
  46             throws IOException {
  47         if ((data.length == 0) || (data[0] != 4)) {
  48             throw new IOException("Only uncompressed point format supported");
  49         }
  50         // Per ANSI X9.62, an encoded point is a 1 byte type followed by
  51         // ceiling(log base 2 field-size / 8) bytes of x and the same of y.
  52         int n = (data.length - 1) / 2;
  53         if (n != ((curve.getField().getFieldSize() + 7 ) >> 3)) {
  54             throw new IOException("Point does not match field size");
  55         }
  56 
  57         byte[] xb = Arrays.copyOfRange(data, 1, 1 + n);
  58         byte[] yb = Arrays.copyOfRange(data, n + 1, n + 1 + n);
  59 
  60         return new ECPoint(new BigInteger(1, xb), new BigInteger(1, yb));
  61     }
  62 
  63     // Used by SunPKCS11 and SunJSSE.
  64     public static byte[] encodePoint(ECPoint point, EllipticCurve curve) {
  65         // get field size in bytes (rounding up)
  66         int n = (curve.getField().getFieldSize() + 7) >> 3;
  67         byte[] xb = trimZeroes(point.getAffineX().toByteArray());
  68         byte[] yb = trimZeroes(point.getAffineY().toByteArray());
  69         if ((xb.length > n) || (yb.length > n)) {
  70             throw new RuntimeException
  71                 ("Point coordinates do not match field size");
  72         }
  73         byte[] b = new byte[1 + (n << 1)];
  74         b[0] = 4; // uncompressed
  75         System.arraycopy(xb, 0, b, n - xb.length + 1, xb.length);
  76         System.arraycopy(yb, 0, b, b.length - yb.length, yb.length);
  77         return b;
  78     }
  79 
  80     public static byte[] trimZeroes(byte[] b) {
  81         int i = 0;
  82         while ((i < b.length - 1) && (b[i] == 0)) {
  83             i++;
  84         }
  85         if (i == 0) {
  86             return b;
  87         }
  88 
  89         return Arrays.copyOfRange(b, i, b.length);
  90     }
  91 
  92     private static KeyFactory getKeyFactory() {
  93         try {
  94             return KeyFactory.getInstance("EC", "SunEC");
  95         } catch (NoSuchAlgorithmException | NoSuchProviderException e) {
  96             throw new RuntimeException(e);
  97         }
  98     }
  99 
 100     public static ECPublicKey decodeX509ECPublicKey(byte[] encoded)
 101             throws InvalidKeySpecException {
 102         KeyFactory keyFactory = getKeyFactory();
 103         X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encoded);
 104 
 105         return (ECPublicKey)keyFactory.generatePublic(keySpec);
 106     }
 107 
 108     public static byte[] x509EncodeECPublicKey(ECPoint w,
 109             ECParameterSpec params) throws InvalidKeySpecException {
 110         KeyFactory keyFactory = getKeyFactory();
 111         ECPublicKeySpec keySpec = new ECPublicKeySpec(w, params);
 112         X509Key key = (X509Key)keyFactory.generatePublic(keySpec);
 113 
 114         return key.getEncoded();
 115     }
 116 
 117     public static ECPrivateKey decodePKCS8ECPrivateKey(byte[] encoded)
 118             throws InvalidKeySpecException {
 119         KeyFactory keyFactory = getKeyFactory();
 120         PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
 121 
 122         return (ECPrivateKey)keyFactory.generatePrivate(keySpec);
 123     }
 124 
 125     public static ECPrivateKey generateECPrivateKey(BigInteger s,
 126             ECParameterSpec params) throws InvalidKeySpecException {
 127         KeyFactory keyFactory = getKeyFactory();
 128         ECPrivateKeySpec keySpec = new ECPrivateKeySpec(s, params);
 129 
 130         return (ECPrivateKey)keyFactory.generatePrivate(keySpec);
 131     }
 132 
 133     public static AlgorithmParameters getECParameters(Provider p) {
 134         try {
 135             if (p != null) {
 136                 return AlgorithmParameters.getInstance("EC", p);
 137             }
 138 
 139             return AlgorithmParameters.getInstance("EC");
 140         } catch (NoSuchAlgorithmException nsae) {
 141             throw new RuntimeException(nsae);
 142         }
 143     }
 144 
 145     public static byte[] encodeECParameterSpec(Provider p,
 146                                                ECParameterSpec spec) {
 147         AlgorithmParameters parameters = getECParameters(p);
 148 
 149         try {
 150             parameters.init(spec);
 151         } catch (InvalidParameterSpecException ipse) {
 152             throw new RuntimeException("Not a known named curve: " + spec);
 153         }
 154 
 155         try {
 156             return parameters.getEncoded();
 157         } catch (IOException ioe) {
 158             // it is a bug if this should happen
 159             throw new RuntimeException(ioe);
 160         }
 161     }
 162 
 163     public static ECParameterSpec getECParameterSpec(Provider p,
 164                                                      ECParameterSpec spec) {
 165         AlgorithmParameters parameters = getECParameters(p);
 166 
 167         try {
 168             parameters.init(spec);
 169             return parameters.getParameterSpec(ECParameterSpec.class);
 170         } catch (InvalidParameterSpecException ipse) {
 171             return null;
 172         }
 173     }
 174 
 175     public static ECParameterSpec getECParameterSpec(Provider p,
 176                                                      byte[] params)
 177             throws IOException {
 178         AlgorithmParameters parameters = getECParameters(p);
 179 
 180         parameters.init(params);
 181 
 182         try {
 183             return parameters.getParameterSpec(ECParameterSpec.class);
 184         } catch (InvalidParameterSpecException ipse) {
 185             return null;
 186         }
 187     }
 188 
 189     public static ECParameterSpec getECParameterSpec(Provider p, String name) {
 190         AlgorithmParameters parameters = getECParameters(p);
 191 
 192         try {
 193             parameters.init(new ECGenParameterSpec(name));
 194             return parameters.getParameterSpec(ECParameterSpec.class);
 195         } catch (InvalidParameterSpecException ipse) {
 196             return null;
 197         }
 198     }
 199 
 200     public static ECParameterSpec getECParameterSpec(Provider p, int keySize) {
 201         AlgorithmParameters parameters = getECParameters(p);
 202 
 203         try {
 204             parameters.init(new ECKeySizeParameterSpec(keySize));
 205             return parameters.getParameterSpec(ECParameterSpec.class);
 206         } catch (InvalidParameterSpecException ipse) {
 207             return null;
 208         }
 209 
 210     }
 211 
 212     public static String getCurveName(Provider p, ECParameterSpec spec) {
 213         ECGenParameterSpec nameSpec;
 214         AlgorithmParameters parameters = getECParameters(p);
 215 
 216         try {
 217             parameters.init(spec);
 218             nameSpec = parameters.getParameterSpec(ECGenParameterSpec.class);
 219         } catch (InvalidParameterSpecException ipse) {
 220             return null;
 221         }
 222 
 223         if (nameSpec == null) {
 224             return null;
 225         }
 226 
 227         return nameSpec.getName();
 228     }
 229 
 230     private ECUtil() {}
 231 }