Path: blob/master/src/java.base/share/classes/sun/security/pkcs/PKCS8Key.java
41159 views
/*1* Copyright (c) 1996, 2021, 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.security.Key;29import java.security.KeyRep;30import java.security.PrivateKey;31import java.security.KeyFactory;32import java.security.MessageDigest;33import java.security.InvalidKeyException;34import java.security.NoSuchAlgorithmException;35import java.security.spec.InvalidKeySpecException;36import java.security.spec.PKCS8EncodedKeySpec;37import java.util.Arrays;3839import jdk.internal.access.SharedSecrets;40import sun.security.x509.*;41import sun.security.util.*;4243/**44* Holds a PKCS#8 key, for example a private key45*46* According to https://tools.ietf.org/html/rfc5958:47*48* OneAsymmetricKey ::= SEQUENCE {49* version Version,50* privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,51* privateKey PrivateKey,52* attributes [0] Attributes OPTIONAL,53* ...,54* [[2: publicKey [1] PublicKey OPTIONAL ]],55* ...56* }57*58* We support this format but do not parse attributes and publicKey now.59*/60public class PKCS8Key implements PrivateKey {6162/** use serialVersionUID from JDK 1.1. for interoperability */63@java.io.Serial64private static final long serialVersionUID = -3836890099307167124L;6566/* The algorithm information (name, parameters, etc). */67protected AlgorithmId algid;6869/* The key bytes, without the algorithm information */70protected byte[] key;7172/* The encoded for the key. Created on demand by encode(). */73protected byte[] encodedKey;7475/* The version for this key */76private static final int V1 = 0;77private static final int V2 = 1;7879/**80* Default constructor. Constructors in sub-classes that create a new key81* from its components require this. These constructors must initialize82* {@link #algid} and {@link #key}.83*/84protected PKCS8Key() { }8586/**87* Another constructor. Constructors in sub-classes that create a new key88* from an encoded byte array require this. We do not assign this89* encoding to {@link #encodedKey} directly.90*91* This method is also used by {@link #parseKey} to create a raw key.92*/93protected PKCS8Key(byte[] input) throws InvalidKeyException {94decode(new ByteArrayInputStream(input));95}9697private void decode(InputStream is) throws InvalidKeyException {98DerValue val = null;99try {100val = new DerValue(is);101if (val.tag != DerValue.tag_Sequence) {102throw new InvalidKeyException("invalid key format");103}104105int version = val.data.getInteger();106if (version != V1 && version != V2) {107throw new InvalidKeyException("unknown version: " + version);108}109algid = AlgorithmId.parse (val.data.getDerValue ());110key = val.data.getOctetString();111112DerValue next;113if (val.data.available() == 0) {114return;115}116next = val.data.getDerValue();117if (next.isContextSpecific((byte)0)) {118if (val.data.available() == 0) {119return;120}121next = val.data.getDerValue();122}123124if (next.isContextSpecific((byte)1)) {125if (version == V1) {126throw new InvalidKeyException("publicKey seen in v1");127}128if (val.data.available() == 0) {129return;130}131}132throw new InvalidKeyException("Extra bytes");133} catch (IOException e) {134throw new InvalidKeyException("IOException : " + e.getMessage());135} finally {136if (val != null) {137val.clear();138}139}140}141142/**143* Construct PKCS#8 subject public key from a DER encoding. If a144* security provider supports the key algorithm with a specific class,145* a PrivateKey from the provider is returned. Otherwise, a raw146* PKCS8Key object is returned.147*148* <P>This mechanism guarantees that keys (and algorithms) may be149* freely manipulated and transferred, without risk of losing150* information. Also, when a key (or algorithm) needs some special151* handling, that specific need can be accommodated.152*153* @param encoded the DER-encoded SubjectPublicKeyInfo value154* @exception IOException on data format errors155*/156public static PrivateKey parseKey(byte[] encoded) throws IOException {157try {158PKCS8Key rawKey = new PKCS8Key(encoded);159byte[] internal = rawKey.getEncodedInternal();160PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(internal);161PrivateKey result = null;162try {163result = KeyFactory.getInstance(rawKey.algid.getName())164.generatePrivate(pkcs8KeySpec);165} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {166// Ignore and return raw key167result = rawKey;168} finally {169if (result != rawKey) {170rawKey.clear();171}172SharedSecrets.getJavaSecuritySpecAccess()173.clearEncodedKeySpec(pkcs8KeySpec);174}175return result;176} catch (InvalidKeyException e) {177throw new IOException("corrupt private key", e);178}179}180181/**182* Returns the algorithm to be used with this key.183*/184public String getAlgorithm() {185return algid.getName();186}187188/**189* Returns the algorithm ID to be used with this key.190*/191public AlgorithmId getAlgorithmId () {192return algid;193}194195/**196* Returns the DER-encoded form of the key as a byte array,197* or {@code null} if an encoding error occurs.198*/199public byte[] getEncoded() {200byte[] b = getEncodedInternal();201return (b == null) ? null : b.clone();202}203204/**205* Returns the format for this key: "PKCS#8"206*/207public String getFormat() {208return "PKCS#8";209}210211/**212* DER-encodes this key as a byte array stored inside this object213* and return it.214*215* @return the encoding, or null if there is an I/O error.216*/217private synchronized byte[] getEncodedInternal() {218if (encodedKey == null) {219try {220DerOutputStream tmp = new DerOutputStream();221tmp.putInteger(V1);222algid.encode(tmp);223tmp.putOctetString(key);224DerValue out = DerValue.wrap(DerValue.tag_Sequence, tmp);225encodedKey = out.toByteArray();226out.clear();227} catch (IOException e) {228// encodedKey is still null229}230}231return encodedKey;232}233234@java.io.Serial235protected Object writeReplace() throws java.io.ObjectStreamException {236return new KeyRep(KeyRep.Type.PRIVATE,237getAlgorithm(),238getFormat(),239getEncodedInternal());240}241242/**243* We used to serialize a PKCS8Key as itself (instead of a KeyRep).244*/245@java.io.Serial246private void readObject(ObjectInputStream stream) throws IOException {247try {248decode(stream);249} catch (InvalidKeyException e) {250throw new IOException("deserialized key is invalid: " +251e.getMessage());252}253}254255/**256* Compares two private keys. This returns false if the object with which257* to compare is not of type <code>Key</code>.258* Otherwise, the encoding of this key object is compared with the259* encoding of the given key object.260*261* @param object the object with which to compare262* @return {@code true} if this key has the same encoding as the263* object argument; {@code false} otherwise.264*/265public boolean equals(Object object) {266if (this == object) {267return true;268}269if (object instanceof PKCS8Key) {270// time-constant comparison271return MessageDigest.isEqual(272getEncodedInternal(),273((PKCS8Key)object).getEncodedInternal());274} else if (object instanceof Key) {275// time-constant comparison276byte[] otherEncoded = ((Key)object).getEncoded();277try {278return MessageDigest.isEqual(279getEncodedInternal(),280otherEncoded);281} finally {282if (otherEncoded != null) {283Arrays.fill(otherEncoded, (byte) 0);284}285}286}287return false;288}289290/**291* Calculates a hash code value for this object. Objects292* which are equal will also have the same hashcode.293*/294public int hashCode() {295return Arrays.hashCode(getEncodedInternal());296}297298public void clear() {299if (encodedKey != null) {300Arrays.fill(encodedKey, (byte)0);301}302Arrays.fill(key, (byte)0);303}304}305306307