Skip to content

Commit 17cb53a

Browse files
committed
8302111: Serialization considerations
Reviewed-by: andrew Backport-of: 369c573383a0120e0d85aeb89a211f38b5261013
1 parent 072651a commit 17cb53a

File tree

21 files changed

+748
-480
lines changed

21 files changed

+748
-480
lines changed
Lines changed: 166 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
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

Comments
 (0)