1 /* 2 * Copyright (c) 2006, 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.ec; 27 28 import java.io.IOException; 29 import java.math.BigInteger; 30 31 import java.security.*; 32 import java.security.spec.*; 33 34 import sun.security.util.*; 35 36 /** 37 * This class implements encoding and decoding of Elliptic Curve parameters 38 * as specified in RFC 3279. 39 * 40 * However, only named curves are currently supported. 41 * 42 * ASN.1 from RFC 3279 follows. Note that X9.62 (2005) has added some additional 43 * options. 44 * 45 * <pre> 46 * EcpkParameters ::= CHOICE { 47 * ecParameters ECParameters, 48 * namedCurve OBJECT IDENTIFIER, 49 * implicitlyCA NULL } 50 * 51 * ECParameters ::= SEQUENCE { 52 * version ECPVer, -- version is always 1 53 * fieldID FieldID, -- identifies the finite field over 54 * -- which the curve is defined 55 * curve Curve, -- coefficients a and b of the 56 * -- elliptic curve 57 * base ECPoint, -- specifies the base point P 58 * -- on the elliptic curve 59 * order INTEGER, -- the order n of the base point 60 * cofactor INTEGER OPTIONAL -- The integer h = #E(Fq)/n 61 * } 62 * 63 * ECPVer ::= INTEGER {ecpVer1(1)} 64 * 65 * Curve ::= SEQUENCE { 66 * a FieldElement, 67 * b FieldElement, 68 * seed BIT STRING OPTIONAL } 69 * 70 * FieldElement ::= OCTET STRING 71 * 72 * ECPoint ::= OCTET STRING 73 * </pre> 74 * 75 * @since 1.6 76 * @author Andreas Sterbenz 77 */ 78 public final class ECParameters extends AlgorithmParametersSpi { 79 80 public ECParameters() { 81 // empty 82 } 83 84 // Used by SunPKCS11 and SunJSSE. 85 public static ECPoint decodePoint(byte[] data, EllipticCurve curve) 86 throws IOException { 87 if ((data.length == 0) || (data[0] != 4)) { 88 throw new IOException("Only uncompressed point format supported"); 89 } 90 int n = (curve.getField().getFieldSize() + 7 ) >> 3; 91 if (data.length != (n * 2) + 1) { 92 throw new IOException("Point does not match field size"); 93 } 94 byte[] xb = new byte[n]; 95 byte[] yb = new byte[n]; 96 System.arraycopy(data, 1, xb, 0, n); 97 System.arraycopy(data, n + 1, yb, 0, n); 98 return new ECPoint(new BigInteger(1, xb), new BigInteger(1, yb)); 99 } 100 101 // Used by SunPKCS11 and SunJSSE. 102 public static byte[] encodePoint(ECPoint point, EllipticCurve curve) { 103 // get field size in bytes (rounding up) 104 int n = (curve.getField().getFieldSize() + 7) >> 3; 105 byte[] xb = trimZeroes(point.getAffineX().toByteArray()); 106 byte[] yb = trimZeroes(point.getAffineY().toByteArray()); 107 if ((xb.length > n) || (yb.length > n)) { 108 throw new RuntimeException 109 ("Point coordinates do not match field size"); 110 } 111 byte[] b = new byte[1 + (n << 1)]; 112 b[0] = 4; // uncompressed 113 System.arraycopy(xb, 0, b, n - xb.length + 1, xb.length); 114 System.arraycopy(yb, 0, b, b.length - yb.length, yb.length); 115 return b; 116 } 117 118 // Copied from the SunPKCS11 code - should be moved to a common location. 119 // trim leading (most significant) zeroes from the result 120 static byte[] trimZeroes(byte[] b) { 121 int i = 0; 122 while ((i < b.length - 1) && (b[i] == 0)) { 123 i++; 124 } 125 if (i == 0) { 126 return b; 127 } 128 byte[] t = new byte[b.length - i]; 129 System.arraycopy(b, i, t, 0, t.length); 130 return t; 131 } 132 133 // Convert the given ECParameterSpec object to a NamedCurve object. 134 // If params does not represent a known named curve, return null. 135 // Used by SunPKCS11. 136 public static NamedCurve getNamedCurve(ECParameterSpec params) { 137 if ((params instanceof NamedCurve) || (params == null)) { 138 return (NamedCurve)params; 139 } 140 // This is a hack to allow SunJSSE to work with 3rd party crypto 141 // providers for ECC and not just SunPKCS11. 142 // This can go away once we decide how to expose curve names in the 143 // public API. 144 // Note that it assumes that the 3rd party provider encodes named 145 // curves using the short form, not explicitly. If it did that, then 146 // the SunJSSE TLS ECC extensions are wrong, which could lead to 147 // interoperability problems. 148 int fieldSize = params.getCurve().getField().getFieldSize(); 149 for (ECParameterSpec namedCurve : NamedCurve.knownECParameterSpecs()) { 150 // ECParameterSpec does not define equals, so check all the 151 // components ourselves. 152 // Quick field size check first 153 if (namedCurve.getCurve().getField().getFieldSize() != fieldSize) { 154 continue; 155 } 156 if (namedCurve.getCurve().equals(params.getCurve()) == false) { 157 continue; 158 } 159 if (namedCurve.getGenerator().equals(params.getGenerator()) == false) { 160 continue; 161 } 162 if (namedCurve.getOrder().equals(params.getOrder()) == false) { 163 continue; 164 } 165 if (namedCurve.getCofactor() != params.getCofactor()) { 166 continue; 167 } 168 // everything matches our named curve, return it 169 return (NamedCurve)namedCurve; 170 } 171 // no match found 172 return null; 173 } 174 175 // Used by SunJSSE. 176 public static String getCurveName(ECParameterSpec params) { 177 NamedCurve curve = getNamedCurve(params); 178 return (curve == null) ? null : curve.getObjectIdentifier().toString(); 179 } 180 181 // Used by SunPKCS11. 182 public static byte[] encodeParameters(ECParameterSpec params) { 183 NamedCurve curve = getNamedCurve(params); 184 if (curve == null) { 185 throw new RuntimeException("Not a known named curve: " + params); 186 } 187 return curve.getEncoded(); 188 } 189 190 // Used by SunPKCS11. 191 public static ECParameterSpec decodeParameters(byte[] params) throws IOException { 192 DerValue encodedParams = new DerValue(params); 193 if (encodedParams.tag == DerValue.tag_ObjectId) { 194 ObjectIdentifier oid = encodedParams.getOID(); 195 ECParameterSpec spec = NamedCurve.getECParameterSpec(oid); 196 if (spec == null) { 197 throw new IOException("Unknown named curve: " + oid); 198 } 199 return spec; 200 } 201 202 throw new IOException("Only named ECParameters supported"); 203 204 // The code below is incomplete. 205 // It is left as a starting point for a complete parsing implementation. 206 207 /* 208 if (encodedParams.tag != DerValue.tag_Sequence) { 209 throw new IOException("Unsupported EC parameters, tag: " + encodedParams.tag); 210 } 211 212 encodedParams.data.reset(); 213 214 DerInputStream in = encodedParams.data; 215 216 int version = in.getInteger(); 217 if (version != 1) { 218 throw new IOException("Unsupported EC parameters version: " + version); 219 } 220 ECField field = parseField(in); 221 EllipticCurve curve = parseCurve(in, field); 222 ECPoint point = parsePoint(in, curve); 223 224 BigInteger order = in.getBigInteger(); 225 int cofactor = 0; 226 227 if (in.available() != 0) { 228 cofactor = in.getInteger(); 229 } 230 231 // XXX HashAlgorithm optional 232 233 if (encodedParams.data.available() != 0) { 234 throw new IOException("encoded params have " + 235 encodedParams.data.available() + 236 " extra bytes"); 237 } 238 239 return new ECParameterSpec(curve, point, order, cofactor); 240 */ 241 } 242 243 /* 244 private static final ObjectIdentifier fieldTypePrime = 245 ObjectIdentifier.newInternal(new int[] {1, 2, 840, 10045, 1, 1}); 246 247 private static final ObjectIdentifier fieldTypeChar2 = 248 ObjectIdentifier.newInternal(new int[] {1, 2, 840, 10045, 1, 2}); 249 250 private static ECField parseField(DerInputStream in) throws IOException { 251 DerValue v = in.getDerValue(); 252 ObjectIdentifier oid = v.data.getOID(); 253 if (oid.equals(fieldTypePrime) == false) { 254 throw new IOException("Only prime fields supported: " + oid); 255 } 256 BigInteger fieldSize = v.data.getBigInteger(); 257 return new ECFieldFp(fieldSize); 258 } 259 260 private static EllipticCurve parseCurve(DerInputStream in, ECField field) 261 throws IOException { 262 DerValue v = in.getDerValue(); 263 byte[] ab = v.data.getOctetString(); 264 byte[] bb = v.data.getOctetString(); 265 return new EllipticCurve(field, new BigInteger(1, ab), new BigInteger(1, bb)); 266 } 267 268 private static ECPoint parsePoint(DerInputStream in, EllipticCurve curve) 269 throws IOException { 270 byte[] data = in.getOctetString(); 271 return decodePoint(data, curve); 272 } 273 */ 274 275 // used by ECPublicKeyImpl and ECPrivateKeyImpl 276 static AlgorithmParameters getAlgorithmParameters(ECParameterSpec spec) 277 throws InvalidKeyException { 278 try { 279 AlgorithmParameters params = AlgorithmParameters.getInstance 280 ("EC", ECKeyFactory.ecInternalProvider); 281 params.init(spec); 282 return params; 283 } catch (GeneralSecurityException e) { 284 throw new InvalidKeyException("EC parameters error", e); 285 } 286 } 287 288 // AlgorithmParameterSpi methods 289 290 // The parameters these AlgorithmParameters object represents. 291 // Currently, it is always an instance of NamedCurve. 292 private ECParameterSpec paramSpec; 293 294 protected void engineInit(AlgorithmParameterSpec paramSpec) 295 throws InvalidParameterSpecException { 296 if (paramSpec instanceof ECParameterSpec) { 297 this.paramSpec = getNamedCurve((ECParameterSpec)paramSpec); 298 if (this.paramSpec == null) { 299 throw new InvalidParameterSpecException 300 ("Not a supported named curve: " + paramSpec); 301 } 302 } else if (paramSpec instanceof ECGenParameterSpec) { 303 String name = ((ECGenParameterSpec)paramSpec).getName(); 304 ECParameterSpec spec = NamedCurve.getECParameterSpec(name); 305 if (spec == null) { 306 throw new InvalidParameterSpecException("Unknown curve: " + name); 307 } 308 this.paramSpec = spec; 309 } else if (paramSpec == null) { 310 throw new InvalidParameterSpecException 311 ("paramSpec must not be null"); 312 } else { 313 throw new InvalidParameterSpecException 314 ("Only ECParameterSpec and ECGenParameterSpec supported"); 315 } 316 } 317 318 protected void engineInit(byte[] params) throws IOException { 319 paramSpec = decodeParameters(params); 320 } 321 322 protected void engineInit(byte[] params, String decodingMethod) throws IOException { 323 engineInit(params); 324 } 325 326 protected <T extends AlgorithmParameterSpec> T engineGetParameterSpec(Class<T> spec) 327 throws InvalidParameterSpecException { 328 if (spec.isAssignableFrom(ECParameterSpec.class)) { 329 return (T)paramSpec; 330 } else if (spec.isAssignableFrom(ECGenParameterSpec.class)) { 331 return (T)new ECGenParameterSpec(getCurveName(paramSpec)); 332 } else { 333 throw new InvalidParameterSpecException 334 ("Only ECParameterSpec and ECGenParameterSpec supported"); 335 } 336 } 337 338 protected byte[] engineGetEncoded() throws IOException { 339 return encodeParameters(paramSpec); 340 } 341 342 protected byte[] engineGetEncoded(String encodingMethod) throws IOException { 343 return engineGetEncoded(); 344 } 345 346 protected String engineToString() { 347 return paramSpec.toString(); 348 } 349 } 350