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