Skip to content

Commit ae0177b

Browse files
committed
8302111: Serialization considerations
Reviewed-by: rrich Backport-of: 369c573383a0120e0d85aeb89a211f38b5261013
1 parent 117ce56 commit ae0177b

File tree

21 files changed

+754
-491
lines changed

21 files changed

+754
-491
lines changed

src/java.base/share/classes/com/sun/crypto/provider/DHPrivateKey.java

Lines changed: 171 additions & 138 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
@@ -57,6 +57,7 @@ final class DHPrivateKey implements PrivateKey,
5757
private BigInteger x;
5858

5959
// the key bytes, without the algorithm information
60+
// cannot be final as it's re-assigned for deserialization
6061
private byte[] key;
6162

6263
// the encoded key
@@ -71,6 +72,135 @@ final class DHPrivateKey implements PrivateKey,
7172
// the private-value length (optional)
7273
private int l;
7374

75+
private static class DHComponents {
76+
final BigInteger x;
77+
final BigInteger p;
78+
final BigInteger g;
79+
final int l;
80+
final byte[] key;
81+
82+
DHComponents(BigInteger x, BigInteger p, BigInteger g, int l,
83+
byte[] key) {
84+
this.x = x;
85+
this.p = p;
86+
this.g = g;
87+
this.l = l;
88+
this.key = key;
89+
}
90+
}
91+
92+
// parses the specified encoding into a DHComponents object
93+
private static DHComponents decode(byte[] encodedKey)
94+
throws IOException {
95+
DerValue val = null;
96+
97+
try {
98+
val = new DerValue(encodedKey);
99+
if (val.tag != DerValue.tag_Sequence) {
100+
throw new IOException("Key not a SEQUENCE");
101+
}
102+
103+
// version
104+
BigInteger parsedVersion = val.data.getBigInteger();
105+
if (!parsedVersion.equals(PKCS8_VERSION)) {
106+
throw new IOException("version mismatch: (supported: " +
107+
PKCS8_VERSION + ", parsed: " + parsedVersion);
108+
}
109+
110+
// privateKeyAlgorithm
111+
DerValue algid = val.data.getDerValue();
112+
if (algid.tag != DerValue.tag_Sequence) {
113+
throw new IOException("AlgId is not a SEQUENCE");
114+
}
115+
DerInputStream derInStream = algid.toDerInputStream();
116+
ObjectIdentifier oid = derInStream.getOID();
117+
if (oid == null) {
118+
throw new IOException("Null OID");
119+
}
120+
if (derInStream.available() == 0) {
121+
throw new IOException("Parameters missing");
122+
}
123+
// parse the parameters
124+
DerValue params = derInStream.getDerValue();
125+
if (params.tag == DerValue.tag_Null) {
126+
throw new IOException("Null parameters");
127+
}
128+
if (params.tag != DerValue.tag_Sequence) {
129+
throw new IOException("Parameters not a SEQUENCE");
130+
}
131+
params.data.reset();
132+
BigInteger p = params.data.getBigInteger();
133+
BigInteger g = params.data.getBigInteger();
134+
// Private-value length is OPTIONAL
135+
int l = (params.data.available() != 0 ?
136+
params.data.getInteger() : 0);
137+
// should have no trailing data
138+
if (params.data.available() != 0) {
139+
throw new IOException("Extra parameter data");
140+
}
141+
142+
// privateKey
143+
byte[] key = val.data.getOctetString();
144+
DerInputStream in = new DerInputStream(key);
145+
BigInteger x = in.getBigInteger();
146+
147+
// should have no trailing data
148+
if (val.data.available() != 0) {
149+
throw new IOException("Excess trailing data");
150+
}
151+
return new DHComponents(x, p, g, l, key);
152+
} catch (NumberFormatException e) {
153+
throw new IOException("Error parsing key encoding", e);
154+
} finally {
155+
if (val != null) {
156+
val.clear();
157+
}
158+
}
159+
}
160+
161+
// Generates the ASN.1 encoding
162+
private static byte[] encode(BigInteger p, BigInteger g, int l,
163+
byte[] key) {
164+
try {
165+
DerOutputStream tmp = new DerOutputStream();
166+
167+
// version
168+
tmp.putInteger(PKCS8_VERSION);
169+
170+
// privateKeyAlgorithm
171+
DerOutputStream algid = new DerOutputStream();
172+
173+
// store OID
174+
algid.putOID(DHPublicKey.DH_OID);
175+
// encode parameters
176+
DerOutputStream params = new DerOutputStream();
177+
params.putInteger(p);
178+
params.putInteger(g);
179+
if (l != 0) {
180+
params.putInteger(l);
181+
}
182+
// wrap parameters into SEQUENCE
183+
DerValue paramSequence = new DerValue(DerValue.tag_Sequence,
184+
params.toByteArray());
185+
// store parameter SEQUENCE in algid
186+
algid.putDerValue(paramSequence);
187+
// wrap algid into SEQUENCE
188+
tmp.write(DerValue.tag_Sequence, algid);
189+
190+
// privateKey
191+
tmp.putOctetString(key);
192+
193+
// make it a SEQUENCE
194+
DerValue val = DerValue.wrap(DerValue.tag_Sequence, tmp);
195+
byte[] encoded = val.toByteArray();
196+
val.clear();
197+
198+
return encoded;
199+
} catch (IOException e) {
200+
throw new AssertionError(e);
201+
}
202+
}
203+
74204
/**
75205
* Make a DH private key out of a private value <code>x</code>, a prime
76206
* modulus <code>p</code>, and a base generator <code>g</code>.
@@ -82,7 +212,7 @@ final class DHPrivateKey implements PrivateKey,
82212
* @throws ProviderException if the key cannot be encoded
83213
*/
84214
DHPrivateKey(BigInteger x, BigInteger p, BigInteger g)
85-
throws InvalidKeyException {
215+
throws InvalidKeyException {
86216
this(x, p, g, 0);
87217
}
88218

@@ -103,16 +233,18 @@ final class DHPrivateKey implements PrivateKey,
103233
this.p = p;
104234
this.g = g;
105235
this.l = l;
236+
237+
byte[] xbytes = x.toByteArray();
238+
DerValue val = new DerValue(DerValue.tag_Integer, xbytes);
106239
try {
107-
byte[] xbytes = x.toByteArray();
108-
DerValue val = new DerValue(DerValue.tag_Integer, xbytes);
109240
this.key = val.toByteArray();
110-
val.clear();
111-
Arrays.fill(xbytes, (byte)0);
112-
encode();
113241
} catch (IOException e) {
114242
throw new ProviderException("Cannot produce ASN.1 encoding", e);
243+
} finally {
244+
val.clear();
245+
Arrays.fill(xbytes, (byte) 0);
115246
}
247+
this.encodedKey = encode(p, g, l, key);
116248
}
117249

