75 * @since 1.6 |
74 * @since 1.6 |
76 * @author Andreas Sterbenz |
75 * @author Andreas Sterbenz |
77 */ |
76 */ |
78 public final class ECParameters extends AlgorithmParametersSpi { |
77 public final class ECParameters extends AlgorithmParametersSpi { |
79 |
78 |
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 // Per ANSI X9.62, an encoded point is a 1 byte type followed by |
|
91 // ceiling(log base 2 field-size / 8) bytes of x and the same of y. |
|
92 int n = (data.length - 1) / 2; |
|
93 if (n != ((curve.getField().getFieldSize() + 7 ) >> 3)) { |
|
94 throw new IOException("Point does not match field size"); |
|
95 } |
|
96 byte[] xb = new byte[n]; |
|
97 byte[] yb = new byte[n]; |
|
98 System.arraycopy(data, 1, xb, 0, n); |
|
99 System.arraycopy(data, n + 1, yb, 0, n); |
|
100 return new ECPoint(new BigInteger(1, xb), new BigInteger(1, yb)); |
|
101 } |
|
102 |
|
103 // Used by SunPKCS11 and SunJSSE. |
|
104 public static byte[] encodePoint(ECPoint point, EllipticCurve curve) { |
|
105 // get field size in bytes (rounding up) |
|
106 int n = (curve.getField().getFieldSize() + 7) >> 3; |
|
107 byte[] xb = trimZeroes(point.getAffineX().toByteArray()); |
|
108 byte[] yb = trimZeroes(point.getAffineY().toByteArray()); |
|
109 if ((xb.length > n) || (yb.length > n)) { |
|
110 throw new RuntimeException |
|
111 ("Point coordinates do not match field size"); |
|
112 } |
|
113 byte[] b = new byte[1 + (n << 1)]; |
|
114 b[0] = 4; // uncompressed |
|
115 System.arraycopy(xb, 0, b, n - xb.length + 1, xb.length); |
|
116 System.arraycopy(yb, 0, b, b.length - yb.length, yb.length); |
|
117 return b; |
|
118 } |
|
119 |
|
120 // Copied from the SunPKCS11 code - should be moved to a common location. |
|
121 // trim leading (most significant) zeroes from the result |
|
122 static byte[] trimZeroes(byte[] b) { |
|
123 int i = 0; |
|
124 while ((i < b.length - 1) && (b[i] == 0)) { |
|
125 i++; |
|
126 } |
|
127 if (i == 0) { |
|
128 return b; |
|
129 } |
|
130 byte[] t = new byte[b.length - i]; |
|
131 System.arraycopy(b, i, t, 0, t.length); |
|
132 return t; |
|
133 } |
|
134 |
|
135 // Convert the given ECParameterSpec object to a NamedCurve object. |
|
136 // If params does not represent a known named curve, return null. |
|
137 // Used by SunPKCS11. |
|
138 public static NamedCurve getNamedCurve(ECParameterSpec params) { |
|
139 if ((params instanceof NamedCurve) || (params == null)) { |
|
140 return (NamedCurve)params; |
|
141 } |
|
142 // This is a hack to allow SunJSSE to work with 3rd party crypto |
|
143 // providers for ECC and not just SunPKCS11. |
|
144 // This can go away once we decide how to expose curve names in the |
|
145 // public API. |
|
146 // Note that it assumes that the 3rd party provider encodes named |
|
147 // curves using the short form, not explicitly. If it did that, then |
|
148 // the SunJSSE TLS ECC extensions are wrong, which could lead to |
|
149 // interoperability problems. |
|
150 int fieldSize = params.getCurve().getField().getFieldSize(); |
|
151 for (ECParameterSpec namedCurve : NamedCurve.knownECParameterSpecs()) { |
|
152 // ECParameterSpec does not define equals, so check all the |
|
153 // components ourselves. |
|
154 // Quick field size check first |
|
155 if (namedCurve.getCurve().getField().getFieldSize() != fieldSize) { |
|
156 continue; |
|
157 } |
|
158 if (namedCurve.getCurve().equals(params.getCurve()) == false) { |
|
159 continue; |
|
160 } |
|
161 if (namedCurve.getGenerator().equals(params.getGenerator()) == false) { |
|
162 continue; |
|
163 } |
|
164 if (namedCurve.getOrder().equals(params.getOrder()) == false) { |
|
165 continue; |
|
166 } |
|
167 if (namedCurve.getCofactor() != params.getCofactor()) { |
|
168 continue; |
|
169 } |
|
170 // everything matches our named curve, return it |
|
171 return (NamedCurve)namedCurve; |
|
172 } |
|
173 // no match found |
|
174 return null; |
|
175 } |
|
176 |
|
177 // Used by SunJSSE. |
|
178 public static String getCurveName(ECParameterSpec params) { |
|
179 NamedCurve curve = getNamedCurve(params); |
|
180 return (curve == null) ? null : curve.getObjectIdentifier().toString(); |
|
181 } |
|
182 |
|
183 // Used by SunPKCS11. |
|
184 public static byte[] encodeParameters(ECParameterSpec params) { |
|
185 NamedCurve curve = getNamedCurve(params); |
|
186 if (curve == null) { |
|
187 throw new RuntimeException("Not a known named curve: " + params); |
|
188 } |
|
189 return curve.getEncoded(); |
|
190 } |
|
191 |
|
192 // Used by SunPKCS11. |
|
193 public static ECParameterSpec decodeParameters(byte[] params) throws IOException { |
|
194 DerValue encodedParams = new DerValue(params); |
|
195 if (encodedParams.tag == DerValue.tag_ObjectId) { |
|
196 ObjectIdentifier oid = encodedParams.getOID(); |
|
197 ECParameterSpec spec = NamedCurve.getECParameterSpec(oid); |
|
198 if (spec == null) { |
|
199 throw new IOException("Unknown named curve: " + oid); |
|
200 } |
|
201 return spec; |
|
202 } |
|
203 |
|
204 throw new IOException("Only named ECParameters supported"); |
|
205 |
|
206 // The code below is incomplete. |
|
207 // It is left as a starting point for a complete parsing implementation. |
|
208 |
|
209 /* |
|
210 if (encodedParams.tag != DerValue.tag_Sequence) { |
|
211 throw new IOException("Unsupported EC parameters, tag: " + encodedParams.tag); |
|
212 } |
|
213 |
|
214 encodedParams.data.reset(); |
|
215 |
|
216 DerInputStream in = encodedParams.data; |
|
217 |
|
218 int version = in.getInteger(); |
|
219 if (version != 1) { |
|
220 throw new IOException("Unsupported EC parameters version: " + version); |
|
221 } |
|
222 ECField field = parseField(in); |
|
223 EllipticCurve curve = parseCurve(in, field); |
|
224 ECPoint point = parsePoint(in, curve); |
|
225 |
|
226 BigInteger order = in.getBigInteger(); |
|
227 int cofactor = 0; |
|
228 |
|
229 if (in.available() != 0) { |
|
230 cofactor = in.getInteger(); |
|
231 } |
|
232 |
|
233 // XXX HashAlgorithm optional |
|
234 |
|
235 if (encodedParams.data.available() != 0) { |
|
236 throw new IOException("encoded params have " + |
|
237 encodedParams.data.available() + |
|
238 " extra bytes"); |
|
239 } |
|
240 |
|
241 return new ECParameterSpec(curve, point, order, cofactor); |
|
242 */ |
|
243 } |
|
244 |
|
245 /* |
|
246 private static final ObjectIdentifier fieldTypePrime = |
|
247 ObjectIdentifier.newInternal(new int[] {1, 2, 840, 10045, 1, 1}); |
|
248 |
|
249 private static final ObjectIdentifier fieldTypeChar2 = |
|
250 ObjectIdentifier.newInternal(new int[] {1, 2, 840, 10045, 1, 2}); |
|
251 |
|
252 private static ECField parseField(DerInputStream in) throws IOException { |
|
253 DerValue v = in.getDerValue(); |
|
254 ObjectIdentifier oid = v.data.getOID(); |
|
255 if (oid.equals(fieldTypePrime) == false) { |
|
256 throw new IOException("Only prime fields supported: " + oid); |
|
257 } |
|
258 BigInteger fieldSize = v.data.getBigInteger(); |
|
259 return new ECFieldFp(fieldSize); |
|
260 } |
|
261 |
|
262 private static EllipticCurve parseCurve(DerInputStream in, ECField field) |
|
263 throws IOException { |
|
264 DerValue v = in.getDerValue(); |
|
265 byte[] ab = v.data.getOctetString(); |
|
266 byte[] bb = v.data.getOctetString(); |
|
267 return new EllipticCurve(field, new BigInteger(1, ab), new BigInteger(1, bb)); |
|
268 } |
|
269 |
|
270 private static ECPoint parsePoint(DerInputStream in, EllipticCurve curve) |
|
271 throws IOException { |
|
272 byte[] data = in.getOctetString(); |
|
273 return decodePoint(data, curve); |
|
274 } |
|
275 */ |
|
276 |
|
277 // used by ECPublicKeyImpl and ECPrivateKeyImpl |
79 // used by ECPublicKeyImpl and ECPrivateKeyImpl |
278 static AlgorithmParameters getAlgorithmParameters(ECParameterSpec spec) |
80 static AlgorithmParameters getAlgorithmParameters(ECParameterSpec spec) |
279 throws InvalidKeyException { |
81 throws InvalidKeyException { |
280 try { |
82 try { |
281 AlgorithmParameters params = |
83 AlgorithmParameters params = |
285 } catch (GeneralSecurityException e) { |
87 } catch (GeneralSecurityException e) { |
286 throw new InvalidKeyException("EC parameters error", e); |
88 throw new InvalidKeyException("EC parameters error", e); |
287 } |
89 } |
288 } |
90 } |
289 |
91 |
|
92 /* |
|
93 * The parameters these AlgorithmParameters object represents. |
|
94 * Currently, it is always an instance of NamedCurve. |
|
95 */ |
|
96 private NamedCurve namedCurve; |
|
97 |
|
98 // A public constructor is required by AlgorithmParameters class. |
|
99 public ECParameters() { |
|
100 // empty |
|
101 } |
|
102 |
290 // AlgorithmParameterSpi methods |
103 // AlgorithmParameterSpi methods |
291 |
|
292 // The parameters these AlgorithmParameters object represents. |
|
293 // Currently, it is always an instance of NamedCurve. |
|
294 private ECParameterSpec paramSpec; |
|
295 |
104 |
296 protected void engineInit(AlgorithmParameterSpec paramSpec) |
105 protected void engineInit(AlgorithmParameterSpec paramSpec) |
297 throws InvalidParameterSpecException { |
106 throws InvalidParameterSpecException { |
|
107 |
|
108 if (paramSpec == null) { |
|
109 throw new InvalidParameterSpecException |
|
110 ("paramSpec must not be null"); |
|
111 } |
|
112 |
|
113 if (paramSpec instanceof NamedCurve) { |
|
114 namedCurve = (NamedCurve)paramSpec; |
|
115 return; |
|
116 } |
|
117 |
298 if (paramSpec instanceof ECParameterSpec) { |
118 if (paramSpec instanceof ECParameterSpec) { |
299 this.paramSpec = getNamedCurve((ECParameterSpec)paramSpec); |
119 namedCurve = CurveDB.lookup((ECParameterSpec)paramSpec); |
300 if (this.paramSpec == null) { |
|
301 throw new InvalidParameterSpecException |
|
302 ("Not a supported named curve: " + paramSpec); |
|
303 } |
|
304 } else if (paramSpec instanceof ECGenParameterSpec) { |
120 } else if (paramSpec instanceof ECGenParameterSpec) { |
305 String name = ((ECGenParameterSpec)paramSpec).getName(); |
121 String name = ((ECGenParameterSpec)paramSpec).getName(); |
306 ECParameterSpec spec = NamedCurve.getECParameterSpec(name); |
122 namedCurve = CurveDB.lookup(name); |
307 if (spec == null) { |
123 } else if (paramSpec instanceof ECKeySizeParameterSpec) { |
308 throw new InvalidParameterSpecException("Unknown curve: " + name); |
124 int keySize = ((ECKeySizeParameterSpec)paramSpec).getKeySize(); |
309 } |
125 namedCurve = CurveDB.lookup(keySize); |
310 this.paramSpec = spec; |
|
311 } else if (paramSpec == null) { |
|
312 throw new InvalidParameterSpecException |
|
313 ("paramSpec must not be null"); |
|
314 } else { |
126 } else { |
315 throw new InvalidParameterSpecException |
127 throw new InvalidParameterSpecException |
316 ("Only ECParameterSpec and ECGenParameterSpec supported"); |
128 ("Only ECParameterSpec and ECGenParameterSpec supported"); |
317 } |
129 } |
|
130 |
|
131 if (namedCurve == null) { |
|
132 throw new InvalidParameterSpecException( |
|
133 "Not a supported curve: " + paramSpec); |
|
134 } |
318 } |
135 } |
319 |
136 |
320 protected void engineInit(byte[] params) throws IOException { |
137 protected void engineInit(byte[] params) throws IOException { |
321 paramSpec = decodeParameters(params); |
138 DerValue encodedParams = new DerValue(params); |
322 } |
139 if (encodedParams.tag == DerValue.tag_ObjectId) { |
323 |
140 ObjectIdentifier oid = encodedParams.getOID(); |
324 protected void engineInit(byte[] params, String decodingMethod) throws IOException { |
141 NamedCurve spec = CurveDB.lookup(oid.toString()); |
|
142 if (spec == null) { |
|
143 throw new IOException("Unknown named curve: " + oid); |
|
144 } |
|
145 |
|
146 namedCurve = spec; |
|
147 return; |
|
148 } |
|
149 |
|
150 throw new IOException("Only named ECParameters supported"); |
|
151 |
|
152 // The code below is incomplete. |
|
153 // It is left as a starting point for a complete parsing implementation. |
|
154 |
|
155 /* |
|
156 if (encodedParams.tag != DerValue.tag_Sequence) { |
|
157 throw new IOException("Unsupported EC parameters, tag: " + |
|
158 encodedParams.tag); |
|
159 } |
|
160 |
|
161 encodedParams.data.reset(); |
|
162 |
|
163 DerInputStream in = encodedParams.data; |
|
164 |
|
165 int version = in.getInteger(); |
|
166 if (version != 1) { |
|
167 throw new IOException("Unsupported EC parameters version: " + |
|
168 version); |
|
169 } |
|
170 ECField field = parseField(in); |
|
171 EllipticCurve curve = parseCurve(in, field); |
|
172 ECPoint point = parsePoint(in, curve); |
|
173 |
|
174 BigInteger order = in.getBigInteger(); |
|
175 int cofactor = 0; |
|
176 |
|
177 if (in.available() != 0) { |
|
178 cofactor = in.getInteger(); |
|
179 } |
|
180 |
|
181 // XXX HashAlgorithm optional |
|
182 |
|
183 if (encodedParams.data.available() != 0) { |
|
184 throw new IOException("encoded params have " + |
|
185 encodedParams.data.available() + |
|
186 " extra bytes"); |
|
187 } |
|
188 |
|
189 return new ECParameterSpec(curve, point, order, cofactor); |
|
190 */ |
|
191 } |
|
192 |
|
193 protected void engineInit(byte[] params, String decodingMethod) |
|
194 throws IOException { |
325 engineInit(params); |
195 engineInit(params); |
326 } |
196 } |
327 |
197 |
328 protected <T extends AlgorithmParameterSpec> T engineGetParameterSpec(Class<T> spec) |
198 protected <T extends AlgorithmParameterSpec> T |
|
199 engineGetParameterSpec(Class<T> spec) |
329 throws InvalidParameterSpecException { |
200 throws InvalidParameterSpecException { |
|
201 |
330 if (spec.isAssignableFrom(ECParameterSpec.class)) { |
202 if (spec.isAssignableFrom(ECParameterSpec.class)) { |
331 return spec.cast(paramSpec); |
203 return spec.cast(namedCurve); |
332 } else if (spec.isAssignableFrom(ECGenParameterSpec.class)) { |
204 } |
333 return spec.cast(new ECGenParameterSpec(getCurveName(paramSpec))); |
205 |
334 } else { |
206 if (spec.isAssignableFrom(ECGenParameterSpec.class)) { |
335 throw new InvalidParameterSpecException |
207 // Ensure the name is the Object ID |
336 ("Only ECParameterSpec and ECGenParameterSpec supported"); |
208 String name = namedCurve.getObjectId(); |
337 } |
209 return spec.cast(new ECGenParameterSpec(name)); |
|
210 } |
|
211 |
|
212 if (spec.isAssignableFrom(ECKeySizeParameterSpec.class)) { |
|
213 int keySize = namedCurve.getCurve().getField().getFieldSize(); |
|
214 return spec.cast(new ECKeySizeParameterSpec(keySize)); |
|
215 } |
|
216 |
|
217 throw new InvalidParameterSpecException( |
|
218 "Only ECParameterSpec and ECGenParameterSpec supported"); |
338 } |
219 } |
339 |
220 |
340 protected byte[] engineGetEncoded() throws IOException { |
221 protected byte[] engineGetEncoded() throws IOException { |
341 return encodeParameters(paramSpec); |
222 return namedCurve.getEncoded(); |
342 } |
223 } |
343 |
224 |
344 protected byte[] engineGetEncoded(String encodingMethod) throws IOException { |
225 protected byte[] engineGetEncoded(String encodingMethod) |
|
226 throws IOException { |
345 return engineGetEncoded(); |
227 return engineGetEncoded(); |
346 } |
228 } |
347 |
229 |
348 protected String engineToString() { |
230 protected String engineToString() { |
349 return paramSpec.toString(); |
231 if (namedCurve == null) { |
|
232 return "Not initialized"; |
|
233 } |
|
234 |
|
235 return namedCurve.toString(); |
350 } |
236 } |
351 } |
237 } |
|
238 |