1 /* 2 * Copyright (c) 1997, 2011, 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.math.BigInteger; 30 import java.security.KeyRep; 31 import java.security.InvalidKeyException; 32 import java.security.ProviderException; 33 import java.security.PublicKey; 34 import javax.crypto.spec.DHParameterSpec; 35 import sun.security.util.*; 36 37 38 /** 39 * A public key in X.509 format for the Diffie-Hellman key agreement algorithm. 40 * 41 * @author Jan Luehe 42 * 43 * 44 * @see DHPrivateKey 45 * @see java.security.KeyAgreement 46 */ 47 final class DHPublicKey implements PublicKey, 48 javax.crypto.interfaces.DHPublicKey, Serializable { 49 50 static final long serialVersionUID = 7647557958927458271L; 51 52 // the public key 53 private BigInteger y; 54 55 // the key bytes, without the algorithm information 56 private byte[] key; 57 58 // the encoded key 59 private byte[] encodedKey; 60 61 // the prime modulus 62 private BigInteger p; 63 64 // the base generator 65 private BigInteger g; 66 67 // the private-value length 68 private int l; 69 70 private int DH_data[] = { 1, 2, 840, 113549, 1, 3, 1 }; 71 72 /** 73 * Make a DH public key out of a public value <code>y</code>, a prime 74 * modulus <code>p</code>, and a base generator <code>g</code>. 75 * 76 * @param y the public value 77 * @param p the prime modulus 78 * @param g the base generator 79 * 80 * @exception InvalidKeyException if the key cannot be encoded 81 */ 82 DHPublicKey(BigInteger y, BigInteger p, BigInteger g) 83 throws InvalidKeyException { 84 this(y, p, g, 0); 85 } 86 87 /** 88 * Make a DH public key out of a public value <code>y</code>, a prime 89 * modulus <code>p</code>, a base generator <code>g</code>, and a 90 * private-value length <code>l</code>. 91 * 92 * @param y the public value 93 * @param p the prime modulus 94 * @param g the base generator 95 * @param l the private-value length 96 * 97 * @exception ProviderException if the key cannot be encoded 98 */ 99 DHPublicKey(BigInteger y, BigInteger p, BigInteger g, int l) { 100 this.y = y; 101 this.p = p; 102 this.g = g; 103 this.l = l; 104 try { 105 this.key = new DerValue(DerValue.tag_Integer, 106 this.y.toByteArray()).toByteArray(); 107 this.encodedKey = getEncoded(); 108 } catch (IOException e) { 109 throw new ProviderException("Cannot produce ASN.1 encoding", e); 110 } 111 } 112 113 /** 114 * Make a DH public key from its DER encoding (X.509). 115 * 116 * @param encodedKey the encoded key 117 * 118 * @exception InvalidKeyException if the encoded key does not represent 119 * a Diffie-Hellman public key 120 */ 121 DHPublicKey(byte[] encodedKey) throws InvalidKeyException { 122 InputStream inStream = new ByteArrayInputStream(encodedKey); 123 try { 124 DerValue derKeyVal = new DerValue(inStream); 125 if (derKeyVal.tag != DerValue.tag_Sequence) { 126 throw new InvalidKeyException ("Invalid key format"); 127 } 128 129 /* 130 * Parse the algorithm identifier 131 */ 132 DerValue algid = derKeyVal.data.getDerValue(); 133 if (algid.tag != DerValue.tag_Sequence) { 134 throw new InvalidKeyException("AlgId is not a SEQUENCE"); 135 } 136 DerInputStream derInStream = algid.toDerInputStream(); 137 ObjectIdentifier oid = derInStream.getOID(); 138 if (oid == null) { 139 throw new InvalidKeyException("Null OID"); 140 } 141 if (derInStream.available() == 0) { 142 throw new InvalidKeyException("Parameters missing"); 143 } 144 145 /* 146 * Parse the parameters 147 */ 148 DerValue params = derInStream.getDerValue(); 149 if (params.tag == DerValue.tag_Null) { 150 throw new InvalidKeyException("Null parameters"); 151 } 152 if (params.tag != DerValue.tag_Sequence) { 153 throw new InvalidKeyException("Parameters not a SEQUENCE"); 154 } 155 params.data.reset(); 156 this.p = params.data.getBigInteger(); 157 this.g = params.data.getBigInteger(); 158 // Private-value length is OPTIONAL 159 if (params.data.available() != 0) { 160 this.l = params.data.getInteger(); 161 } 162 if (params.data.available() != 0) { 163 throw new InvalidKeyException("Extra parameter data"); 164 } 165 166 /* 167 * Parse the key 168 */ 169 this.key = derKeyVal.data.getBitString(); 170 parseKeyBits(); 171 if (derKeyVal.data.available() != 0) { 172 throw new InvalidKeyException("Excess key data"); 173 } 174 175 this.encodedKey = encodedKey.clone(); 176 177 } catch (NumberFormatException e) { 178 throw new InvalidKeyException("Private-value length too big"); 179 180 } catch (IOException e) { 181 throw new InvalidKeyException("Error parsing key encoding: " + e.toString()); 182 } 183 } 184 185 /** 186 * Returns the encoding format of this key: "X.509" 187 */ 188 public String getFormat() { 189 return "X.509"; 190 } 191 192 /** 193 * Returns the name of the algorithm associated with this key: "DH" 194 */ 195 public String getAlgorithm() { 196 return "DH"; 197 } 198 199 /** 200 * Get the encoding of the key. 201 */ 202 public synchronized byte[] getEncoded() { 203 if (this.encodedKey == null) { 204 try { 205 DerOutputStream algid = new DerOutputStream(); 206 207 // store oid in algid 208 algid.putOID(new ObjectIdentifier(DH_data)); 209 210 // encode parameters 211 DerOutputStream params = new DerOutputStream(); 212 params.putInteger(this.p); 213 params.putInteger(this.g); 214 if (this.l != 0) 215 params.putInteger(this.l); 216 // wrap parameters into SEQUENCE 217 DerValue paramSequence = new DerValue(DerValue.tag_Sequence, 218 params.toByteArray()); 219 // store parameter SEQUENCE in algid 220 algid.putDerValue(paramSequence); 221 222 // wrap algid into SEQUENCE, and store it in key encoding 223 DerOutputStream tmpDerKey = new DerOutputStream(); 224 tmpDerKey.write(DerValue.tag_Sequence, algid); 225 226 // store key data 227 tmpDerKey.putBitString(this.key); 228 229 // wrap algid and key into SEQUENCE 230 DerOutputStream derKey = new DerOutputStream(); 231 derKey.write(DerValue.tag_Sequence, tmpDerKey); 232 this.encodedKey = derKey.toByteArray(); 233 } catch (IOException e) { 234 return null; 235 } 236 } 237 return this.encodedKey.clone(); 238 } 239 240 /** 241 * Returns the public value, <code>y</code>. 242 * 243 * @return the public value, <code>y</code> 244 */ 245 public BigInteger getY() { 246 return this.y; 247 } 248 249 /** 250 * Returns the key parameters. 251 * 252 * @return the key parameters 253 */ 254 public DHParameterSpec getParams() { 255 if (this.l != 0) 256 return new DHParameterSpec(this.p, this.g, this.l); 257 else 258 return new DHParameterSpec(this.p, this.g); 259 } 260 261 public String toString() { 262 String LINE_SEP = System.getProperty("line.separator"); 263 264 StringBuffer strbuf 265 = new StringBuffer("SunJCE Diffie-Hellman Public Key:" 266 + LINE_SEP + "y:" + LINE_SEP 267 + Debug.toHexString(this.y) 268 + LINE_SEP + "p:" + LINE_SEP 269 + Debug.toHexString(this.p) 270 + LINE_SEP + "g:" + LINE_SEP 271 + Debug.toHexString(this.g)); 272 if (this.l != 0) 273 strbuf.append(LINE_SEP + "l:" + LINE_SEP + " " + this.l); 274 return strbuf.toString(); 275 } 276 277 private void parseKeyBits() throws InvalidKeyException { 278 try { 279 DerInputStream in = new DerInputStream(this.key); 280 this.y = in.getBigInteger(); 281 } catch (IOException e) { 282 throw new InvalidKeyException("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 */ 290 public int hashCode() { 291 int retval = 0; 292 byte[] enc = getEncoded(); 293 294 for (int i = 1; i < enc.length; i++) { 295 retval += enc[i] * i; 296 } 297 return(retval); 298 } 299 300 public boolean equals(Object obj) { 301 if (this == obj) 302 return true; 303 304 if (!(obj instanceof PublicKey)) 305 return false; 306 307 byte[] thisEncoded = this.getEncoded(); 308 byte[] thatEncoded = ((PublicKey)obj).getEncoded(); 309 310 return java.util.Arrays.equals(thisEncoded, thatEncoded); 311 } 312 313 /** 314 * Replace the DH public key to be serialized. 315 * 316 * @return the standard KeyRep object to be serialized 317 * 318 * @throws java.io.ObjectStreamException if a new object representing 319 * this DH public key could not be created 320 */ 321 private Object writeReplace() throws java.io.ObjectStreamException { 322 return new KeyRep(KeyRep.Type.PUBLIC, 323 getAlgorithm(), 324 getFormat(), 325 getEncoded()); 326 } 327 }