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 }