118250
/**
@@ -124,71 +256,18 @@ final class DHPrivateKey implements PrivateKey,
124256
* a Diffie-Hellman private key
125257
*/
126258
DHPrivateKey(byte[] encodedKey) throws InvalidKeyException {
127-
DerValue val = null;
259+
this.encodedKey = encodedKey.clone();
260+
DHComponents dc;
128261
try {
129-
val = new DerValue(encodedKey);
130-
if (val.tag != DerValue.tag_Sequence) {
131-
throw new InvalidKeyException ("Key not a SEQUENCE");
132-
}
133-
134-
//
135-
// version
136-
//
137-
BigInteger parsedVersion = val.data.getBigInteger();
138-
if (!parsedVersion.equals(PKCS8_VERSION)) {
139-
throw new IOException("version mismatch: (supported: " +
140-
PKCS8_VERSION + ", parsed: " +
141-
parsedVersion);
142-
}
143-
144-
//
145-
// privateKeyAlgorithm
146-
//
147-
DerValue algid = val.data.getDerValue();
148-
if (algid.tag != DerValue.tag_Sequence) {
149-
throw new InvalidKeyException("AlgId is not a SEQUENCE");
150-
}
151-
DerInputStream derInStream = algid.toDerInputStream();
152-
ObjectIdentifier oid = derInStream.getOID();
153-
if (oid == null) {
154-
throw new InvalidKeyException("Null OID");
155-
}
156-
if (derInStream.available() == 0) {
157-
throw new InvalidKeyException("Parameters missing");
158-
}
159-
// parse the parameters
160-
DerValue params = derInStream.getDerValue();
161-
if (params.tag == DerValue.tag_Null) {
162-
throw new InvalidKeyException("Null parameters");
163-
}
164-
if (params.tag != DerValue.tag_Sequence) {
165-
throw new InvalidKeyException("Parameters not a SEQUENCE");
166-
}
167-
params.data.reset();
168-
this.p = params.data.getBigInteger();
169-
this.g = params.data.getBigInteger();
170-
// Private-value length is OPTIONAL
171-
if (params.data.available() != 0) {
172-
this.l = params.data.getInteger();
173-
}
174-
if (params.data.available() != 0) {
175-
throw new InvalidKeyException("Extra parameter data");
176-
}
177-
178-
//
179-
// privateKey
180-
//
181-
this.key = val.data.getOctetString();
182-
parseKeyBits();
183-
184-
this.encodedKey = encodedKey.clone();
185-
} catch (IOException | NumberFormatException e) {
186-
throw new InvalidKeyException("Error parsing key encoding", e);
187-
} finally {
188-
if (val != null) {
189-
val.clear();
190-
}
262+
dc = decode(this.encodedKey);
263+
} catch (IOException e) {
264+
throw new InvalidKeyException("Invalid encoding", e);
191265
}
266+
this.x = dc.x;
267+
this.p = dc.p;
268+
this.g = dc.g;
269+
this.l = dc.l;
270+
this.key = dc.key;
192271
}
193272

