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