All Downloads are FREE. Search and download functionalities are using the official Maven repository.

sun.security.pkcs.PKCS8Key Maven / Gradle / Ivy

There is a newer version: 17.alpha.0.57
Show newest version
/*
 * Copyright (c) 1996, 2021, 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 jdk.internal.access.SharedSecrets;
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.
 */
public class PKCS8Key implements PrivateKey {

    /** 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
     * {@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 {@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 {
        DerValue val = null;
        try {
            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());
        } finally {
            if (val != null) {
                val.clear();
            }
        }
    }

    /**
     * Construct PKCS#8 subject public key from a DER encoding.  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. * * @param encoded the DER-encoded SubjectPublicKeyInfo value * @exception IOException on data format errors */ public static PrivateKey parseKey(byte[] encoded) throws IOException { try { PKCS8Key rawKey = new PKCS8Key(encoded); byte[] internal = rawKey.getEncodedInternal(); PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(internal); PrivateKey result = null; try { result = KeyFactory.getInstance(rawKey.algid.getName()) .generatePrivate(pkcs8KeySpec); } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { // Ignore and return raw key result = rawKey; } finally { if (result != rawKey) { rawKey.clear(); } SharedSecrets.getJavaSecuritySpecAccess() .clearEncodedKeySpec(pkcs8KeySpec); } return result; } catch (InvalidKeyException e) { throw new IOException("corrupt private key", e); } } /** * 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. */ public AlgorithmId getAlgorithmId () { return algid; } /** * Returns the DER-encoded form of the key as a byte array, * or {@code null} if an encoding error occurs. */ public byte[] getEncoded() { byte[] b = getEncodedInternal(); return (b == null) ? null : b.clone(); } /** * Returns the format for this key: "PKCS#8" */ public String getFormat() { return "PKCS#8"; } /** * DER-encodes this key as a byte array stored inside this object * and return it. * * @return the encoding, or null if there is an I/O error. */ private synchronized byte[] getEncodedInternal() { if (encodedKey == null) { try { DerOutputStream tmp = new DerOutputStream(); tmp.putInteger(V1); algid.encode(tmp); tmp.putOctetString(key); DerValue out = DerValue.wrap(DerValue.tag_Sequence, tmp); encodedKey = out.toByteArray(); out.clear(); } catch (IOException e) { // encodedKey is still null } } return encodedKey; } @java.io.Serial protected Object writeReplace() throws java.io.ObjectStreamException { return new KeyRep(KeyRep.Type.PRIVATE, getAlgorithm(), getFormat(), getEncodedInternal()); } /** * 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. * * @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 PKCS8Key) { // time-constant comparison return MessageDigest.isEqual( getEncodedInternal(), ((PKCS8Key)object).getEncodedInternal()); } else if (object instanceof Key) { // time-constant comparison byte[] otherEncoded = ((Key)object).getEncoded(); try { return MessageDigest.isEqual( getEncodedInternal(), otherEncoded); } finally { if (otherEncoded != null) { Arrays.fill(otherEncoded, (byte) 0); } } } return false; } /** * Calculates a hash code value for this object. Objects * which are equal will also have the same hashcode. */ public int hashCode() { return Arrays.hashCode(getEncodedInternal()); } public void clear() { if (encodedKey != null) { Arrays.fill(encodedKey, (byte)0); } Arrays.fill(key, (byte)0); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy