Path: blob/master/src/java.base/share/classes/javax/crypto/EncryptedPrivateKeyInfo.java
41152 views
/*1* Copyright (c) 2001, 2017, 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)80throws IOException {81if (encoded == null) {82throw new NullPointerException("the encoded parameter " +83"must be non-null");84}85this.encoded = encoded.clone();86DerValue val = new DerValue(this.encoded);8788DerValue[] seq = new DerValue[2];8990seq[0] = val.data.getDerValue();91seq[1] = val.data.getDerValue();9293if (val.data.available() != 0) {94throw new IOException("overrun, bytes = " + val.data.available());95}9697this.algid = AlgorithmId.parse(seq[0]);98if (seq[0].data.available() != 0) {99throw new IOException("encryptionAlgorithm field overrun");100}101102this.encryptedData = seq[1].getOctetString();103if (seq[1].data.available() != 0) {104throw new IOException("encryptedData field overrun");105}106}107108/**109* Constructs an <code>EncryptedPrivateKeyInfo</code> from the110* encryption algorithm name and the encrypted data.111*112* <p>Note: This constructor will use null as the value of the113* algorithm parameters. If the encryption algorithm has114* parameters whose value is not null, a different constructor,115* e.g. EncryptedPrivateKeyInfo(AlgorithmParameters, byte[]),116* should be used.117*118* @param algName encryption algorithm name. See the119* <a href="{@docRoot}/../specs/security/standard-names.html">120* Java Security Standard Algorithm Names</a> document121* for information about standard Cipher algorithm names.122* @param encryptedData encrypted data. The contents of123* <code>encrypedData</code> are copied to protect against subsequent124* modification when constructing this object.125* @exception NullPointerException if <code>algName</code> or126* <code>encryptedData</code> is null.127* @exception IllegalArgumentException if <code>encryptedData</code>128* is empty, i.e. 0-length.129* @exception NoSuchAlgorithmException if the specified algName is130* not supported.131*/132public EncryptedPrivateKeyInfo(String algName, byte[] encryptedData)133throws NoSuchAlgorithmException {134135if (algName == null)136throw new NullPointerException("the algName parameter " +137"must be non-null");138this.algid = AlgorithmId.get(algName);139140if (encryptedData == null) {141throw new NullPointerException("the encryptedData " +142"parameter must be non-null");143} else if (encryptedData.length == 0) {144throw new IllegalArgumentException("the encryptedData " +145"parameter must not be empty");146} else {147this.encryptedData = encryptedData.clone();148}149// delay the generation of ASN.1 encoding until150// getEncoded() is called151this.encoded = null;152}153154/**155* Constructs an <code>EncryptedPrivateKeyInfo</code> from the156* encryption algorithm parameters and the encrypted data.157*158* @param algParams the algorithm parameters for the encryption159* algorithm. <code>algParams.getEncoded()</code> should return160* the ASN.1 encoded bytes of the <code>parameters</code> field161* of the <code>AlgorithmIdentifer</code> component of the162* <code>EncryptedPrivateKeyInfo</code> type.163* @param encryptedData encrypted data. The contents of164* <code>encrypedData</code> are copied to protect against165* subsequent modification when constructing this object.166* @exception NullPointerException if <code>algParams</code> or167* <code>encryptedData</code> is null.168* @exception IllegalArgumentException if <code>encryptedData</code>169* is empty, i.e. 0-length.170* @exception NoSuchAlgorithmException if the specified algName of171* the specified <code>algParams</code> parameter is not supported.172*/173public EncryptedPrivateKeyInfo(AlgorithmParameters algParams,174byte[] encryptedData) throws NoSuchAlgorithmException {175176if (algParams == null) {177throw new NullPointerException("algParams must be non-null");178}179this.algid = AlgorithmId.get(algParams);180181if (encryptedData == null) {182throw new NullPointerException("encryptedData must be non-null");183} else if (encryptedData.length == 0) {184throw new IllegalArgumentException("the encryptedData " +185"parameter must not be empty");186} else {187this.encryptedData = encryptedData.clone();188}189190// delay the generation of ASN.1 encoding until191// getEncoded() is called192this.encoded = null;193}194195196/**197* Returns the encryption algorithm.198* <p>Note: Standard name is returned instead of the specified one199* in the constructor when such mapping is available.200* See the <a href="{@docRoot}/../specs/security/standard-names.html">201* Java Security Standard Algorithm Names</a> document202* 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, keyAlg);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, keyAlg);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 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");418keyAlg = AlgorithmId.parse(values[1]).getName();419checkTag(values[2], DerValue.tag_OctetString, "privateKey");420break;421default:422throw new IOException("invalid key encoding");423}424}425}426427428