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