11/*
2- * Copyright (c) 1997, 2023 , Oracle and/or its affiliates. All rights reserved.
2+ * Copyright (c) 1997, 2024 , Oracle and/or its affiliates. All rights reserved.
33 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44 *
55 * This code is free software; you can redistribute it and/or modify it
@@ -56,6 +56,7 @@ final class DHPrivateKey implements PrivateKey,
5656 private BigInteger x ;
5757
5858 // the key bytes, without the algorithm information
59+ // cannot be final as it's re-assigned for deserialization
5960 private byte [] key ;
6061
6162 // the encoded key
@@ -70,118 +71,194 @@ final class DHPrivateKey implements PrivateKey,
7071 // the private-value length (optional)
7172 private int l ;
7273
73- /**
74- * Make a DH private key out of a private value <code>x</code>, a prime
75- * modulus <code>p</code>, and a base generator <code>g</code>.
76- *
77- * @param x the private value
78- * @param p the prime modulus
79- * @param g the base generator
80- */
81- DHPrivateKey (BigInteger x , BigInteger p , BigInteger g )
82- throws InvalidKeyException {
83- this (x , p , g , 0 );
84- }
85-
86- /**
87- * Make a DH private key out of a private value <code>x</code>, a prime
88- * modulus <code>p</code>, a base generator <code>g</code>, and a
89- * private-value length <code>l</code>.
90- *
91- * @param x the private value
92- * @param p the prime modulus
93- * @param g the base generator
94- * @param l the private-value length
95- */
96- DHPrivateKey (BigInteger x , BigInteger p , BigInteger g , int l ) {
97- this .x = x ;
98- this .p = p ;
99- this .g = g ;
100- this .l = l ;
101- byte [] xbytes = x .toByteArray ();
102- DerValue val = new DerValue (DerValue .tag_Integer , xbytes );
103- this .key = val .toByteArray ();
104- val .clear ();
105- Arrays .fill (xbytes , (byte ) 0 );
106- encode ();
74+ private static class DHComponents {
75+ final BigInteger x ;
76+ final BigInteger p ;
77+ final BigInteger g ;
78+ final int l ;
79+ final byte [] key ;
80+
81+ DHComponents (BigInteger x , BigInteger p , BigInteger g , int l ,
82+ byte [] key ) {
83+ this .x = x ;
84+ this .p = p ;
85+ this .g = g ;
86+ this .l = l ;
87+ this .key = key ;
88+ }
10789 }
10890
109- /**
110- * Make a DH private key from its DER encoding (PKCS #8).
111- *
112- * @param encodedKey the encoded key
113- *
114- * @throws InvalidKeyException if the encoded key does not represent
115- * a Diffie-Hellman private key
116- */
117- DHPrivateKey (byte [] encodedKey ) throws InvalidKeyException {
91+ // parses the specified encoding into a DHComponents object
92+ private static DHComponents decode (byte [] encodedKey )
93+ throws IOException {
11894 DerValue val = null ;
95+
11996 try {
12097 val = new DerValue (encodedKey );
12198 if (val .tag != DerValue .tag_Sequence ) {
122- throw new InvalidKeyException ("Key not a SEQUENCE" );
99+ throw new IOException ("Key not a SEQUENCE" );
123100 }
124101
125- //
126102 // version
127- //
128103 BigInteger parsedVersion = val .data .getBigInteger ();
129104 if (!parsedVersion .equals (PKCS8_VERSION )) {
130105 throw new IOException ("version mismatch: (supported: " +
131- PKCS8_VERSION + ", parsed: " +
132- parsedVersion );
106+ PKCS8_VERSION + ", parsed: " + parsedVersion );
133107 }
134108
135- //
136109 // privateKeyAlgorithm
137- //
138110 DerValue algid = val .data .getDerValue ();
139111 if (algid .tag != DerValue .tag_Sequence ) {
140- throw new InvalidKeyException ("AlgId is not a SEQUENCE" );
112+ throw new IOException ("AlgId is not a SEQUENCE" );
141113 }
142114 DerInputStream derInStream = algid .toDerInputStream ();
143115 ObjectIdentifier oid = derInStream .getOID ();
144116 if (oid == null ) {
145- throw new InvalidKeyException ("Null OID" );
117+ throw new IOException ("Null OID" );
146118 }
147119 if (derInStream .available () == 0 ) {
148- throw new InvalidKeyException ("Parameters missing" );
120+ throw new IOException ("Parameters missing" );
149121 }
150122 // parse the parameters
151123 DerValue params = derInStream .getDerValue ();
152124 if (params .tag == DerValue .tag_Null ) {
153- throw new InvalidKeyException ("Null parameters" );
125+ throw new IOException ("Null parameters" );
154126 }
155127 if (params .tag != DerValue .tag_Sequence ) {
156- throw new InvalidKeyException ("Parameters not a SEQUENCE" );
128+ throw new IOException ("Parameters not a SEQUENCE" );
157129 }
158130 params .data .reset ();
159- this . p = params .data .getBigInteger ();
160- this . g = params .data .getBigInteger ();
131+ BigInteger p = params .data .getBigInteger ();
132+ BigInteger g = params .data .getBigInteger ();
161133 // Private-value length is OPTIONAL
134+ int l = (params .data .available () != 0 ?
135+ params .data .getInteger () : 0 );
136+ // should have no trailing data
162137 if (params .data .available () != 0 ) {
163- this .l = params .data .getInteger ();
164- }
165- if (params .data .available () != 0 ) {
166- throw new InvalidKeyException ("Extra parameter data" );
138+ throw new IOException ("Extra parameter data" );
167139 }
168140
169- //
170141 // privateKey
171- //
172- this . key = val . data . getOctetString ( );
173- parseKeyBits ();
142+ byte [] key = val . data . getOctetString ();
143+ DerInputStream in = new DerInputStream ( key );
144+ BigInteger x = in . getBigInteger ();
174145
175- this .encodedKey = encodedKey .clone ();
176- } catch (IOException | NumberFormatException e ) {
177- throw new InvalidKeyException ("Error parsing key encoding" , e );
146+ // should have no trailing data
147+ if (val .data .available () != 0 ) {
148+ throw new IOException ("Excess trailing data" );
149+ }
150+ return new DHComponents (x , p , g , l , key );
151+ } catch (NumberFormatException e ) {
152+ throw new IOException ("Error parsing key encoding" , e );
178153 } finally {
179154 if (val != null ) {
180155 val .clear ();
181156 }
182157 }
183158 }
184159
160+ // Generates the ASN.1 encoding
161+ private static byte [] encode (BigInteger p , BigInteger g , int l ,
162+ byte [] key ) {
163+ DerOutputStream tmp = new DerOutputStream ();
164+
165+ // version
166+ tmp .putInteger (PKCS8_VERSION );
167+
168+ // privateKeyAlgorithm
169+ DerOutputStream algid = new DerOutputStream ();
170+
171+ // store OID
172+ algid .putOID (DHPublicKey .DH_OID );
173+ // encode parameters
174+ DerOutputStream params = new DerOutputStream ();
175+ params .putInteger (p );
176+ params .putInteger (g );
177+ if (l != 0 ) {
178+ params .putInteger (l );
179+ }
180+ // wrap parameters into SEQUENCE
181+ DerValue paramSequence = new DerValue (DerValue .tag_Sequence ,
182+ params .toByteArray ());
183+ // store parameter SEQUENCE in algid
184+ algid .putDerValue (paramSequence );
185+ // wrap algid into SEQUENCE
186+ tmp .write (DerValue .tag_Sequence , algid );
187+
188+ // privateKey
189+ tmp .putOctetString (key );
190+
191+ // make it a SEQUENCE
192+ DerValue val = DerValue .wrap (DerValue .tag_Sequence , tmp );
193+ byte [] encoded = val .toByteArray ();
194+ val .clear ();
195+
196+ return encoded ;
197+ }
198+
199+ /**
200+ * Make a DH private key out of a private value <code>x</code>, a prime
201+ * modulus <code>p</code>, and a base generator <code>g</code>.
202+ *
203+ * @param x the private value
204+ * @param p the prime modulus
205+ * @param g the base generator
206+ */
207+ DHPrivateKey (BigInteger x , BigInteger p , BigInteger g )
208+ throws InvalidKeyException {
209+ this (x , p , g , 0 );
210+ }
211+
212+ /**
213+ * Make a DH private key out of a private value <code>x</code>, a prime
214+ * modulus <code>p</code>, a base generator <code>g</code>, and a
215+ * private-value length <code>l</code>.
216+ *
217+ * @param x the private value
218+ * @param p the prime modulus
219+ * @param g the base generator
220+ * @param l the private-value length
221+ */
222+ DHPrivateKey (BigInteger x , BigInteger p , BigInteger g , int l ) {
223+ this .x = x ;
224+ this .p = p ;
225+ this .g = g ;
226+ this .l = l ;
227+
228+ byte [] xbytes = x .toByteArray ();
229+ DerValue val = new DerValue (DerValue .tag_Integer , xbytes );
230+ try {
231+ this .key = val .toByteArray ();
232+ } finally {
233+ val .clear ();
234+ Arrays .fill (xbytes , (byte ) 0 );
235+ }
236+ this .encodedKey = encode (p , g , l , key );
237+ }
238+
239+ /**
240+ * Make a DH private key from its DER encoding (PKCS #8).
241+ *
242+ * @param encodedKey the encoded key
243+ *
244+ * @throws InvalidKeyException if the encoded key does not represent
245+ * a Diffie-Hellman private key
246+ */
247+ DHPrivateKey (byte [] encodedKey ) throws InvalidKeyException {
248+ this .encodedKey = encodedKey .clone ();
249+ DHComponents dc ;
250+ try {
251+ dc = decode (this .encodedKey );
252+ } catch (IOException e ) {
253+ throw new InvalidKeyException ("Invalid encoding" , e );
254+ }
255+ this .x = dc .x ;
256+ this .p = dc .p ;
257+ this .g = dc .g ;
258+ this .l = dc .l ;
259+ this .key = dc .key ;
260+ }
261+
185262 /**
186263 * Returns the encoding format of this key: "PKCS#8"
187264 */
@@ -200,55 +277,9 @@ public String getAlgorithm() {
200277 * Get the encoding of the key.
201278 */
202279 public synchronized byte [] getEncoded () {
203- encode ();
204280 return encodedKey .clone ();
205281 }
206282
207- /**
208- * Generate the encodedKey field if it has not been calculated.
209- * Could generate null.
210- */
211- private void encode () {
212- if (this .encodedKey == null ) {
213- DerOutputStream tmp = new DerOutputStream ();
214-
215- //
216- // version
217- //
218- tmp .putInteger (PKCS8_VERSION );
219-
220- //
221- // privateKeyAlgorithm
222- //
223- DerOutputStream algid = new DerOutputStream ();
224-
225- // store OID
226- algid .putOID (DHPublicKey .DH_OID );
227- // encode parameters
228- DerOutputStream params = new DerOutputStream ();
229- params .putInteger (this .p );
230- params .putInteger (this .g );
231- if (this .l != 0 ) {
232- params .putInteger (this .l );
233- }
234- // wrap parameters into SEQUENCE
235- DerValue paramSequence = new DerValue (DerValue .tag_Sequence ,
236- params .toByteArray ());
237- // store parameter SEQUENCE in algid
238- algid .putDerValue (paramSequence );
239- // wrap algid into SEQUENCE
240- tmp .write (DerValue .tag_Sequence , algid );
241-
242- // privateKey
243- tmp .putOctetString (this .key );
244-
245- // make it a SEQUENCE
246- DerValue val = DerValue .wrap (DerValue .tag_Sequence , tmp );
247- this .encodedKey = val .toByteArray ();
248- val .clear ();
249- }
250- }
251-
252283 /**
253284 * Returns the private value, <code>x</code>.
254285 *
@@ -271,16 +302,6 @@ public DHParameterSpec getParams() {
271302 }
272303 }
273304
274- private void parseKeyBits () throws InvalidKeyException {
275- try {
276- DerInputStream in = new DerInputStream (this .key );
277- this .x = in .getBigInteger ();
278- } catch (IOException e ) {
279- throw new InvalidKeyException (
280- "Error parsing key encoding: " + e .getMessage (), e );
281- }
282- }
283-
284305 /**
285306 * Calculates a hash code value for the object.
286307 * Objects that are equal will also have the same hashcode.
@@ -313,10 +334,7 @@ public boolean equals(Object obj) {
313334 */
314335 @ java .io .Serial
315336 private Object writeReplace () throws java .io .ObjectStreamException {
316- encode ();
317- return new KeyRep (KeyRep .Type .PRIVATE ,
318- getAlgorithm (),
319- getFormat (),
337+ return new KeyRep (KeyRep .Type .PRIVATE , getAlgorithm (), getFormat (),
320338 encodedKey );
321339 }
322340
@@ -336,11 +354,28 @@ private void readObject(ObjectInputStream stream)
336354 if ((key == null ) || (key .length == 0 )) {
337355 throw new InvalidObjectException ("key not deserializable" );
338356 }
339- this .key = key .clone ();
340357 if ((encodedKey == null ) || (encodedKey .length == 0 )) {
341358 throw new InvalidObjectException (
342359 "encoded key not deserializable" );
343360 }
344- this .encodedKey = encodedKey .clone ();
361+ // check if the "encodedKey" value matches the deserialized fields
362+ DHComponents c ;
363+ byte [] encodedKeyIntern = encodedKey .clone ();
364+ try {
365+ c = decode (encodedKeyIntern );
366+ } catch (IOException e ) {
367+ throw new InvalidObjectException ("Invalid encoding" , e );
368+ }
369+ if (!Arrays .equals (c .key , key ) || !c .x .equals (x ) || !c .p .equals (p )
370+ || !c .g .equals (g ) || c .l != l ) {
371+ throw new InvalidObjectException (
372+ "encoded key not matching internal fields" );
373+ }
374+ // zero out external arrays
375+ Arrays .fill (key , (byte )0x00 );
376+ Arrays .fill (encodedKey , (byte )0x00 );
377+ // use self-created internal copies
378+ this .key = c .key ;
379+ this .encodedKey = encodedKeyIntern ;
345380 }
346381}
0 commit comments