Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/javax/crypto/EncryptedPrivateKeyInfo.java
38829 views
/*1* Copyright (c) 2001, 2011, 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 "encryptedData" field63private byte[] encryptedData;6465// the ASN.1 encoded contents of this class66private byte[] encoded = null;6768/**69* Constructs (i.e., parses) an <code>EncryptedPrivateKeyInfo</code> from70* its ASN.1 encoding.71* @param encoded the ASN.1 encoding of this object. The contents of72* the array are copied to protect against subsequent modification.73* @exception NullPointerException if the <code>encoded</code> is null.74* @exception IOException if error occurs when parsing the ASN.1 encoding.75*/76public EncryptedPrivateKeyInfo(byte[] encoded)77throws IOException {78if (encoded == null) {79throw new NullPointerException("the encoded parameter " +80"must be non-null");81}82this.encoded = encoded.clone();83DerValue val = new DerValue(this.encoded);8485DerValue[] seq = new DerValue[2];8687seq[0] = val.data.getDerValue();88seq[1] = val.data.getDerValue();8990if (val.data.available() != 0) {91throw new IOException("overrun, bytes = " + val.data.available());92}9394this.algid = AlgorithmId.parse(seq[0]);95if (seq[0].data.available() != 0) {96throw new IOException("encryptionAlgorithm field overrun");97}9899this.encryptedData = seq[1].getOctetString();100if (seq[1].data.available() != 0) {101throw new IOException("encryptedData field overrun");102}103}104105/**106* Constructs an <code>EncryptedPrivateKeyInfo</code> from the107* encryption algorithm name and the encrypted data.108*109* <p>Note: This constructor will use null as the value of the110* algorithm parameters. If the encryption algorithm has111* parameters whose value is not null, a different constructor,112* e.g. EncryptedPrivateKeyInfo(AlgorithmParameters, byte[]),113* should be used.114*115* @param algName encryption algorithm name. See Appendix A in the116* <a href=117* "{@docRoot}/../technotes/guides/security/crypto/CryptoSpec.html#AppA">118* Java Cryptography Architecture Reference Guide</a>119* for information about standard Cipher algorithm names.120* @param encryptedData encrypted data. The contents of121* <code>encrypedData</code> are copied to protect against subsequent122* modification when constructing this object.123* @exception NullPointerException if <code>algName</code> or124* <code>encryptedData</code> is null.125* @exception IllegalArgumentException if <code>encryptedData</code>126* is empty, i.e. 0-length.127* @exception NoSuchAlgorithmException if the specified algName is128* not supported.129*/130public EncryptedPrivateKeyInfo(String algName, byte[] encryptedData)131throws NoSuchAlgorithmException {132133if (algName == null)134throw new NullPointerException("the algName parameter " +135"must be non-null");136this.algid = AlgorithmId.get(algName);137138if (encryptedData == null) {139throw new NullPointerException("the encryptedData " +140"parameter must be non-null");141} else if (encryptedData.length == 0) {142throw new IllegalArgumentException("the encryptedData " +143"parameter must not be empty");144} else {145this.encryptedData = encryptedData.clone();146}147// delay the generation of ASN.1 encoding until148// getEncoded() is called149this.encoded = null;150}151152/**153* Constructs an <code>EncryptedPrivateKeyInfo</code> from the154* encryption algorithm parameters and the encrypted data.155*156* @param algParams the algorithm parameters for the encryption157* algorithm. <code>algParams.getEncoded()</code> should return158* the ASN.1 encoded bytes of the <code>parameters</code> field159* of the <code>AlgorithmIdentifer</code> component of the160* <code>EncryptedPrivateKeyInfo</code> type.161* @param encryptedData encrypted data. The contents of162* <code>encrypedData</code> are copied to protect against163* subsequent modification when constructing this object.164* @exception NullPointerException if <code>algParams</code> or165* <code>encryptedData</code> is null.166* @exception IllegalArgumentException if <code>encryptedData</code>167* is empty, i.e. 0-length.168* @exception NoSuchAlgorithmException if the specified algName of169* the specified <code>algParams</code> parameter is not supported.170*/171public EncryptedPrivateKeyInfo(AlgorithmParameters algParams,172byte[] encryptedData) throws NoSuchAlgorithmException {173174if (algParams == null) {175throw new NullPointerException("algParams must be non-null");176}177this.algid = AlgorithmId.get(algParams);178179if (encryptedData == null) {180throw new NullPointerException("encryptedData must be non-null");181} else if (encryptedData.length == 0) {182throw new IllegalArgumentException("the encryptedData " +183"parameter must not be empty");184} else {185this.encryptedData = encryptedData.clone();186}187188// delay the generation of ASN.1 encoding until189// getEncoded() is called190this.encoded = null;191}192193194/**195* Returns the encryption algorithm.196* <p>Note: Standard name is returned instead of the specified one197* in the constructor when such mapping is available.198* See Appendix A in the199* <a href=200* "{@docRoot}/../technotes/guides/security/crypto/CryptoSpec.html#AppA">201* Java Cryptography Architecture Reference Guide</a>202* for information about standard Cipher algorithm names.203*204* @return the encryption algorithm name.205*/206public String getAlgName() {207return this.algid.getName();208}209210/**211* Returns the algorithm parameters used by the encryption algorithm.212* @return the algorithm parameters.213*/214public AlgorithmParameters getAlgParameters() {215return this.algid.getParameters();216}217218/**219* Returns the encrypted data.220* @return the encrypted data. Returns a new array221* each time this method is called.222*/223public byte[] getEncryptedData() {224return this.encryptedData.clone();225}226227/**228* Extract the enclosed PKCS8EncodedKeySpec object from the229* encrypted data and return it.230* <br>Note: In order to successfully retrieve the enclosed231* PKCS8EncodedKeySpec object, <code>cipher</code> needs232* to be initialized to either Cipher.DECRYPT_MODE or233* Cipher.UNWRAP_MODE, with the same key and parameters used234* for generating the encrypted data.235*236* @param cipher the initialized cipher object which will be237* used for decrypting the encrypted data.238* @return the PKCS8EncodedKeySpec object.239* @exception NullPointerException if <code>cipher</code>240* is null.241* @exception InvalidKeySpecException if the given cipher is242* inappropriate for the encrypted data or the encrypted243* data is corrupted and cannot be decrypted.244*/245public PKCS8EncodedKeySpec getKeySpec(Cipher cipher)246throws InvalidKeySpecException {247byte[] encoded = null;248try {249encoded = cipher.doFinal(encryptedData);250checkPKCS8Encoding(encoded);251} catch (GeneralSecurityException |252IOException |253IllegalStateException ex) {254throw new InvalidKeySpecException(255"Cannot retrieve the PKCS8EncodedKeySpec", ex);256}257return new PKCS8EncodedKeySpec(encoded);258}259260private PKCS8EncodedKeySpec getKeySpecImpl(Key decryptKey,261Provider provider) throws NoSuchAlgorithmException,262InvalidKeyException {263byte[] encoded = null;264Cipher c;265try {266if (provider == null) {267// use the most preferred one268c = Cipher.getInstance(algid.getName());269} else {270c = Cipher.getInstance(algid.getName(), provider);271}272c.init(Cipher.DECRYPT_MODE, decryptKey, algid.getParameters());273encoded = c.doFinal(encryptedData);274checkPKCS8Encoding(encoded);275} catch (NoSuchAlgorithmException nsae) {276// rethrow277throw nsae;278} catch (GeneralSecurityException | IOException ex) {279throw new InvalidKeyException(280"Cannot retrieve the PKCS8EncodedKeySpec", ex);281}282return new PKCS8EncodedKeySpec(encoded);283}284285/**286* Extract the enclosed PKCS8EncodedKeySpec object from the287* encrypted data and return it.288* @param decryptKey key used for decrypting the encrypted data.289* @return the PKCS8EncodedKeySpec object.290* @exception NullPointerException if <code>decryptKey</code>291* is null.292* @exception NoSuchAlgorithmException if cannot find appropriate293* cipher to decrypt the encrypted data.294* @exception InvalidKeyException if <code>decryptKey</code>295* cannot be used to decrypt the encrypted data or the decryption296* result is not a valid PKCS8KeySpec.297*298* @since 1.5299*/300public PKCS8EncodedKeySpec getKeySpec(Key decryptKey)301throws NoSuchAlgorithmException, InvalidKeyException {302if (decryptKey == null) {303throw new NullPointerException("decryptKey is null");304}305return getKeySpecImpl(decryptKey, null);306}307308/**309* Extract the enclosed PKCS8EncodedKeySpec object from the310* encrypted data and return it.311* @param decryptKey key used for decrypting the encrypted data.312* @param providerName the name of provider whose Cipher313* implementation will be used.314* @return the PKCS8EncodedKeySpec object.315* @exception NullPointerException if <code>decryptKey</code>316* or <code>providerName</code> is null.317* @exception NoSuchProviderException if no provider318* <code>providerName</code> is registered.319* @exception NoSuchAlgorithmException if cannot find appropriate320* cipher to decrypt the encrypted data.321* @exception InvalidKeyException if <code>decryptKey</code>322* cannot be used to decrypt the encrypted data or the decryption323* result is not a valid PKCS8KeySpec.324*325* @since 1.5326*/327public PKCS8EncodedKeySpec getKeySpec(Key decryptKey,328String providerName) throws NoSuchProviderException,329NoSuchAlgorithmException, InvalidKeyException {330if (decryptKey == null) {331throw new NullPointerException("decryptKey is null");332}333if (providerName == null) {334throw new NullPointerException("provider is null");335}336Provider provider = Security.getProvider(providerName);337if (provider == null) {338throw new NoSuchProviderException("provider " +339providerName + " not found");340}341return getKeySpecImpl(decryptKey, provider);342}343344/**345* Extract the enclosed PKCS8EncodedKeySpec object from the346* encrypted data and return it.347* @param decryptKey key used for decrypting the encrypted data.348* @param provider the name of provider whose Cipher implementation349* will be used.350* @return the PKCS8EncodedKeySpec object.351* @exception NullPointerException if <code>decryptKey</code>352* or <code>provider</code> is null.353* @exception NoSuchAlgorithmException if cannot find appropriate354* cipher to decrypt the encrypted data in <code>provider</code>.355* @exception InvalidKeyException if <code>decryptKey</code>356* cannot be used to decrypt the encrypted data or the decryption357* result is not a valid PKCS8KeySpec.358*359* @since 1.5360*/361public PKCS8EncodedKeySpec getKeySpec(Key decryptKey,362Provider provider) throws NoSuchAlgorithmException,363InvalidKeyException {364if (decryptKey == null) {365throw new NullPointerException("decryptKey is null");366}367if (provider == null) {368throw new NullPointerException("provider is null");369}370return getKeySpecImpl(decryptKey, provider);371}372373/**374* Returns the ASN.1 encoding of this object.375* @return the ASN.1 encoding. Returns a new array376* each time this method is called.377* @exception IOException if error occurs when constructing its378* ASN.1 encoding.379*/380public byte[] getEncoded() throws IOException {381if (this.encoded == null) {382DerOutputStream out = new DerOutputStream();383DerOutputStream tmp = new DerOutputStream();384385// encode encryption algorithm386algid.encode(tmp);387388// encode encrypted data389tmp.putOctetString(encryptedData);390391// wrap everything into a SEQUENCE392out.write(DerValue.tag_Sequence, tmp);393this.encoded = out.toByteArray();394}395return this.encoded.clone();396}397398private static void checkTag(DerValue val, byte tag, String valName)399throws IOException {400if (val.getTag() != tag) {401throw new IOException("invalid key encoding - wrong tag for " +402valName);403}404}405406@SuppressWarnings("fallthrough")407private static void checkPKCS8Encoding(byte[] encodedKey)408throws IOException {409DerInputStream in = new DerInputStream(encodedKey);410DerValue[] values = in.getSequence(3);411412switch (values.length) {413case 4:414checkTag(values[3], DerValue.TAG_CONTEXT, "attributes");415/* fall through */416case 3:417checkTag(values[0], DerValue.tag_Integer, "version");418DerInputStream algid = values[1].toDerInputStream();419algid.getOID();420if (algid.available() != 0) {421algid.getDerValue();422}423checkTag(values[2], DerValue.tag_OctetString, "privateKey");424break;425default:426throw new IOException("invalid key encoding");427}428}429}430431432