Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/security/pkcs/PKCS8Key.java
38830 views
/*1* Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.pkcs;2627import java.io.*;28import java.util.Properties;29import java.math.*;30import java.security.Key;31import java.security.KeyRep;32import java.security.PrivateKey;33import java.security.KeyFactory;34import java.security.MessageDigest;35import java.security.Security;36import java.security.Provider;37import java.security.InvalidKeyException;38import java.security.NoSuchAlgorithmException;39import java.security.spec.InvalidKeySpecException;40import java.security.spec.PKCS8EncodedKeySpec;4142import sun.misc.HexDumpEncoder;43import sun.security.x509.*;44import sun.security.util.*;4546/**47* Holds a PKCS#8 key, for example a private key48*49* @author Dave Brownell50* @author Benjamin Renaud51*/52public class PKCS8Key implements PrivateKey {5354/** use serialVersionUID from JDK 1.1. for interoperability */55private static final long serialVersionUID = -3836890099307167124L;5657/* The algorithm information (name, parameters, etc). */58protected AlgorithmId algid;5960/* The key bytes, without the algorithm information */61protected byte[] key;6263/* The encoded for the key. */64protected byte[] encodedKey;6566/* The version for this key */67public static final BigInteger version = BigInteger.ZERO;6869/**70* Default constructor. The key constructed must have its key71* and algorithm initialized before it may be used, for example72* by using <code>decode</code>.73*/74public PKCS8Key() { }7576/*77* Build and initialize as a "default" key. All PKCS#8 key78* data is stored and transmitted losslessly, but no knowledge79* about this particular algorithm is available.80*/81private PKCS8Key (AlgorithmId algid, byte key [])82throws InvalidKeyException {83this.algid = algid;84this.key = key;85encode();86}8788/*89* Binary backwards compatibility. New uses should call parseKey().90*/91public static PKCS8Key parse (DerValue in) throws IOException {92PrivateKey key;9394key = parseKey(in);95if (key instanceof PKCS8Key)96return (PKCS8Key)key;9798throw new IOException("Provider did not return PKCS8Key");99}100101/**102* Construct PKCS#8 subject public key from a DER value. If103* the runtime environment is configured with a specific class for104* this kind of key, a subclass is returned. Otherwise, a generic105* PKCS8Key object is returned.106*107* <P>This mechanism gurantees that keys (and algorithms) may be108* freely manipulated and transferred, without risk of losing109* information. Also, when a key (or algorithm) needs some special110* handling, that specific need can be accomodated.111*112* @param in the DER-encoded SubjectPublicKeyInfo value113* @exception IOException on data format errors114*/115public static PrivateKey parseKey (DerValue in) throws IOException116{117AlgorithmId algorithm;118PrivateKey privKey;119120if (in.tag != DerValue.tag_Sequence)121throw new IOException ("corrupt private key");122123BigInteger parsedVersion = in.data.getBigInteger();124if (!version.equals(parsedVersion)) {125throw new IOException("version mismatch: (supported: " +126Debug.toHexString(version) +127", parsed: " +128Debug.toHexString(parsedVersion));129}130131algorithm = AlgorithmId.parse (in.data.getDerValue ());132133try {134privKey = buildPKCS8Key (algorithm, in.data.getOctetString ());135136} catch (InvalidKeyException e) {137throw new IOException("corrupt private key");138}139140if (in.data.available () != 0)141throw new IOException ("excess private key");142return privKey;143}144145/**146* Parse the key bits. This may be redefined by subclasses to take147* advantage of structure within the key. For example, RSA public148* keys encapsulate two unsigned integers (modulus and exponent) as149* DER values within the <code>key</code> bits; Diffie-Hellman and150* DSS/DSA keys encapsulate a single unsigned integer.151*152* <P>This function is called when creating PKCS#8 SubjectPublicKeyInfo153* values using the PKCS8Key member functions, such as <code>parse</code>154* and <code>decode</code>.155*156* @exception IOException if a parsing error occurs.157* @exception InvalidKeyException if the key encoding is invalid.158*/159protected void parseKeyBits () throws IOException, InvalidKeyException {160encode();161}162163/*164* Factory interface, building the kind of key associated with this165* specific algorithm ID or else returning this generic base class.166* See the description above.167*/168static PrivateKey buildPKCS8Key (AlgorithmId algid, byte[] key)169throws IOException, InvalidKeyException170{171/*172* Use the algid and key parameters to produce the ASN.1 encoding173* of the key, which will then be used as the input to the174* key factory.175*/176DerOutputStream pkcs8EncodedKeyStream = new DerOutputStream();177encode(pkcs8EncodedKeyStream, algid, key);178PKCS8EncodedKeySpec pkcs8KeySpec179= new PKCS8EncodedKeySpec(pkcs8EncodedKeyStream.toByteArray());180181try {182// Instantiate the key factory of the appropriate algorithm183KeyFactory keyFac = KeyFactory.getInstance(algid.getName());184185// Generate the private key186return keyFac.generatePrivate(pkcs8KeySpec);187} catch (NoSuchAlgorithmException e) {188// Return generic PKCS8Key with opaque key data (see below)189} catch (InvalidKeySpecException e) {190// Return generic PKCS8Key with opaque key data (see below)191}192193/*194* Try again using JDK1.1-style for backwards compatibility.195*/196String classname = "";197try {198Properties props;199String keytype;200Provider sunProvider;201202sunProvider = Security.getProvider("SUN");203if (sunProvider == null)204throw new InstantiationException();205classname = sunProvider.getProperty("PrivateKey.PKCS#8." +206algid.getName());207if (classname == null) {208throw new InstantiationException();209}210211Class<?> keyClass = null;212try {213keyClass = Class.forName(classname);214} catch (ClassNotFoundException e) {215ClassLoader cl = ClassLoader.getSystemClassLoader();216if (cl != null) {217keyClass = cl.loadClass(classname);218}219}220221Object inst = null;222PKCS8Key result;223224if (keyClass != null)225inst = keyClass.newInstance();226if (inst instanceof PKCS8Key) {227result = (PKCS8Key) inst;228result.algid = algid;229result.key = key;230result.parseKeyBits();231return result;232}233} catch (ClassNotFoundException e) {234} catch (InstantiationException e) {235} catch (IllegalAccessException e) {236// this should not happen.237throw new IOException (classname + " [internal error]");238}239240PKCS8Key result = new PKCS8Key();241result.algid = algid;242result.key = key;243return result;244}245246/**247* Returns the algorithm to be used with this key.248*/249public String getAlgorithm() {250return algid.getName();251}252253/**254* Returns the algorithm ID to be used with this key.255*/256public AlgorithmId getAlgorithmId () { return algid; }257258/**259* PKCS#8 sequence on the DER output stream.260*/261public final void encode(DerOutputStream out) throws IOException262{263encode(out, this.algid, this.key);264}265266/**267* Returns the DER-encoded form of the key as a byte array.268*/269public synchronized byte[] getEncoded() {270byte[] result = null;271try {272result = encode();273} catch (InvalidKeyException e) {274}275return result;276}277278/**279* Returns the format for this key: "PKCS#8"280*/281public String getFormat() {282return "PKCS#8";283}284285/**286* Returns the DER-encoded form of the key as a byte array.287*288* @exception InvalidKeyException if an encoding error occurs.289*/290public byte[] encode() throws InvalidKeyException {291if (encodedKey == null) {292try {293DerOutputStream out;294295out = new DerOutputStream ();296encode (out);297encodedKey = out.toByteArray();298299} catch (IOException e) {300throw new InvalidKeyException ("IOException : " +301e.getMessage());302}303}304return encodedKey.clone();305}306307/**308* Initialize an PKCS8Key object from an input stream. The data309* on that input stream must be encoded using DER, obeying the310* PKCS#8 format: a sequence consisting of a version, an algorithm311* ID and a bit string which holds the key. (That bit string is312* often used to encapsulate another DER encoded sequence.)313*314* <P>Subclasses should not normally redefine this method; they should315* instead provide a <code>parseKeyBits</code> method to parse any316* fields inside the <code>key</code> member.317*318* @param in an input stream with a DER-encoded PKCS#8319* SubjectPublicKeyInfo value320*321* @exception InvalidKeyException if a parsing error occurs.322*/323public void decode(InputStream in) throws InvalidKeyException324{325DerValue val;326327try {328val = new DerValue (in);329if (val.tag != DerValue.tag_Sequence)330throw new InvalidKeyException ("invalid key format");331332333BigInteger version = val.data.getBigInteger();334if (!version.equals(PKCS8Key.version)) {335throw new IOException("version mismatch: (supported: " +336Debug.toHexString(PKCS8Key.version) +337", parsed: " +338Debug.toHexString(version));339}340algid = AlgorithmId.parse (val.data.getDerValue ());341key = val.data.getOctetString ();342parseKeyBits ();343344if (val.data.available () != 0) {345// OPTIONAL attributes not supported yet346}347348} catch (IOException e) {349throw new InvalidKeyException("IOException : " +350e.getMessage());351}352}353354public void decode(byte[] encodedKey) throws InvalidKeyException {355decode(new ByteArrayInputStream(encodedKey));356}357358protected Object writeReplace() throws java.io.ObjectStreamException {359return new KeyRep(KeyRep.Type.PRIVATE,360getAlgorithm(),361getFormat(),362getEncoded());363}364365/**366* Serialization read ... PKCS#8 keys serialize as367* themselves, and they're parsed when they get read back.368*/369private void readObject (ObjectInputStream stream)370throws IOException {371372try {373decode(stream);374375} catch (InvalidKeyException e) {376e.printStackTrace();377throw new IOException("deserialized key is invalid: " +378e.getMessage());379}380}381382/*383* Produce PKCS#8 encoding from algorithm id and key material.384*/385static void encode(DerOutputStream out, AlgorithmId algid, byte[] key)386throws IOException {387DerOutputStream tmp = new DerOutputStream();388tmp.putInteger(version);389algid.encode(tmp);390tmp.putOctetString(key);391out.write(DerValue.tag_Sequence, tmp);392}393394/**395* Compares two private keys. This returns false if the object with which396* to compare is not of type <code>Key</code>.397* Otherwise, the encoding of this key object is compared with the398* encoding of the given key object.399*400* @param object the object with which to compare401* @return <code>true</code> if this key has the same encoding as the402* object argument; <code>false</code> otherwise.403*/404public boolean equals(Object object) {405if (this == object) {406return true;407}408409if (object instanceof Key) {410411// this encoding412byte[] b1;413if (encodedKey != null) {414b1 = encodedKey;415} else {416b1 = getEncoded();417}418419// that encoding420byte[] b2 = ((Key)object).getEncoded();421422// time-constant comparison423return MessageDigest.isEqual(b1, b2);424}425return false;426}427428/**429* Calculates a hash code value for this object. Objects430* which are equal will also have the same hashcode.431*/432public int hashCode() {433int retval = 0;434byte[] b1 = getEncoded();435436for (int i = 1; i < b1.length; i++) {437retval += b1[i] * i;438}439return(retval);440}441}442443444