1 /*
2  * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package com.sun.crypto.provider;
27 
28 import java.io.*;
29 import java.util.Objects;
30 import java.math.BigInteger;
31 import java.security.KeyRep;
32 import java.security.InvalidKeyException;
33 import java.security.ProviderException;
34 import java.security.PublicKey;
35 import javax.crypto.spec.DHParameterSpec;
36 import sun.security.util.*;
37 
38 
39 /**
40  * A public key in X.509 format for the Diffie-Hellman key agreement algorithm.
41  *
42  * @author Jan Luehe
43  *
44  *
45  * @see DHPrivateKey
46  * @see javax.crypto.KeyAgreement
47  */
48 final class DHPublicKey implements PublicKey,
49 javax.crypto.interfaces.DHPublicKey, Serializable {
50 
51     static final long serialVersionUID = 7647557958927458271L;
52 
53     // the public key
54     private BigInteger y;
55 
56     // the key bytes, without the algorithm information
57     private byte[] key;
58 
59     // the encoded key
60     private byte[] encodedKey;
61 
62     // the prime modulus
63     private BigInteger p;
64 
65     // the base generator
66     private BigInteger g;
67 
68     // the private-value length (optional)
69     private int l;
70 
71     private int DH_data[] = { 1, 2, 840, 113549, 1, 3, 1 };
72 
73     /**
74      * Make a DH public key out of a public value <code>y</code>, a prime
75      * modulus <code>p</code>, and a base generator <code>g</code>.
76      *
77      * @param y the public value
78      * @param p the prime modulus
79      * @param g the base generator
80      *
81      * @exception InvalidKeyException if the key cannot be encoded
82      */
DHPublicKey(BigInteger y, BigInteger p, BigInteger g)83     DHPublicKey(BigInteger y, BigInteger p, BigInteger g)
84         throws InvalidKeyException {
85         this(y, p, g, 0);
86     }
87 
88     /**
89      * Make a DH public key out of a public value <code>y</code>, a prime
90      * modulus <code>p</code>, a base generator <code>g</code>, and a
91      * private-value length <code>l</code>.
92      *
93      * @param y the public value
94      * @param p the prime modulus
95      * @param g the base generator
96      * @param l the private-value length
97      *
98      * @exception ProviderException if the key cannot be encoded
99      */
DHPublicKey(BigInteger y, BigInteger p, BigInteger g, int l)100     DHPublicKey(BigInteger y, BigInteger p, BigInteger g, int l) {
101         this.y = y;
102         this.p = p;
103         this.g = g;
104         this.l = l;
105         try {
106             this.key = new DerValue(DerValue.tag_Integer,
107                                     this.y.toByteArray()).toByteArray();
108             this.encodedKey = getEncoded();
109         } catch (IOException e) {
110             throw new ProviderException("Cannot produce ASN.1 encoding", e);
111         }
112     }
113 
114     /**
115      * Make a DH public key from its DER encoding (X.509).
116      *
117      * @param encodedKey the encoded key
118      *
119      * @exception InvalidKeyException if the encoded key does not represent
120      * a Diffie-Hellman public key
121      */
DHPublicKey(byte[] encodedKey)122     DHPublicKey(byte[] encodedKey) throws InvalidKeyException {
123         InputStream inStream = new ByteArrayInputStream(encodedKey);
124         try {
125             DerValue derKeyVal = new DerValue(inStream);
126             if (derKeyVal.tag != DerValue.tag_Sequence) {
127                 throw new InvalidKeyException ("Invalid key format");
128             }
129 
130             /*
131              * Parse the algorithm identifier
132              */
133             DerValue algid = derKeyVal.data.getDerValue();
134             if (algid.tag != DerValue.tag_Sequence) {
135                 throw new InvalidKeyException("AlgId is not a SEQUENCE");
136             }
137             DerInputStream derInStream = algid.toDerInputStream();
138             ObjectIdentifier oid = derInStream.getOID();
139             if (oid == null) {
140                 throw new InvalidKeyException("Null OID");
141             }
142             if (derInStream.available() == 0) {
143                 throw new InvalidKeyException("Parameters missing");
144             }
145 
146             /*
147              * Parse the parameters
148              */
149             DerValue params = derInStream.getDerValue();
150             if (params.tag == DerValue.tag_Null) {
151                 throw new InvalidKeyException("Null parameters");
152             }
153             if (params.tag != DerValue.tag_Sequence) {
154                 throw new InvalidKeyException("Parameters not a SEQUENCE");
155             }
156             params.data.reset();
157             this.p = params.data.getBigInteger();
158             this.g = params.data.getBigInteger();
159             // Private-value length is OPTIONAL
160             if (params.data.available() != 0) {
161                 this.l = params.data.getInteger();
162             }
163             if (params.data.available() != 0) {
164                 throw new InvalidKeyException("Extra parameter data");
165             }
166 
167             /*
168              * Parse the key
169              */
170             this.key = derKeyVal.data.getBitString();
171             parseKeyBits();
172             if (derKeyVal.data.available() != 0) {
173                 throw new InvalidKeyException("Excess key data");
174             }
175 
176             this.encodedKey = encodedKey.clone();
177         } catch (IOException | NumberFormatException e) {
178             throw new InvalidKeyException("Error parsing key encoding", e);
179         }
180     }
181 
182     /**
183      * Returns the encoding format of this key: "X.509"
184      */
getFormat()185     public String getFormat() {
186         return "X.509";
187     }
188 
189     /**
190      * Returns the name of the algorithm associated with this key: "DH"
191      */
getAlgorithm()192     public String getAlgorithm() {
193         return "DH";
194     }
195 
196     /**
197      * Get the encoding of the key.
198      */
getEncoded()199     public synchronized byte[] getEncoded() {
200         if (this.encodedKey == null) {
201             try {
202                 DerOutputStream algid = new DerOutputStream();
203 
204                 // store oid in algid
205                 algid.putOID(new ObjectIdentifier(DH_data));
206 
207                 // encode parameters
208                 DerOutputStream params = new DerOutputStream();
209                 params.putInteger(this.p);
210                 params.putInteger(this.g);
211                 if (this.l != 0) {
212                     params.putInteger(this.l);
213                 }
214                 // wrap parameters into SEQUENCE
215                 DerValue paramSequence = new DerValue(DerValue.tag_Sequence,
216                                                       params.toByteArray());
217                 // store parameter SEQUENCE in algid
218                 algid.putDerValue(paramSequence);
219 
220                 // wrap algid into SEQUENCE, and store it in key encoding
221                 DerOutputStream tmpDerKey = new DerOutputStream();
222                 tmpDerKey.write(DerValue.tag_Sequence, algid);
223 
224                 // store key data
225                 tmpDerKey.putBitString(this.key);
226 
227                 // wrap algid and key into SEQUENCE
228                 DerOutputStream derKey = new DerOutputStream();
229                 derKey.write(DerValue.tag_Sequence, tmpDerKey);
230                 this.encodedKey = derKey.toByteArray();
231             } catch (IOException e) {
232                 return null;
233             }
234         }
235         return this.encodedKey.clone();
236     }
237 
238     /**
239      * Returns the public value, <code>y</code>.
240      *
241      * @return the public value, <code>y</code>
242      */
getY()243     public BigInteger getY() {
244         return this.y;
245     }
246 
247     /**
248      * Returns the key parameters.
249      *
250      * @return the key parameters
251      */
getParams()252     public DHParameterSpec getParams() {
253         if (this.l != 0) {
254             return new DHParameterSpec(this.p, this.g, this.l);
255         } else {
256             return new DHParameterSpec(this.p, this.g);
257         }
258     }
259 
toString()260     public String toString() {
261         String LINE_SEP = System.lineSeparator();
262 
263         StringBuilder sb
264             = new StringBuilder("SunJCE Diffie-Hellman Public Key:"
265                                + LINE_SEP + "y:" + LINE_SEP
266                                + Debug.toHexString(this.y)
267                                + LINE_SEP + "p:" + LINE_SEP
268                                + Debug.toHexString(this.p)
269                                + LINE_SEP + "g:" + LINE_SEP
270                                + Debug.toHexString(this.g));
271         if (this.l != 0)
272             sb.append(LINE_SEP + "l:" + LINE_SEP + "    " + this.l);
273         return sb.toString();
274     }
275 
parseKeyBits()276     private void parseKeyBits() throws InvalidKeyException {
277         try {
278             DerInputStream in = new DerInputStream(this.key);
279             this.y = in.getBigInteger();
280         } catch (IOException e) {
281             throw new InvalidKeyException(
282                 "Error parsing key encoding: " + e.toString());
283         }
284     }
285 
286     /**
287      * Calculates a hash code value for the object.
288      * Objects that are equal will also have the same hashcode.
289      */
hashCode()290     public int hashCode() {
291         return Objects.hash(y, p, g);
292     }
293 
equals(Object obj)294     public boolean equals(Object obj) {
295         if (this == obj) return true;
296 
297         if (!(obj instanceof javax.crypto.interfaces.DHPublicKey)) {
298             return false;
299         }
300 
301         javax.crypto.interfaces.DHPublicKey other =
302             (javax.crypto.interfaces.DHPublicKey) obj;
303         DHParameterSpec otherParams = other.getParams();
304         return ((this.y.compareTo(other.getY()) == 0) &&
305                 (this.p.compareTo(otherParams.getP()) == 0) &&
306                 (this.g.compareTo(otherParams.getG()) == 0));
307     }
308 
309     /**
310      * Replace the DH public key to be serialized.
311      *
312      * @return the standard KeyRep object to be serialized
313      *
314      * @throws java.io.ObjectStreamException if a new object representing
315      * this DH public key could not be created
316      */
writeReplace()317     private Object writeReplace() throws java.io.ObjectStreamException {
318         return new KeyRep(KeyRep.Type.PUBLIC,
319                         getAlgorithm(),
320                         getFormat(),
321                         getEncoded());
322     }
323 }
324