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