@@ -103,17 +103,10 @@ final class ECPrivateKey extends PKCS8Key implements java.security.interfaces.EC
103103 this .provider = provider ;
104104
105105 try {
106- // Set parameters.
107- AlgorithmParameters algParams = this .algid .getParameters ();
108- if (algParams == null ) {
109- throw new IOException (
110- "EC domain parameters must be encoded in the algorithm identifier" );
111- }
112- this .params = algParams .getParameterSpec (ECParameterSpec .class );
113-
114106 // Get from the encoding:
115107 // * the private key as a BigInteger (this.s)
116108 // * the public key, if available (this.pubKeyEncoded)
109+ // and set parameters.
117110 parsePrivateKeyEncoding ();
118111
119112 // Create appropriate encoding and create ecKey.
@@ -140,20 +133,13 @@ final class ECPrivateKey extends PKCS8Key implements java.security.interfaces.EC
140133 algidOut .putDerValue (new DerValue (ecKey .getParameters ()));
141134 this .algid = AlgorithmId
142135 .parse (new DerValue (DerValue .tag_Sequence , algidOut .toByteArray ()));
143-
144- AlgorithmParameters algParams = this .algid .getParameters ();
145- if (algParams == null ) {
146- throw new IOException (
147- "EC domain parameters must be encoded in the algorithm identifier" );
148- }
149- this .params = algParams .getParameterSpec (ECParameterSpec .class );
150-
151136 // Get private key encoding from ECKey.
152137 this .privKeyMaterial = ecKey .getPrivateKeyBytes ();
153138
154139 // Get from the encoding:
155140 // * the private key as a BigInteger (this.s)
156141 // * the public key, if available (this.pubKeyEncoded)
142+ // and set parameters.
157143 parsePrivateKeyEncoding ();
158144 } catch (Exception exception ) {
159145 throw new InvalidKeyException ("Failed to create EC private key" , exception );
@@ -181,33 +167,13 @@ private byte[] createEncodedPrivateKeyWithParams() throws IOException {
181167 DerValue [] inputDerValue = privKeyBytesEncodedStream .getSequence (4 );
182168 DerOutputStream outEncodedStream = new DerOutputStream ();
183169
184- if (inputDerValue .length < 2 ) {
185- throw new IOException ("Incorrect EC private key encoding" );
186- }
187170 BigInteger tempVersion1 = inputDerValue [0 ].getBigInteger ();
188- if (tempVersion1 .compareTo (BigInteger .ONE ) != 0 ) {
189- throw new IOException ("Decoding EC private key failed. The version must be 1" );
190- }
191171 outEncodedStream .putInteger (tempVersion1 );
192172
193173 byte [] privateKeyBytes = inputDerValue [1 ].getOctetString ();
194174 outEncodedStream .putOctetString (privateKeyBytes );
195175
196176 byte [] encodedParams = this .getAlgorithmId ().getEncodedParams ();
197- if (inputDerValue .length > 2 ) {
198- if (inputDerValue [2 ].isContextSpecific (TAG_PARAMETERS_ATTRS )) {
199- DerInputStream paramDerInputStream = inputDerValue [2 ].getData ();
200- byte [] privateKeyParams = paramDerInputStream .toByteArray ();
201- // Check against the existing parameters created by PKCS8Key.
202- if (!Arrays .equals (privateKeyParams , encodedParams )) {
203- throw new IOException ("Decoding EC private key failed. The params are not the same as PKCS8Key's" );
204- }
205- } else if (!inputDerValue [2 ].isContextSpecific (TAG_PUBLIC_KEY_ATTRS )) {
206- // Unknown third+ element: we can throw, or ignore.
207- // Keeping old behavior would be to throw; but RFC allows only [0]/[1] here.
208- throw new IOException ("Decoding EC private key failed. Unexpected tagged field in ECPrivateKey" );
209- }
210- }
211177 // The native library needs the ASN.1 DER decoding of the private key to contain the parameters (i.e., the OID).
212178 outEncodedStream .write (
213179 DerValue .createTag (DerValue .TAG_CONTEXT , true , TAG_PARAMETERS_ATTRS ),
@@ -219,25 +185,69 @@ private byte[] createEncodedPrivateKeyWithParams() throws IOException {
219185 }
220186
221187 /**
222- * Parse the private key encoding to:
188+ * Check that the encoding is correct and at the same time
189+ * parse the private key encoding to:
223190 * - get the key and set it as a BigInteger (i.e., this.s)
191+ * - validate the parameters, if available
224192 * - get the public key, if available, and save its X.509 encoding
225193 *
226- * @throws IOException
194+ * @throws InvalidKeyException
227195 */
228- private void parsePrivateKeyEncoding () throws IOException {
229- DerInputStream privKeyBytesEncodedStream = new DerInputStream (this .privKeyMaterial );
230- DerValue [] inputDerValue = privKeyBytesEncodedStream .getSequence (4 );
196+ private void parsePrivateKeyEncoding () throws InvalidKeyException {
197+ // Parse private key material from PKCS8Key.decode()
198+ try {
199+ DerInputStream in = new DerInputStream (this .privKeyMaterial );
200+ DerValue derValue = in .getDerValue ();
201+ if (derValue .tag != DerValue .tag_Sequence ) {
202+ throw new IOException ("Not a SEQUENCE" );
203+ }
204+ DerInputStream data = derValue .data ;
205+ int version = data .getInteger ();
206+ if (version != 1 ) {
207+ throw new IOException ("Version must be 1" );
208+ }
209+ byte [] privData = data .getOctetString ();
210+ this .s = new BigInteger (1 , privData );
231211
232- byte [] privateKeyBytes = inputDerValue [1 ].getOctetString ();
233- this .s = new BigInteger (1 , privateKeyBytes );
212+ // Validate parameters stored from PKCS8Key.decode()
213+ AlgorithmParameters algParams = this .algid .getParameters ();
214+ if (algParams == null ) {
215+ throw new InvalidKeyException ("EC domain parameters must be "
216+ + "encoded in the algorithm identifier" );
217+ }
218+ this .params = algParams .getParameterSpec (ECParameterSpec .class );
219+
220+ if (data .available () == 0 ) {
221+ return ;
222+ }
223+
224+ DerValue value = data .getDerValue ();
225+ if (value .isContextSpecific (TAG_PARAMETERS_ATTRS )) {
226+ byte [] privateKeyParams = value .getDataBytes ();
227+ byte [] encodedParams = this .getAlgorithmId ().getEncodedParams ();
228+ // Check against the existing parameters created by PKCS8Key.
229+ if (!Arrays .equals (privateKeyParams , encodedParams )) {
230+ throw new InvalidKeyException ("Decoding EC private key failed. The params are not the same as PKCS8Key's" );
231+ }
232+ if (data .available () == 0 ) {
233+ return ;
234+ }
235+ value = data .getDerValue ();
236+ }
234237
235- for (int i = 2 ; i < inputDerValue .length ; i ++) {
236- DerValue v = inputDerValue [i ];
237- if (v .isContextSpecific (TAG_PUBLIC_KEY_ATTRS )) {
238- DerValue bits = v .withTag (DerValue .tag_BitString );
238+ if (value .isContextSpecific (TAG_PUBLIC_KEY_ATTRS )) {
239+ DerValue bits = value .withTag (DerValue .tag_BitString );
239240 this .pubKeyEncoded = new X509Key (this .algid , bits .data .getUnalignedBitString ()).getEncoded ();
241+ } else {
242+ throw new InvalidKeyException ("Unexpected value: " + value );
243+ }
244+
245+ if (data .available () != 0 ) {
246+ throw new InvalidKeyException ("Encoding has more than 4 values." );
240247 }
248+
249+ } catch (IOException | InvalidParameterSpecException e ) {
250+ throw new InvalidKeyException ("Invalid EC private key" , e );
241251 }
242252 }
243253
@@ -324,4 +334,39 @@ private void checkDestroyed() {
324334 throw new IllegalStateException ("This key is no longer valid" );
325335 }
326336 }
337+
338+ /**
339+ * Compares two private keys.
340+ *
341+ * The PKCS8Key.equals() method that compares encodings is used first.
342+ *
343+ * If that fails, we compare the private part of the key and the params to validate equivalence,
344+ * since the keys might be equal but have different encodings if one or more of the optional
345+ * parts are missing.
346+ *
347+ * @param object the object with which to compare
348+ * @return {@code true} if this key is equal to the object argument; {@code false} otherwise.
349+ */
350+ @ Override
351+ public boolean equals (Object object ) {
352+ boolean sameEncoding = super .equals (object );
353+ if (!sameEncoding ) {
354+ if (!(object instanceof java .security .interfaces .ECPrivateKey ecObj )) {
355+ return false ;
356+ }
357+
358+ // 1. Compare the secret scalar (S)
359+ if (!this .getS ().equals (ecObj .getS ())) {
360+ return false ;
361+ }
362+
363+ // 2. Compare the Curve Parameters
364+ ECParameterSpec s1 = this .getParams ();
365+ ECParameterSpec s2 = ecObj .getParams ();
366+
367+ return ECUtils .equals (s1 , s2 );
368+ }
369+
370+ return true ;
371+ }
327372}
0 commit comments