194273
/**
@@ -209,59 +288,9 @@ public String getAlgorithm() {
209288
* Get the encoding of the key.
210289
*/
211290
public synchronized byte[] getEncoded() {
212-
encode();
213291
return encodedKey.clone();
214292
}
215293

216-
/**
217-
* Generate the encodedKey field if it has not been calculated.
218-
* Could generate null.
219-
*/
220-
private void encode() {
221-
if (this.encodedKey == null) {
222-
try {
223-
DerOutputStream tmp = new DerOutputStream();
224-
225-
//
226-
// version
227-
//
228-
tmp.putInteger(PKCS8_VERSION);
229-
230-
//
231-
// privateKeyAlgorithm
232-
//
233-
DerOutputStream algid = new DerOutputStream();
234-
235-
// store OID
236-
algid.putOID(DHPublicKey.DH_OID);
237-
// encode parameters
238-
DerOutputStream params = new DerOutputStream();
239-
params.putInteger(this.p);
240-
params.putInteger(this.g);
241-
if (this.l != 0) {
242-
params.putInteger(this.l);
243-
}
244-
// wrap parameters into SEQUENCE
245-
DerValue paramSequence = new DerValue(DerValue.tag_Sequence,
246-
params.toByteArray());
247-
// store parameter SEQUENCE in algid
248-
algid.putDerValue(paramSequence);
249-
// wrap algid into SEQUENCE
250-
tmp.write(DerValue.tag_Sequence, algid);
251-
252-
// privateKey
253-
tmp.putOctetString(this.key);
254-
255-
// make it a SEQUENCE
256-
DerValue val = DerValue.wrap(DerValue.tag_Sequence, tmp);
257-
this.encodedKey = val.toByteArray();
258-
val.clear();
259-
} catch (IOException e) {
260-
throw new AssertionError(e);
261-
}
262-
}
263-
}
264-
265294
/**
266295
* Returns the private value, <code>x</code>.
267296
*
@@ -284,18 +313,6 @@ public DHParameterSpec getParams() {
284313
}
285314
}
286315

287-
private void parseKeyBits() throws InvalidKeyException {
288-
try {
289-
DerInputStream in = new DerInputStream(this.key);
290-
this.x = in.getBigInteger();
291-
} catch (IOException e) {
292-
InvalidKeyException ike = new InvalidKeyException(
293-
"Error parsing key encoding: " + e.getMessage());
294-
ike.initCause(e);
295-
throw ike;
296-
}
297-
}
298-
299316
/**
300317
* Calculates a hash code value for the object.
301318
* Objects that are equal will also have the same hashcode.
@@ -328,10 +345,7 @@ public boolean equals(Object obj) {
328345
*/
329346
@java.io.Serial
330347
private Object writeReplace() throws java.io.ObjectStreamException {
331-
encode();
332-
return new KeyRep(KeyRep.Type.PRIVATE,
333-
getAlgorithm(),
334-
getFormat(),
348+
return new KeyRep(KeyRep.Type.PRIVATE, getAlgorithm(), getFormat(),
335349
encodedKey);
336350
}
337351

@@ -351,11 +365,30 @@ private void readObject(ObjectInputStream stream)
351365
if ((key == null) || (key.length == 0)) {
352366
throw new InvalidObjectException("key not deserializable");
353367
}
354-
this.key = key.clone();
355368
if ((encodedKey == null) || (encodedKey.length == 0)) {
356369
throw new InvalidObjectException(
357370
"encoded key not deserializable");
358371
}
359-
this.encodedKey = encodedKey.clone();
372+
// check if the "encodedKey" value matches the deserialized fields
373+
DHComponents c;
374+
byte[] encodedKeyIntern = encodedKey.clone();
375+
try {
376+
c = decode(encodedKeyIntern);
377+
} catch (IOException e) {
378+
InvalidObjectException ioe = new InvalidObjectException("Invalid encoding");
379+
ioe.initCause(e);
380+
throw ioe;
381+
}
382+
if (!Arrays.equals(c.key, key) || !c.x.equals(x) || !c.p.equals(p)
383+
|| !c.g.equals(g) || c.l != l) {
384+
throw new InvalidObjectException(
385+
"encoded key not matching internal fields");
386+
}
387+
// zero out external arrays
388+
Arrays.fill(key, (byte)0x00);
389+
Arrays.fill(encodedKey, (byte)0x00);
390+
// use self-created internal copies
391+
this.key = c.key;
392+
this.encodedKey = encodedKeyIntern;
360393
}
361394
}

0 commit comments

Comments
 (0)