Path: blob/master/src/java.base/share/classes/javax/crypto/EncryptedPrivateKeyInfo.java
67743 views
/*1* Copyright (c) 2001, 2022, 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 javax.crypto;2627import java.io.*;28import java.security.*;29import java.security.spec.*;30import sun.security.x509.AlgorithmId;31import sun.security.util.DerValue;32import sun.security.util.DerInputStream;33import sun.security.util.DerOutputStream;3435/**36* This class implements the <code>EncryptedPrivateKeyInfo</code> type37* as defined in PKCS #8.38* <p>Its ASN.1 definition is as follows:39*40* <pre>41* EncryptedPrivateKeyInfo ::= SEQUENCE {42* encryptionAlgorithm AlgorithmIdentifier,43* encryptedData OCTET STRING }44*45* AlgorithmIdentifier ::= SEQUENCE {46* algorithm OBJECT IDENTIFIER,47* parameters ANY DEFINED BY algorithm OPTIONAL }48* </pre>49*50* @author Valerie Peng51*52* @see java.security.spec.PKCS8EncodedKeySpec53*54* @since 1.455*/5657public class EncryptedPrivateKeyInfo {5859// the "encryptionAlgorithm" field60private AlgorithmId algid;6162// the algorithm name of the encrypted private key63private String keyAlg;6465// the "encryptedData" field66private byte[] encryptedData;6768// the ASN.1 encoded contents of this class69private byte[] encoded = null;7071/**72* Constructs (i.e., parses) an <code>EncryptedPrivateKeyInfo</code> from73* its ASN.1 encoding.74* @param encoded the ASN.1 encoding of this object. The contents of75* the array are copied to protect against subsequent modification.76* @exception NullPointerException if the <code>encoded</code> is null.77* @exception IOException if error occurs when parsing the ASN.1 encoding.78*/79public EncryptedPrivateKeyInfo(byte[] encoded) throws IOException {80if (encoded == null) {81throw new NullPointerException("the encoded parameter " +82"must be non-null");83}8485this.encoded = encoded.clone();86DerValue val = DerValue.wrap(this.encoded);87if (val.tag != DerValue.tag_Sequence) {88throw new IOException("DER header error: no SEQ tag");89}9091DerValue[] seq = new DerValue[2];9293seq[0] = val.data.getDerValue();94seq[1] = val.data.getDerValue();9596if (val.data.available() != 0) {97throw new IOException("overrun, bytes = " + val.data.available());98}99100this.algid = AlgorithmId.parse(seq[0]);101if (seq[0].data.available() != 0) {102throw new IOException("encryptionAlgorithm field overrun");103}104105this.encryptedData = seq[1].getOctetString();106if (seq[1].data.available() != 0) {107throw new IOException("encryptedData field overrun");108}109}110111/**112* Constructs an <code>EncryptedPrivateKeyInfo</code> from the113* encryption algorithm name and the encrypted data.114*115* <p>Note: This constructor will use null as the value of the116* algorithm parameters. If the encryption algorithm has117* parameters whose value is not null, a different constructor,118* e.g. EncryptedPrivateKeyInfo(AlgorithmParameters, byte[]),119* should be used.120*121* @param algName encryption algorithm name. See the122* <a href="{@docRoot}/../specs/security/standard-names.html">123* Java Security Standard Algorithm Names</a> document124* for information about standard Cipher algorithm names.125* @param encryptedData encrypted data. The contents of126* <code>encrypedData</code> are copied to protect against subsequent127* modification when constructing this object.128* @exception NullPointerException if <code>algName</code> or129* <code>encryptedData</code> is null.130* @exception IllegalArgumentException if <code>encryptedData</code>131* is empty, i.e. 0-length.132* @exception NoSuchAlgorithmException if the specified algName is133* not supported.134*/135public EncryptedPrivateKeyInfo(String algName, byte[] encryptedData)136throws NoSuchAlgorithmException {137138if (algName == null)139throw new NullPointerException("the algName parameter " +140"must be non-null");141this.algid = AlgorithmId.get(algName);142143if (encryptedData == null) {144throw new NullPointerException("the encryptedData " +145"parameter must be non-null");146} else if (encryptedData.length == 0) {147throw new IllegalArgumentException("the encryptedData " +148"parameter must not be empty");149} else {150this.encryptedData = encryptedData.clone();151}152// delay the generation of ASN.1 encoding until153// getEncoded() is called154this.encoded = null;155}156157/**158* Constructs an <code>EncryptedPrivateKeyInfo</code> from the159* encryption algorithm parameters and the encrypted data.160*161* @param algParams the algorithm parameters for the encryption162* algorithm. <code>algParams.getEncoded()</code> should return163* the ASN.1 encoded bytes of the <code>parameters</code> field164* of the <code>AlgorithmIdentifer</code> component of the165* <code>EncryptedPrivateKeyInfo</code> type.166* @param encryptedData encrypted data. The contents of167* <code>encrypedData</code> are copied to protect against168* subsequent modification when constructing this object.169* @exception NullPointerException if <code>algParams</code> or170* <code>encryptedData</code> is null.171* @exception IllegalArgumentException if <code>encryptedData</code>172* is empty, i.e. 0-length.173* @exception NoSuchAlgorithmException if the specified algName of174* the specified <code>algParams</code> parameter is not supported.175*/176public EncryptedPrivateKeyInfo(AlgorithmParameters algParams,177byte[] encryptedData) throws NoSuchAlgorithmException {178179if (algParams == null) {180throw new NullPointerException("algParams must be non-null");181}182this.algid = AlgorithmId.get(algParams);183184if (encryptedData == null) {185throw new NullPointerException("encryptedData must be non-null");186} else if (encryptedData.length == 0) {187throw new IllegalArgumentException("the encryptedData " +188"parameter must not be empty");189} else {190this.encryptedData = encryptedData.clone();191}192193// delay the generation of ASN.1 encoding until194// getEncoded() is called195this.encoded = null;196}197198199/**200* Returns the encryption algorithm.201* <p>Note: Standard name is returned instead of the specified one202* in the constructor when such mapping is available.203* See the <a href="{@docRoot}/../specs/security/standard-names.html">204* Java Security Standard Algorithm Names</a> document205* for information about standard Cipher algorithm names.206*207* @return the encryption algorithm name.208*/209public String getAlgName() {210return this.algid.getName();211}212213/**214* Returns the algorithm parameters used by the encryption algorithm.215* @return the algorithm parameters.216*/217public AlgorithmParameters getAlgParameters() {218return this.algid.getParameters();219}220221/**222* Returns the encrypted data.223* @return the encrypted data. Returns a new array224* each time this method is called.225*/226public byte[] getEncryptedData() {227return this.encryptedData.clone();228}229230/**231* Extract the enclosed PKCS8EncodedKeySpec object from the232* encrypted data and return it.233* <br>Note: In order to successfully retrieve the enclosed234* PKCS8EncodedKeySpec object, <code>cipher</code> needs235* to be initialized to either Cipher.DECRYPT_MODE or236* Cipher.UNWRAP_MODE, with the same key and parameters used237* for generating the encrypted data.238*239* @param cipher the initialized cipher object which will be240* used for decrypting the encrypted data.241* @return the PKCS8EncodedKeySpec object.242* @exception NullPointerException if <code>cipher</code>243* is null.244* @exception InvalidKeySpecException if the given cipher is245* inappropriate for the encrypted data or the encrypted246* data is corrupted and cannot be decrypted.247*/248public PKCS8EncodedKeySpec getKeySpec(Cipher cipher)249throws InvalidKeySpecException {250byte[] encoded = null;251try {252encoded = cipher.doFinal(encryptedData);253checkPKCS8Encoding(encoded);254} catch (GeneralSecurityException |255IOException |256IllegalStateException ex) {257throw new InvalidKeySpecException(258"Cannot retrieve the PKCS8EncodedKeySpec", ex);259}260return new PKCS8EncodedKeySpec(encoded, keyAlg);261}262263private PKCS8EncodedKeySpec getKeySpecImpl(Key decryptKey,264Provider provider) throws NoSuchAlgorithmException,265InvalidKeyException {266byte[] encoded = null;267Cipher c;268try {269if (provider == null) {270// use the most preferred one271c = Cipher.getInstance(algid.getName());272} else {273c = Cipher.getInstance(algid.getName(), provider);274}275c.init(Cipher.DECRYPT_MODE, decryptKey, algid.getParameters());276encoded = c.doFinal(encryptedData);277checkPKCS8Encoding(encoded);278} catch (NoSuchAlgorithmException nsae) {279// rethrow280throw nsae;281} catch (GeneralSecurityException | IOException ex) {282throw new InvalidKeyException(283"Cannot retrieve the PKCS8EncodedKeySpec", ex);284}285return new PKCS8EncodedKeySpec(encoded, keyAlg);286}287288/**289* Extract the enclosed PKCS8EncodedKeySpec object from the290* encrypted data and return it.291* @param decryptKey key used for decrypting the encrypted data.292* @return the PKCS8EncodedKeySpec object.293* @exception NullPointerException if <code>decryptKey</code>294* is null.295* @exception NoSuchAlgorithmException if cannot find appropriate296* cipher to decrypt the encrypted data.297* @exception InvalidKeyException if <code>decryptKey</code>298* cannot be used to decrypt the encrypted data or the decryption299* result is not a valid PKCS8KeySpec.300*301* @since 1.5302*/303public PKCS8EncodedKeySpec getKeySpec(Key decryptKey)304throws NoSuchAlgorithmException, InvalidKeyException {305if (decryptKey == null) {306throw new NullPointerException("decryptKey is null");307}308return getKeySpecImpl(decryptKey, null);309}310311/**312* Extract the enclosed PKCS8EncodedKeySpec object from the313* encrypted data and return it.314* @param decryptKey key used for decrypting the encrypted data.315* @param providerName the name of provider whose Cipher316* implementation will be used.317* @return the PKCS8EncodedKeySpec object.318* @exception NullPointerException if <code>decryptKey</code>319* or <code>providerName</code> is null.320* @exception NoSuchProviderException if no provider321* <code>providerName</code> is registered.322* @exception NoSuchAlgorithmException if cannot find appropriate323* cipher to decrypt the encrypted data.324* @exception InvalidKeyException if <code>decryptKey</code>325* cannot be used to decrypt the encrypted data or the decryption326* result is not a valid PKCS8KeySpec.327*328* @since 1.5329*/330public PKCS8EncodedKeySpec getKeySpec(Key decryptKey,331String providerName) throws NoSuchProviderException,332NoSuchAlgorithmException, InvalidKeyException {333if (decryptKey == null) {334throw new NullPointerException("decryptKey is null");335}336if (providerName == null) {337throw new NullPointerException("provider is null");338}339Provider provider = Security.getProvider(providerName);340if (provider == null) {341throw new NoSuchProviderException("provider " +342providerName + " not found");343}344return getKeySpecImpl(decryptKey, provider);345}346347/**348* Extract the enclosed PKCS8EncodedKeySpec object from the349* encrypted data and return it.350* @param decryptKey key used for decrypting the encrypted data.351* @param provider the name of provider whose Cipher implementation352* will be used.353* @return the PKCS8EncodedKeySpec object.354* @exception NullPointerException if <code>decryptKey</code>355* or <code>provider</code> is null.356* @exception NoSuchAlgorithmException if cannot find appropriate357* cipher to decrypt the encrypted data in <code>provider</code>.358* @exception InvalidKeyException if <code>decryptKey</code>359* cannot be used to decrypt the encrypted data or the decryption360* result is not a valid PKCS8KeySpec.361*362* @since 1.5363*/364public PKCS8EncodedKeySpec getKeySpec(Key decryptKey,365Provider provider) throws NoSuchAlgorithmException,366InvalidKeyException {367if (decryptKey == null) {368throw new NullPointerException("decryptKey is null");369}370if (provider == null) {371throw new NullPointerException("provider is null");372}373return getKeySpecImpl(decryptKey, provider);374}375376/**377* Returns the ASN.1 encoding of this object.378* @return the ASN.1 encoding. Returns a new array379* each time this method is called.380* @exception IOException if error occurs when constructing its381* ASN.1 encoding.382*/383public byte[] getEncoded() throws IOException {384if (this.encoded == null) {385DerOutputStream out = new DerOutputStream();386DerOutputStream tmp = new DerOutputStream();387388// encode encryption algorithm389algid.encode(tmp);390391// encode encrypted data392tmp.putOctetString(encryptedData);393394// wrap everything into a SEQUENCE395out.write(DerValue.tag_Sequence, tmp);396this.encoded = out.toByteArray();397}398return this.encoded.clone();399}400401private static void checkTag(DerValue val, byte tag, String valName)402throws IOException {403if (val.getTag() != tag) {404throw new IOException("invalid key encoding - wrong tag for " +405valName);406}407}408409@SuppressWarnings("fallthrough")410private void checkPKCS8Encoding(byte[] encodedKey)411throws IOException {412DerInputStream in = new DerInputStream(encodedKey);413DerValue[] values = in.getSequence(3);414415switch (values.length) {416case 4:417checkTag(values[3], DerValue.TAG_CONTEXT, "attributes");418/* fall through */419case 3:420checkTag(values[0], DerValue.tag_Integer, "version");421keyAlg = AlgorithmId.parse(values[1]).getName();422checkTag(values[2], DerValue.tag_OctetString, "privateKey");423break;424default:425throw new IOException("invalid key encoding");426}427}428}429430431