/*
 * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package sun.security.pkcs;

import java.io.*;
import java.security.Key;
import java.security.KeyRep;
import java.security.PrivateKey;
import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Arrays;

import sun.security.x509.*;
import sun.security.util.*;

Holds a PKCS#8 key, for example a private key According to https://tools.ietf.org/html/rfc5958: OneAsymmetricKey ::= SEQUENCE { version Version, privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, privateKey PrivateKey, attributes [0] Attributes OPTIONAL, ..., [[2: publicKey [1] PublicKey OPTIONAL ]], ... } We support this format but do not parse attributes and publicKey now.
/** * Holds a PKCS#8 key, for example a private key * * According to https://tools.ietf.org/html/rfc5958: * * OneAsymmetricKey ::= SEQUENCE { * version Version, * privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, * privateKey PrivateKey, * attributes [0] Attributes OPTIONAL, * ..., * [[2: publicKey [1] PublicKey OPTIONAL ]], * ... * } * * We support this format but do not parse attributes and publicKey now. */
public class PKCS8Key implements PrivateKey {
use serialVersionUID from JDK 1.1. for interoperability
/** use serialVersionUID from JDK 1.1. for interoperability */
@java.io.Serial private static final long serialVersionUID = -3836890099307167124L; /* The algorithm information (name, parameters, etc). */ protected AlgorithmId algid; /* The key bytes, without the algorithm information */ protected byte[] key; /* The encoded for the key. Created on demand by encode(). */ protected byte[] encodedKey; /* The version for this key */ private static final int V1 = 0; private static final int V2 = 1;
Default constructor. Constructors in sub-classes that create a new key from its components require this. These constructors must initialize algid and key.
/** * Default constructor. Constructors in sub-classes that create a new key * from its components require this. These constructors must initialize * {@link #algid} and {@link #key}. */
protected PKCS8Key() { }
Another constructor. Constructors in sub-classes that create a new key from an encoded byte array require this. We do not assign this encoding to encodedKey directly. This method is also used by parseKey to create a raw key.
/** * Another constructor. Constructors in sub-classes that create a new key * from an encoded byte array require this. We do not assign this * encoding to {@link #encodedKey} directly. * * This method is also used by {@link #parseKey} to create a raw key. */
protected PKCS8Key(byte[] input) throws InvalidKeyException { decode(new ByteArrayInputStream(input)); } private void decode(InputStream is) throws InvalidKeyException { try { DerValue val = new DerValue(is); if (val.tag != DerValue.tag_Sequence) { throw new InvalidKeyException("invalid key format"); } int version = val.data.getInteger(); if (version != V1 && version != V2) { throw new InvalidKeyException("unknown version: " + version); } algid = AlgorithmId.parse (val.data.getDerValue ()); key = val.data.getOctetString (); DerValue next; if (val.data.available() == 0) { return; } next = val.data.getDerValue(); if (next.isContextSpecific((byte)0)) { if (val.data.available() == 0) { return; } next = val.data.getDerValue(); } if (next.isContextSpecific((byte)1)) { if (version == V1) { throw new InvalidKeyException("publicKey seen in v1"); } if (val.data.available() == 0) { return; } } throw new InvalidKeyException("Extra bytes"); } catch (IOException e) { throw new InvalidKeyException("IOException : " + e.getMessage()); } }
Construct PKCS#8 subject public key from a DER value. If a security provider supports the key algorithm with a specific class, a PrivateKey from the provider is returned. Otherwise, a raw PKCS8Key object is returned.

This mechanism guarantees that keys (and algorithms) may be freely manipulated and transferred, without risk of losing information. Also, when a key (or algorithm) needs some special handling, that specific need can be accommodated.

Params:
  • in – the DER-encoded SubjectPublicKeyInfo value
Throws:
/** * Construct PKCS#8 subject public key from a DER value. If a * security provider supports the key algorithm with a specific class, * a PrivateKey from the provider is returned. Otherwise, a raw * PKCS8Key object is returned. * * <P>This mechanism guarantees that keys (and algorithms) may be * freely manipulated and transferred, without risk of losing * information. Also, when a key (or algorithm) needs some special * handling, that specific need can be accommodated. * * @param in the DER-encoded SubjectPublicKeyInfo value * @exception IOException on data format errors */
public static PrivateKey parseKey(DerValue in) throws IOException { try { PKCS8Key rawKey = new PKCS8Key(in.toByteArray()); PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(rawKey.getEncoded()); try { return KeyFactory.getInstance(rawKey.algid.getName()) .generatePrivate(pkcs8KeySpec); } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { // Ignore and return raw key return rawKey; } } catch (InvalidKeyException e) { throw new IOException("corrupt private key", e); } }
Returns the algorithm to be used with this key.
/** * Returns the algorithm to be used with this key. */
public String getAlgorithm() { return algid.getName(); }
Returns the algorithm ID to be used with this key.
/** * Returns the algorithm ID to be used with this key. */
public AlgorithmId getAlgorithmId () { return algid; }
Returns the DER-encoded form of the key as a byte array, or null if an encoding error occurs.
/** * Returns the DER-encoded form of the key as a byte array, * or {@code null} if an encoding error occurs. */
public synchronized byte[] getEncoded() { try { encode(); return encodedKey.clone(); } catch (InvalidKeyException e) { // ignored and return null } return null; }
Returns the format for this key: "PKCS#8"
/** * Returns the format for this key: "PKCS#8" */
public String getFormat() { return "PKCS#8"; }
DER-encodes this key as a byte array that can be retrieved by the getEncoded() method.
Throws:
/** * DER-encodes this key as a byte array that can be retrieved * by the {@link #getEncoded()} method. * * @exception InvalidKeyException if an encoding error occurs. */
private void encode() throws InvalidKeyException { if (encodedKey == null) { try { DerOutputStream out = new DerOutputStream (); DerOutputStream tmp = new DerOutputStream(); tmp.putInteger(V1); algid.encode(tmp); tmp.putOctetString(key); out.write(DerValue.tag_Sequence, tmp); encodedKey = out.toByteArray(); } catch (IOException e) { throw new InvalidKeyException ("IOException : " + e.getMessage()); } } } @java.io.Serial protected Object writeReplace() throws java.io.ObjectStreamException { return new KeyRep(KeyRep.Type.PRIVATE, getAlgorithm(), getFormat(), getEncoded()); }
We used to serialize a PKCS8Key as itself (instead of a KeyRep).
/** * We used to serialize a PKCS8Key as itself (instead of a KeyRep). */
@java.io.Serial private void readObject(ObjectInputStream stream) throws IOException { try { decode(stream); } catch (InvalidKeyException e) { throw new IOException("deserialized key is invalid: " + e.getMessage()); } }
Compares two private keys. This returns false if the object with which to compare is not of type Key. Otherwise, the encoding of this key object is compared with the encoding of the given key object.
Params:
  • object – the object with which to compare
Returns:true if this key has the same encoding as the object argument; false otherwise.
/** * Compares two private keys. This returns false if the object with which * to compare is not of type <code>Key</code>. * Otherwise, the encoding of this key object is compared with the * encoding of the given key object. * * @param object the object with which to compare * @return {@code true} if this key has the same encoding as the * object argument; {@code false} otherwise. */
public boolean equals(Object object) { if (this == object) { return true; } if (object instanceof Key) { // time-constant comparison return MessageDigest.isEqual( getEncoded(), ((Key)object).getEncoded()); } return false; }
Calculates a hash code value for this object. Objects which are equal will also have the same hashcode.
/** * Calculates a hash code value for this object. Objects * which are equal will also have the same hashcode. */
public int hashCode() { return Arrays.hashCode(getEncoded()); } }