/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package javax.crypto; import java.io.IOException; import java.security.AlgorithmParameters; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.Key; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.Provider; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import org.apache.harmony.security.asn1.ASN1Any; import org.apache.harmony.security.asn1.ASN1Implicit; import org.apache.harmony.security.asn1.ASN1Integer; import org.apache.harmony.security.asn1.ASN1OctetString; import org.apache.harmony.security.asn1.ASN1Sequence; import org.apache.harmony.security.asn1.ASN1SetOf; import org.apache.harmony.security.asn1.ASN1Type; import org.apache.harmony.security.utils.AlgNameMapper; import org.apache.harmony.security.x509.AlgorithmIdentifier; /** * This class implements the {@code EncryptedPrivateKeyInfo} ASN.1 type as * specified in PKCS * #8 - Private-Key Information Syntax Standard. *
* The definition of ASN.1 is as follows: *
* The cipher must be initialize in either {@code Cipher.DECRYPT_MODE} or * {@code Cipher.UNWRAP_MODE} with the same parameters and key used for * encrypting this. * * @param cipher * the cipher initialized for decrypting the encrypted data. * @return the extracted {@code PKCS8EncodedKeySpec}. * @throws InvalidKeySpecException * if the specified cipher is not suited to decrypt the * encrypted data. * @throws NullPointerException * if {@code cipher} is {@code null}. */ public PKCS8EncodedKeySpec getKeySpec(Cipher cipher) throws InvalidKeySpecException { if (cipher == null) { throw new NullPointerException("cipher == null"); } try { byte[] decryptedData = cipher.doFinal(encryptedData); try { ASN1PrivateKeyInfo.verify(decryptedData); } catch (IOException e1) { throw new InvalidKeySpecException("Decrypted data does not represent valid PKCS#8 PrivateKeyInfo"); } return new PKCS8EncodedKeySpec(decryptedData); } catch (IllegalStateException e) { throw new InvalidKeySpecException(e.getMessage()); } catch (IllegalBlockSizeException e) { throw new InvalidKeySpecException(e.getMessage()); } catch (BadPaddingException e) { throw new InvalidKeySpecException(e.getMessage()); } } /** * Returns the {@code PKCS8EncodedKeySpec} object extracted from the * encrypted data. * * @param decryptKey * the key to decrypt the encrypted data with. * @return the extracted {@code PKCS8EncodedKeySpec}. * @throws NoSuchAlgorithmException * if no usable cipher can be found to decrypt the encrypted * data. * @throws InvalidKeyException * if {@code decryptKey} is not usable to decrypt the encrypted * data. * @throws NullPointerException * if {@code decryptKey} is {@code null}. */ public PKCS8EncodedKeySpec getKeySpec(Key decryptKey) throws NoSuchAlgorithmException, InvalidKeyException { if (decryptKey == null) { throw new NullPointerException("decryptKey == null"); } try { Cipher cipher = Cipher.getInstance(algName); if (algParameters == null) { cipher.init(Cipher.DECRYPT_MODE, decryptKey); } else { cipher.init(Cipher.DECRYPT_MODE, decryptKey, algParameters); } byte[] decryptedData = cipher.doFinal(encryptedData); try { ASN1PrivateKeyInfo.verify(decryptedData); } catch (IOException e1) { throw invalidKey(); } return new PKCS8EncodedKeySpec(decryptedData); } catch (NoSuchPaddingException e) { throw new NoSuchAlgorithmException(e.getMessage()); } catch (InvalidAlgorithmParameterException e) { throw new NoSuchAlgorithmException(e.getMessage()); } catch (IllegalStateException e) { throw new InvalidKeyException(e.getMessage()); } catch (IllegalBlockSizeException e) { throw new InvalidKeyException(e.getMessage()); } catch (BadPaddingException e) { throw new InvalidKeyException(e.getMessage()); } } /** * Returns the {@code PKCS8EncodedKeySpec} object extracted from the * encrypted data. * * @param decryptKey * the key to decrypt the encrypted data with. * @param providerName * the name of a provider whose cipher implementation should be * used. * @return the extracted {@code PKCS8EncodedKeySpec}. * @throws NoSuchProviderException * if no provider with {@code providerName} can be found. * @throws NoSuchAlgorithmException * if no usable cipher can be found to decrypt the encrypted * data. * @throws InvalidKeyException * if {@code decryptKey} is not usable to decrypt the encrypted * data. * @throws NullPointerException * if {@code decryptKey} or {@code providerName} is {@code null} * . */ public PKCS8EncodedKeySpec getKeySpec(Key decryptKey, String providerName) throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeyException { if (decryptKey == null) { throw new NullPointerException("decryptKey == null"); } if (providerName == null) { throw new NullPointerException("providerName == null"); } try { Cipher cipher = Cipher.getInstance(algName, providerName); if (algParameters == null) { cipher.init(Cipher.DECRYPT_MODE, decryptKey); } else { cipher.init(Cipher.DECRYPT_MODE, decryptKey, algParameters); } byte[] decryptedData = cipher.doFinal(encryptedData); try { ASN1PrivateKeyInfo.verify(decryptedData); } catch (IOException e1) { throw invalidKey(); } return new PKCS8EncodedKeySpec(decryptedData); } catch (NoSuchPaddingException e) { throw new NoSuchAlgorithmException(e.getMessage()); } catch (InvalidAlgorithmParameterException e) { throw new NoSuchAlgorithmException(e.getMessage()); } catch (IllegalStateException e) { throw new InvalidKeyException(e.getMessage()); } catch (IllegalBlockSizeException e) { throw new InvalidKeyException(e.getMessage()); } catch (BadPaddingException e) { throw new InvalidKeyException(e.getMessage()); } } /** * Returns the {@code PKCS8EncodedKeySpec} object extracted from the * encrypted data. * * @param decryptKey * the key to decrypt the encrypted data with. * @param provider * the provider whose cipher implementation should be used. * @return the extracted {@code PKCS8EncodedKeySpec}. * @throws NoSuchAlgorithmException * if no usable cipher can be found to decrypt the encrypted * data. * @throws InvalidKeyException * if {@code decryptKey} is not usable to decrypt the encrypted * data. * @throws NullPointerException * if {@code decryptKey} or {@code provider} is {@code null}. */ public PKCS8EncodedKeySpec getKeySpec(Key decryptKey, Provider provider) throws NoSuchAlgorithmException, InvalidKeyException { if (decryptKey == null) { throw new NullPointerException("decryptKey == null"); } if (provider == null) { throw new NullPointerException("provider == null"); } try { Cipher cipher = Cipher.getInstance(algName, provider); if (algParameters == null) { cipher.init(Cipher.DECRYPT_MODE, decryptKey); } else { cipher.init(Cipher.DECRYPT_MODE, decryptKey, algParameters); } byte[] decryptedData = cipher.doFinal(encryptedData); try { ASN1PrivateKeyInfo.verify(decryptedData); } catch (IOException e1) { throw invalidKey(); } return new PKCS8EncodedKeySpec(decryptedData); } catch (NoSuchPaddingException e) { throw new NoSuchAlgorithmException(e.getMessage()); } catch (InvalidAlgorithmParameterException e) { throw new NoSuchAlgorithmException(e.getMessage()); } catch (IllegalStateException e) { throw new InvalidKeyException(e.getMessage()); } catch (IllegalBlockSizeException e) { throw new InvalidKeyException(e.getMessage()); } catch (BadPaddingException e) { throw new InvalidKeyException(e.getMessage()); } } private InvalidKeyException invalidKey() throws InvalidKeyException { throw new InvalidKeyException("Decrypted data does not represent valid PKCS#8 PrivateKeyInfo"); } /** * Returns the ASN.1 encoded representation of this object. * * @return the ASN.1 encoded representation of this object. * @throws IOException * if encoding this object fails. */ public byte[] getEncoded() throws IOException { if (encoded == null) { // Generate ASN.1 encoding: encoded = asn1.encode(this); } byte[] ret = new byte[encoded.length]; System.arraycopy(encoded, 0, ret, 0, encoded.length); return ret; } // Performs all needed alg name mappings. // Returns 'true' if mapping available 'false' otherwise private boolean mapAlgName() { if (AlgNameMapper.isOID(this.algName)) { // OID provided to the ctor // get rid of possible leading "OID." this.oid = AlgNameMapper.normalize(this.algName); // try to find mapping OID->algName this.algName = AlgNameMapper.map2AlgName(this.oid); // if there is no mapping OID->algName // set OID as algName if (this.algName == null) { this.algName = this.oid; } } else { String stdName = AlgNameMapper.getStandardName(this.algName); // Alg name provided to the ctor // try to find mapping algName->OID or // (algName->stdAlgName)->OID this.oid = AlgNameMapper.map2OID(this.algName); if (this.oid == null) { if (stdName == null) { // no above mappings available return false; } this.oid = AlgNameMapper.map2OID(stdName); if (this.oid == null) { return false; } this.algName = stdName; } else if (stdName != null) { this.algName = stdName; } } return true; } // // EncryptedPrivateKeyInfo DER encoder/decoder. // EncryptedPrivateKeyInfo ASN.1 definition // (as defined in PKCS #8: Private-Key Information Syntax Standard // http://www.ietf.org/rfc/rfc2313.txt) // // EncryptedPrivateKeyInfo ::= SEQUENCE { // encryptionAlgorithm AlgorithmIdentifier, // encryptedData OCTET STRING } // private static final byte[] nullParam = new byte[] { 5, 0 }; private static final ASN1Sequence asn1 = new ASN1Sequence(new ASN1Type[] { AlgorithmIdentifier.ASN1, ASN1OctetString.getInstance() }) { @Override protected void getValues(Object object, Object[] values) { EncryptedPrivateKeyInfo epki = (EncryptedPrivateKeyInfo) object; try { byte[] algParmsEncoded = (epki.algParameters == null) ? nullParam : epki.algParameters.getEncoded(); values[0] = new AlgorithmIdentifier(epki.oid, algParmsEncoded); values[1] = epki.encryptedData; } catch (IOException e) { throw new RuntimeException(e.getMessage()); } } }; // PrivateKeyInfo DER decoder. // PrivateKeyInfo ASN.1 definition // (as defined in PKCS #8: Private-Key Information Syntax Standard // http://www.ietf.org/rfc/rfc2313.txt) // // // PrivateKeyInfo ::= SEQUENCE { // version Version, // privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, // privateKey PrivateKey, // attributes [0] IMPLICIT Attributes OPTIONAL } // // Version ::= INTEGER // // PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier // // PrivateKey ::= OCTET STRING // // Attributes ::= SET OF Attribute private static final ASN1SetOf ASN1Attributes = new ASN1SetOf(ASN1Any.getInstance()); private static final ASN1Sequence ASN1PrivateKeyInfo = new ASN1Sequence( new ASN1Type[] { ASN1Integer.getInstance(), AlgorithmIdentifier.ASN1, ASN1OctetString.getInstance(), new ASN1Implicit(0, ASN1Attributes) }) { { setOptional(3); //attributes are optional } }; }