KeyPairPKCS8.java
/*
* Copyright (c) 2013-2018 ymnk, JCraft,Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted
* provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions
* and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other materials provided with
* the distribution.
*
* 3. The names of the authors may not be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jcraft.jsch;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
class KeyPairPKCS8 extends KeyPair {
private static final byte[] rsaEncryption = {(byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86,
(byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01, (byte) 0x01};
private static final byte[] dsaEncryption =
{(byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0xce, (byte) 0x38, (byte) 0x04, (byte) 0x01};
private static final byte[] ecPublicKey =
{(byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0xce, (byte) 0x3d, (byte) 0x02, (byte) 0x01};
private static final byte[] ed25519 = {(byte) 0x2b, (byte) 0x65, (byte) 0x70};
private static final byte[] ed448 = {(byte) 0x2b, (byte) 0x65, (byte) 0x71};
private static final byte[] secp256r1 = {(byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0xce,
(byte) 0x3d, (byte) 0x03, (byte) 0x01, (byte) 0x07};
private static final byte[] secp384r1 =
{(byte) 0x2b, (byte) 0x81, (byte) 0x04, (byte) 0x00, (byte) 0x22};
private static final byte[] secp521r1 =
{(byte) 0x2b, (byte) 0x81, (byte) 0x04, (byte) 0x00, (byte) 0x23};
private static final byte[] pbes2 = {(byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86,
(byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x05, (byte) 0x0d};
private static final byte[] pbkdf2 = {(byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86,
(byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x05, (byte) 0x0c};
private static final byte[] scrypt = {(byte) 0x2b, (byte) 0x06, (byte) 0x01, (byte) 0x04,
(byte) 0x01, (byte) 0xda, (byte) 0x47, (byte) 0x04, (byte) 0x0b};
private static final byte[] hmacWithSha1 = {(byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86,
(byte) 0xf7, (byte) 0x0d, (byte) 0x02, (byte) 0x07};
private static final byte[] hmacWithSha224 = {(byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86,
(byte) 0xf7, (byte) 0x0d, (byte) 0x02, (byte) 0x08};
private static final byte[] hmacWithSha256 = {(byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86,
(byte) 0xf7, (byte) 0x0d, (byte) 0x02, (byte) 0x09};
private static final byte[] hmacWithSha384 = {(byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86,
(byte) 0xf7, (byte) 0x0d, (byte) 0x02, (byte) 0x0a};
private static final byte[] hmacWithSha512 = {(byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86,
(byte) 0xf7, (byte) 0x0d, (byte) 0x02, (byte) 0x0b};
private static final byte[] hmacWithSha512224 = {(byte) 0x2a, (byte) 0x86, (byte) 0x48,
(byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x02, (byte) 0x0c};
private static final byte[] hmacWithSha512256 = {(byte) 0x2a, (byte) 0x86, (byte) 0x48,
(byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x02, (byte) 0x0d};
private static final byte[] aes128cbc = {(byte) 0x60, (byte) 0x86, (byte) 0x48, (byte) 0x01,
(byte) 0x65, (byte) 0x03, (byte) 0x04, (byte) 0x01, (byte) 0x02};
private static final byte[] aes192cbc = {(byte) 0x60, (byte) 0x86, (byte) 0x48, (byte) 0x01,
(byte) 0x65, (byte) 0x03, (byte) 0x04, (byte) 0x01, (byte) 0x16};
private static final byte[] aes256cbc = {(byte) 0x60, (byte) 0x86, (byte) 0x48, (byte) 0x01,
(byte) 0x65, (byte) 0x03, (byte) 0x04, (byte) 0x01, (byte) 0x2a};
private static final byte[] descbc =
{(byte) 0x2b, (byte) 0x0e, (byte) 0x03, (byte) 0x02, (byte) 0x07};
private static final byte[] des3cbc = {(byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86,
(byte) 0xf7, (byte) 0x0d, (byte) 0x03, (byte) 0x07};
private static final byte[] rc2cbc = {(byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86,
(byte) 0xf7, (byte) 0x0d, (byte) 0x03, (byte) 0x02};
private static final byte[] rc5cbc = {(byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86,
(byte) 0xf7, (byte) 0x0d, (byte) 0x03, (byte) 0x09};
private static final byte[] pbeWithMD2AndDESCBC = {(byte) 0x2a, (byte) 0x86, (byte) 0x48,
(byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x05, (byte) 0x01};
private static final byte[] pbeWithMD2AndRC2CBC = {(byte) 0x2a, (byte) 0x86, (byte) 0x48,
(byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x05, (byte) 0x04};
private static final byte[] pbeWithMD5AndDESCBC = {(byte) 0x2a, (byte) 0x86, (byte) 0x48,
(byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x05, (byte) 0x03};
private static final byte[] pbeWithMD5AndRC2CBC = {(byte) 0x2a, (byte) 0x86, (byte) 0x48,
(byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x05, (byte) 0x06};
private static final byte[] pbeWithSHA1AndDESCBC = {(byte) 0x2a, (byte) 0x86, (byte) 0x48,
(byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x05, (byte) 0x0a};
private static final byte[] pbeWithSHA1AndRC2CBC = {(byte) 0x2a, (byte) 0x86, (byte) 0x48,
(byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x05, (byte) 0x0b};
private KeyPair kpair = null;
KeyPairPKCS8(JSch.InstanceLogger instLogger) {
super(instLogger);
}
@Override
void generate(int key_size) throws JSchException {}
private static final byte[] begin = Util.str2byte("-----BEGIN DSA PRIVATE KEY-----");
private static final byte[] end = Util.str2byte("-----END DSA PRIVATE KEY-----");
@Override
byte[] getBegin() {
return begin;
}
@Override
byte[] getEnd() {
return end;
}
@Override
byte[] getPrivateKey() {
return null;
}
@Override
boolean parse(byte[] plain) {
/*
* from RFC5208 PrivateKeyInfo ::= SEQUENCE { version Version, privateKeyAlgorithm
* PrivateKeyAlgorithmIdentifier, privateKey PrivateKey, attributes [0] IMPLICIT Attributes
* OPTIONAL } Version ::= INTEGER PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
* PrivateKey ::= OCTET STRING Attributes ::= SET OF Attribute }
*/
byte[] _data = null;
byte[] prv_array = null;
byte[] _plain = null;
KeyPair _key = null;
try {
ASN1[] contents;
ASN1 asn1 = new ASN1(plain);
if (!asn1.isSEQUENCE()) {
throw new ASN1Exception();
}
contents = asn1.getContents();
if (contents.length < 3 || contents.length > 4) {
throw new ASN1Exception();
}
if (!contents[0].isINTEGER()) {
throw new ASN1Exception();
}
if (!contents[1].isSEQUENCE()) {
throw new ASN1Exception();
}
if (!contents[2].isOCTETSTRING()) {
throw new ASN1Exception();
}
// attributes [0] IMPLICIT Attributes OPTIONAL
if (contents.length > 3 && !contents[3].isCONTEXTCONSTRUCTED(0)) {
throw new ASN1Exception();
}
int version = parseASN1IntegerAsInt(contents[0].getContent());
if (version != 0) {
throw new ASN1Exception();
}
ASN1 privateKeyAlgorithm = contents[1];
ASN1 privateKey = contents[2];
contents = privateKeyAlgorithm.getContents();
if (contents.length == 0) {
throw new ASN1Exception();
}
if (!contents[0].isOBJECT()) {
throw new ASN1Exception();
}
byte[] privateKeyAlgorithmID = contents[0].getContent();
_data = privateKey.getContent();
KeyPair _kpair = null;
if (Util.array_equals(privateKeyAlgorithmID, rsaEncryption)) {
if (contents.length != 2) {
throw new ASN1Exception();
}
if (!contents[1].isNULL()) {
throw new ASN1Exception();
}
_kpair = new KeyPairRSA(instLogger);
_kpair.copy(this);
if (_kpair.parse(_data)) {
kpair = _kpair;
return true;
} else {
throw new JSchException("failed to parse RSA");
}
} else if (Util.array_equals(privateKeyAlgorithmID, dsaEncryption)) {
List<byte[]> values = new ArrayList<>(3);
if (contents.length > 1 && contents[1].isSEQUENCE()) {
contents = contents[1].getContents();
if (contents.length != 3) {
throw new ASN1Exception();
}
if (!contents[0].isINTEGER()) {
throw new ASN1Exception();
}
if (!contents[1].isINTEGER()) {
throw new ASN1Exception();
}
if (!contents[2].isINTEGER()) {
throw new ASN1Exception();
}
values.add(contents[0].getContent());
values.add(contents[1].getContent());
values.add(contents[2].getContent());
}
asn1 = new ASN1(_data);
if (values.size() == 0) { // embedded DSA parameters format
/*
* SEQUENCE SEQUENCE INTEGER // P_array INTEGER // Q_array INTEGER // G_array INTEGER //
* prv_array
*/
if (!asn1.isSEQUENCE()) {
throw new ASN1Exception();
}
contents = asn1.getContents();
if (contents.length != 2) {
throw new ASN1Exception();
}
if (!contents[0].isSEQUENCE()) {
throw new ASN1Exception();
}
if (!contents[1].isINTEGER()) {
throw new ASN1Exception();
}
prv_array = contents[1].getContent();
contents = contents[0].getContents();
if (contents.length != 3) {
throw new ASN1Exception();
}
if (!contents[0].isINTEGER()) {
throw new ASN1Exception();
}
if (!contents[1].isINTEGER()) {
throw new ASN1Exception();
}
if (!contents[2].isINTEGER()) {
throw new ASN1Exception();
}
values.add(contents[0].getContent());
values.add(contents[1].getContent());
values.add(contents[2].getContent());
} else {
/*
* INTEGER // prv_array
*/
if (!asn1.isINTEGER()) {
throw new ASN1Exception();
}
prv_array = asn1.getContent();
}
byte[] P_array = values.get(0);
byte[] Q_array = values.get(1);
byte[] G_array = values.get(2);
// Y = g^X mode p
byte[] pub_array = (new BigInteger(G_array))
.modPow(new BigInteger(prv_array), new BigInteger(P_array)).toByteArray();
_key = new KeyPairDSA(instLogger, P_array, Q_array, G_array, pub_array, prv_array);
_plain = _key.getPrivateKey();
_kpair = new KeyPairDSA(instLogger);
_kpair.copy(this);
if (_kpair.parse(_plain)) {
kpair = _kpair;
return true;
} else {
throw new JSchException("failed to parse DSA");
}
} else if (Util.array_equals(privateKeyAlgorithmID, ecPublicKey)) {
if (contents.length != 2) {
throw new ASN1Exception();
}
if (!contents[1].isOBJECT()) {
throw new ASN1Exception();
}
byte[] namedCurve = contents[1].getContent();
byte[] name;
if (!Util.array_equals(namedCurve, secp256r1)) {
name = Util.str2byte("nistp256");
} else if (!Util.array_equals(namedCurve, secp384r1)) {
name = Util.str2byte("nistp384");
} else if (!Util.array_equals(namedCurve, secp521r1)) {
name = Util.str2byte("nistp521");
} else {
throw new JSchException("unsupported named curve oid: " + Util.toHex(namedCurve));
}
ASN1 ecPrivateKey = new ASN1(_data);
if (!ecPrivateKey.isSEQUENCE()) {
throw new ASN1Exception();
}
// ECPrivateKey ::= SEQUENCE {
// version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
// privateKey OCTET STRING,
// parameters [0] ECParameters {{ NamedCurve }} OPTIONAL,
// publicKey [1] BIT STRING OPTIONAL
// }
contents = ecPrivateKey.getContents();
if (contents.length < 3 || contents.length > 4) {
throw new ASN1Exception();
}
if (!contents[0].isINTEGER()) {
throw new ASN1Exception();
}
if (!contents[1].isOCTETSTRING()) {
throw new ASN1Exception();
}
version = parseASN1IntegerAsInt(contents[0].getContent());
if (version != 1) {
throw new ASN1Exception();
}
prv_array = contents[1].getContent();
// publicKey is required here since there is no other way to derive it.
ASN1 publicKey;
if (contents.length == 3) {
publicKey = contents[2];
} else {
publicKey = contents[3];
// parameters [0] ECParameters {{ NamedCurve }} OPTIONAL
if (!contents[2].isCONTEXTCONSTRUCTED(0)) {
throw new ASN1Exception();
}
// NamedCurve isn't required here since it is already known.
// But if it is included, they should be the same...
ASN1[] goo = contents[2].getContents();
if (goo.length != 1) {
throw new ASN1Exception();
}
if (!goo[0].isOBJECT()) {
throw new ASN1Exception();
}
if (!Util.array_equals(goo[0].getContent(), namedCurve)) {
throw new ASN1Exception();
}
}
// publicKey [1] BIT STRING OPTIONAL
if (!publicKey.isCONTEXTCONSTRUCTED(1)) {
throw new ASN1Exception();
}
contents = publicKey.getContents();
if (contents.length != 1) {
throw new ASN1Exception();
}
if (!contents[0].isBITSTRING()) {
throw new ASN1Exception();
}
byte[] Q_array = contents[0].getContent();
byte[][] tmp = KeyPairECDSA.fromPoint(Q_array);
byte[] r_array = tmp[0];
byte[] s_array = tmp[1];
_key = new KeyPairECDSA(instLogger, name, r_array, s_array, prv_array);
_plain = _key.getPrivateKey();
_kpair = new KeyPairECDSA(instLogger);
_kpair.copy(this);
if (_kpair.parse(_plain)) {
kpair = _kpair;
return true;
} else {
throw new JSchException("failed to parse ECDSA");
}
} else if (Util.array_equals(privateKeyAlgorithmID, ed25519)
|| Util.array_equals(privateKeyAlgorithmID, ed448)) {
if (contents.length != 1) {
throw new ASN1Exception();
}
ASN1 curvePrivateKey = new ASN1(_data);
if (!curvePrivateKey.isOCTETSTRING()) {
throw new ASN1Exception();
}
prv_array = curvePrivateKey.getContent();
if (Util.array_equals(privateKeyAlgorithmID, ed25519)) {
_kpair = new KeyPairEd25519(instLogger);
} else {
_kpair = new KeyPairEd448(instLogger);
}
_kpair.copy(this);
if (_kpair.parse(prv_array)) {
kpair = _kpair;
return true;
} else {
throw new JSchException("failed to parse EdDSA");
}
} else {
throw new JSchException(
"unsupported privateKeyAlgorithm oid: " + Util.toHex(privateKeyAlgorithmID));
}
} catch (ASN1Exception e) {
if (instLogger.getLogger().isEnabled(Logger.ERROR)) {
instLogger.getLogger().log(Logger.ERROR, "PKCS8: failed to parse key: ASN1 parsing error",
e);
}
return false;
} catch (Exception e) {
if (instLogger.getLogger().isEnabled(Logger.ERROR)) {
instLogger.getLogger().log(Logger.ERROR, "PKCS8: failed to parse key: " + e.getMessage(),
e);
}
return false;
} finally {
Util.bzero(_data);
Util.bzero(prv_array);
Util.bzero(_plain);
if (_key != null) {
_key.dispose();
}
}
}
@Override
public byte[] getPublicKeyBlob() {
if (kpair != null) {
return kpair.getPublicKeyBlob();
} else {
return super.getPublicKeyBlob();
}
}
@Override
byte[] getKeyTypeName() {
if (kpair != null) {
return kpair.getKeyTypeName();
} else {
return new byte[0];
}
}
@Override
public int getKeyType() {
if (kpair != null) {
return kpair.getKeyType();
} else {
return UNKNOWN;
}
}
@Override
public int getKeySize() {
return kpair.getKeySize();
}
@Override
public byte[] getSignature(byte[] data) {
return kpair.getSignature(data);
}
@Override
public byte[] getSignature(byte[] data, String alg) {
return kpair.getSignature(data, alg);
}
@Override
public Signature getVerifier() {
return kpair.getVerifier();
}
@Override
public Signature getVerifier(String alg) {
return kpair.getVerifier(alg);
}
@Override
public byte[] forSSHAgent() throws JSchException {
return kpair.forSSHAgent();
}
@Override
public boolean decrypt(byte[] _passphrase) {
if (!isEncrypted()) {
return true;
}
if (_passphrase == null) {
return !isEncrypted();
}
/*
* SEQUENCE SEQUENCE OBJECT :PBES2 SEQUENCE SEQUENCE OBJECT :PBKDF2 SEQUENCE OCTET STRING [HEX
* DUMP]:E4E24ADC9C00BD4D INTEGER :0800 SEQUENCE OBJECT :aes-128-cbc OCTET STRING [HEX
* DUMP]:5B66E6B3BF03944C92317BC370CC3AD0 OCTET STRING [HEX DUMP]:
*
* or
*
* SEQUENCE SEQUENCE OBJECT :PBES2 SEQUENCE SEQUENCE OBJECT :PBKDF2 SEQUENCE OCTET STRING [HEX
* DUMP]:E4E24ADC9C00BD4D INTEGER :0800 SEQUENCE OBJECT :hmacWithSHA256 NULL SEQUENCE OBJECT
* :aes-128-cbc OCTET STRING [HEX DUMP]:5B66E6B3BF03944C92317BC370CC3AD0 OCTET STRING [HEX
* DUMP]:
*
* or
*
* SEQUENCE SEQUENCE OBJECT :pbeWithMD5AndDES-CBC SEQUENCE OCTET STRING [HEX
* DUMP]:DBF75ECB69E3C0FC INTEGER :0800 OCTET STRING [HEX DUMP]
*/
byte[] _data = null;
byte[] key = null;
byte[] plain = null;
try {
ASN1[] contents;
ASN1 asn1 = new ASN1(data);
if (!asn1.isSEQUENCE()) {
throw new ASN1Exception();
}
contents = asn1.getContents();
if (contents.length != 2) {
throw new ASN1Exception();
}
if (!contents[0].isSEQUENCE()) {
throw new ASN1Exception();
}
if (!contents[1].isOCTETSTRING()) {
throw new ASN1Exception();
}
_data = contents[1].getContent();
ASN1 pbes = contents[0];
contents = pbes.getContents();
if (contents.length != 2) {
throw new ASN1Exception();
}
if (!contents[0].isOBJECT()) {
throw new ASN1Exception();
}
if (!contents[1].isSEQUENCE()) {
throw new ASN1Exception();
}
byte[] pbesid = contents[0].getContent();
ASN1 pbesparam = contents[1];
String kdfname;
KDF kdfinst;
byte[] encryptfuncid;
ASN1 encryptparams;
if (Util.array_equals(pbesid, pbes2)) {
contents = pbesparam.getContents();
if (contents.length != 2) {
throw new ASN1Exception();
}
ASN1 kdf = contents[0];
ASN1 encryptfunc = contents[1];
if (!kdf.isSEQUENCE()) {
throw new ASN1Exception();
}
if (!encryptfunc.isSEQUENCE()) {
throw new ASN1Exception();
}
contents = encryptfunc.getContents();
if (contents.length != 2) {
throw new ASN1Exception();
}
if (!contents[0].isOBJECT()) {
throw new ASN1Exception();
}
encryptfuncid = contents[0].getContent();
encryptparams = contents[1];
contents = kdf.getContents();
if (contents.length != 2) {
throw new ASN1Exception();
}
if (!contents[0].isOBJECT()) {
throw new ASN1Exception();
}
if (!contents[1].isSEQUENCE()) {
throw new ASN1Exception();
}
byte[] kdfid = contents[0].getContent();
if (Util.array_equals(kdfid, pbkdf2)) {
ASN1 pbkdf2func = contents[1];
if (!pbkdf2func.isSEQUENCE()) {
throw new ASN1Exception();
}
ASN1 prf = null;
contents = pbkdf2func.getContents();
if (contents.length < 2 || contents.length > 4) {
throw new ASN1Exception();
}
if (!contents[0].isOCTETSTRING()) {
throw new ASN1Exception();
}
if (!contents[1].isINTEGER()) {
throw new ASN1Exception();
}
if (contents.length == 4) {
if (!contents[2].isINTEGER()) {
throw new ASN1Exception();
}
if (!contents[3].isSEQUENCE()) {
throw new ASN1Exception();
}
prf = contents[3];
} else if (contents.length == 3) {
if (contents[2].isSEQUENCE()) {
prf = contents[2];
} else if (!contents[2].isINTEGER()) {
throw new ASN1Exception();
}
}
byte[] prfid = null;
byte[] salt = contents[0].getContent();
int iterations = parseASN1IntegerAsInt(contents[1].getContent());
if (prf != null) {
contents = prf.getContents();
if (contents.length != 2) {
throw new ASN1Exception();
}
if (!contents[0].isOBJECT()) {
throw new ASN1Exception();
}
if (!contents[1].isNULL()) {
throw new ASN1Exception();
}
prfid = contents[0].getContent();
}
kdfname = getPBKDF2Name(prfid);
PBKDF2 pbkdf2kdf = getPBKDF2(kdfname);
pbkdf2kdf.init(salt, iterations);
kdfinst = pbkdf2kdf;
} else if (Util.array_equals(kdfid, scrypt)) {
contents = contents[1].getContents();
if (contents.length < 4 || contents.length > 5) {
throw new ASN1Exception();
}
if (!contents[0].isOCTETSTRING()) {
throw new ASN1Exception();
}
if (!contents[1].isINTEGER()) {
throw new ASN1Exception();
}
if (!contents[2].isINTEGER()) {
throw new ASN1Exception();
}
if (!contents[3].isINTEGER()) {
throw new ASN1Exception();
}
if (contents.length > 4 && !contents[4].isINTEGER()) {
throw new ASN1Exception();
}
byte[] salt = contents[0].getContent();
int cost = parseASN1IntegerAsInt(contents[1].getContent());
int blocksize = parseASN1IntegerAsInt(contents[2].getContent());
int parallel = parseASN1IntegerAsInt(contents[3].getContent());
kdfname = "scrypt";
SCrypt scryptkdf = getSCrypt();
scryptkdf.init(salt, cost, blocksize, parallel);
kdfinst = scryptkdf;
} else {
throw new JSchException("unsupported kdf oid: " + Util.toHex(kdfid));
}
} else {
String message;
if (Util.array_equals(pbesid, pbeWithMD2AndDESCBC)) {
message = "pbeWithMD2AndDES-CBC unsupported";
} else if (Util.array_equals(pbesid, pbeWithMD2AndRC2CBC)) {
message = "pbeWithMD2AndRC2-CBC unsupported";
} else if (Util.array_equals(pbesid, pbeWithMD5AndDESCBC)) {
message = "pbeWithMD5AndDES-CBC unsupported";
} else if (Util.array_equals(pbesid, pbeWithMD5AndRC2CBC)) {
message = "pbeWithMD5AndRC2-CBC unsupported";
} else if (Util.array_equals(pbesid, pbeWithSHA1AndDESCBC)) {
message = "pbeWithSHA1AndDES-CBC unsupported";
} else if (Util.array_equals(pbesid, pbeWithSHA1AndRC2CBC)) {
message = "pbeWithSHA1AndRC2-CBC unsupported";
} else {
message = "unsupported encryption oid: " + Util.toHex(pbesid);
}
throw new JSchException(message);
}
byte[][] ivp = new byte[1][];
Cipher cipher = getCipher(encryptfuncid, encryptparams, ivp);
byte[] iv = ivp[0];
key = kdfinst.getKey(_passphrase, cipher.getBlockSize());
if (key == null) {
throw new JSchException("failed to generate key from KDF " + kdfname);
}
cipher.init(Cipher.DECRYPT_MODE, key, iv);
plain = new byte[_data.length];
cipher.update(_data, 0, _data.length, plain, 0);
if (parse(plain)) {
encrypted = false;
Util.bzero(data);
return true;
} else {
throw new JSchException("failed to parse decrypted key");
}
} catch (ASN1Exception e) {
if (instLogger.getLogger().isEnabled(Logger.ERROR)) {
instLogger.getLogger().log(Logger.ERROR, "PKCS8: failed to decrypt key: ASN1 parsing error",
e);
}
return false;
} catch (Exception e) {
if (instLogger.getLogger().isEnabled(Logger.ERROR)) {
instLogger.getLogger().log(Logger.ERROR, "PKCS8: failed to decrypt key: " + e.getMessage(),
e);
}
return false;
} finally {
Util.bzero(_data);
Util.bzero(key);
Util.bzero(plain);
}
}
static String getPBKDF2Name(byte[] id) throws JSchException {
String name = null;
if (id == null || Util.array_equals(id, hmacWithSha1)) {
name = "pbkdf2-hmac-sha1";
} else if (Util.array_equals(id, hmacWithSha224)) {
name = "pbkdf2-hmac-sha224";
} else if (Util.array_equals(id, hmacWithSha256)) {
name = "pbkdf2-hmac-sha256";
} else if (Util.array_equals(id, hmacWithSha384)) {
name = "pbkdf2-hmac-sha384";
} else if (Util.array_equals(id, hmacWithSha512)) {
name = "pbkdf2-hmac-sha512";
} else if (Util.array_equals(id, hmacWithSha512224)) {
name = "pbkdf2-hmac-sha512-224";
} else if (Util.array_equals(id, hmacWithSha512256)) {
name = "pbkdf2-hmac-sha512-256";
}
if (name == null) {
throw new JSchException("unsupported pbkdf2 function oid: " + Util.toHex(id));
}
return name;
}
static PBKDF2 getPBKDF2(String name) throws JSchException {
try {
Class<? extends PBKDF2> c = Class.forName(JSch.getConfig(name)).asSubclass(PBKDF2.class);
return c.getDeclaredConstructor().newInstance();
} catch (Exception e) {
throw new JSchException(name + " is not supported", e);
}
}
static SCrypt getSCrypt() throws JSchException {
try {
Class<? extends SCrypt> c = Class.forName(JSch.getConfig("scrypt")).asSubclass(SCrypt.class);
return c.getDeclaredConstructor().newInstance();
} catch (Exception e) {
throw new JSchException("scrypt is not supported", e);
}
}
static Cipher getCipher(byte[] id, ASN1 encryptparams, byte[][] ivp) throws Exception {
String name = null;
if (Util.array_equals(id, aes128cbc)) {
name = "aes128-cbc";
} else if (Util.array_equals(id, aes192cbc)) {
name = "aes192-cbc";
} else if (Util.array_equals(id, aes256cbc)) {
name = "aes256-cbc";
} else if (Util.array_equals(id, descbc)) {
throw new JSchException("unsupported cipher function: des-cbc");
} else if (Util.array_equals(id, des3cbc)) {
throw new JSchException("unsupported cipher function: 3des-cbc");
} else if (Util.array_equals(id, rc2cbc)) {
throw new JSchException("unsupported cipher function: rc2-cbc");
} else if (Util.array_equals(id, rc5cbc)) {
throw new JSchException("unsupported cipher function: rc5-cbc");
}
if (name == null) {
throw new JSchException("unsupported cipher function oid: " + Util.toHex(id));
}
if (!encryptparams.isOCTETSTRING()) {
throw new ASN1Exception();
}
ivp[0] = encryptparams.getContent();
try {
Class<? extends Cipher> c = Class.forName(JSch.getConfig(name)).asSubclass(Cipher.class);
return c.getDeclaredConstructor().newInstance();
} catch (Exception e) {
throw new JSchException(name + " is not supported", e);
}
}
static int parseASN1IntegerAsInt(byte[] content) {
BigInteger b = new BigInteger(content);
// https://github.com/mwiede/jsch/issues/392 not using intValueExact() because of Android
// incompatibility.
if (b.bitLength() <= 31) {
return b.intValue();
} else {
throw new ArithmeticException("BigInteger out of int range");
}
}
}