KeyPairEdDSA.java
/*
* Copyright (c) 2002-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.util.Arrays;
abstract class KeyPairEdDSA extends KeyPair {
private byte[] pub_array;
private byte[] prv_array;
KeyPairEdDSA(JSch.InstanceLogger instLogger, byte[] pub_array, byte[] prv_array) {
super(instLogger);
this.pub_array = pub_array;
this.prv_array = prv_array;
}
abstract String getSshName();
abstract String getJceName();
@Override
void generate(int key_size) throws JSchException {
try {
Class<? extends KeyPairGenEdDSA> c =
Class.forName(JSch.getConfig("keypairgen.eddsa")).asSubclass(KeyPairGenEdDSA.class);
KeyPairGenEdDSA keypairgen = c.getDeclaredConstructor().newInstance();
keypairgen.init(getJceName(), getKeySize());
pub_array = keypairgen.getPub();
prv_array = keypairgen.getPrv();
keypairgen = null;
} catch (Exception | NoClassDefFoundError e) {
throw new JSchException(e.toString(), e);
}
}
// These methods appear to be for writing keys to a file.
// And since writing VENDOR_OPENSSH_V1 isn't supported yet, have these methods fail.
@Override
byte[] getBegin() {
throw new UnsupportedOperationException();
}
@Override
byte[] getEnd() {
throw new UnsupportedOperationException();
}
@Override
byte[] getPrivateKey() {
throw new UnsupportedOperationException();
}
@Override
boolean parse(byte[] plain) {
if (vendor == VENDOR_PUTTY || vendor == VENDOR_PUTTY_V3) {
Buffer buf = new Buffer(plain);
buf.skip(plain.length);
try {
byte[][] tmp = buf.getBytes(1, "");
prv_array = tmp[0];
} catch (JSchException e) {
if (instLogger.getLogger().isEnabled(Logger.ERROR)) {
instLogger.getLogger().log(Logger.ERROR, "failed to parse key", e);
}
return false;
}
return true;
} else if (vendor == VENDOR_OPENSSH_V1) {
try {
// OPENSSH Key v1 Format
final Buffer buf = new Buffer(plain);
int checkInt1 = buf.getInt(); // uint32 checkint1
int checkInt2 = buf.getInt(); // uint32 checkint2
if (checkInt1 != checkInt2) {
throw new JSchException("check failed");
}
String keyType = Util.byte2str(buf.getString()); // string keytype
pub_array = buf.getString(); // public key
// OpenSSH stores private key in first half of string and duplicate copy of public key in
// second half of string
byte[] tmp = buf.getString(); // secret key (private key + public key)
prv_array = Arrays.copyOf(tmp, getKeySize());
publicKeyComment = Util.byte2str(buf.getString());
return true;
} catch (Exception e) {
if (instLogger.getLogger().isEnabled(Logger.ERROR)) {
instLogger.getLogger().log(Logger.ERROR, "failed to parse key", e);
}
return false;
}
} else if (vendor == VENDOR_PKCS8) {
try {
Class<? extends KeyPairGenEdDSA> c =
Class.forName(JSch.getConfig("keypairgen_fromprivate.eddsa"))
.asSubclass(KeyPairGenEdDSA.class);
KeyPairGenEdDSA keypairgen = c.getDeclaredConstructor().newInstance();
keypairgen.init(getJceName(), plain);
pub_array = keypairgen.getPub();
prv_array = keypairgen.getPrv();
return true;
} catch (Exception | NoClassDefFoundError e) {
if (instLogger.getLogger().isEnabled(Logger.ERROR)) {
instLogger.getLogger().log(Logger.ERROR, "failed to parse key", e);
}
return false;
}
} else {
if (instLogger.getLogger().isEnabled(Logger.ERROR)) {
instLogger.getLogger().log(Logger.ERROR, "failed to parse key");
}
return false;
}
}
@Override
public byte[] getPublicKeyBlob() {
byte[] foo = super.getPublicKeyBlob();
if (foo != null)
return foo;
if (pub_array == null)
return null;
byte[][] tmp = new byte[2][];
tmp[0] = getKeyTypeName();
tmp[1] = pub_array;
return Buffer.fromBytes(tmp).buffer;
}
@Override
byte[] getKeyTypeName() {
return Util.str2byte(getSshName());
}
@Override
public byte[] getSignature(byte[] data) {
return getSignature(data, getSshName());
}
@Override
public byte[] getSignature(byte[] data, String alg) {
try {
Class<? extends SignatureEdDSA> c =
Class.forName(JSch.getConfig(alg)).asSubclass(SignatureEdDSA.class);
SignatureEdDSA eddsa = c.getDeclaredConstructor().newInstance();
eddsa.init();
eddsa.setPrvKey(prv_array);
eddsa.update(data);
byte[] sig = eddsa.sign();
byte[][] tmp = new byte[2][];
tmp[0] = Util.str2byte(alg);
tmp[1] = sig;
return Buffer.fromBytes(tmp).buffer;
} catch (Exception | NoClassDefFoundError e) {
if (instLogger.getLogger().isEnabled(Logger.ERROR)) {
instLogger.getLogger().log(Logger.ERROR, "failed to generate signature", e);
}
}
return null;
}
@Override
public Signature getVerifier() {
return getVerifier(getSshName());
}
@Override
public Signature getVerifier(String alg) {
try {
Class<? extends SignatureEdDSA> c =
Class.forName(JSch.getConfig(alg)).asSubclass(SignatureEdDSA.class);
SignatureEdDSA eddsa = c.getDeclaredConstructor().newInstance();
eddsa.init();
if (pub_array == null && getPublicKeyBlob() != null) {
Buffer buf = new Buffer(getPublicKeyBlob());
buf.getString();
pub_array = buf.getString();
}
eddsa.setPubKey(pub_array);
return eddsa;
} catch (Exception | NoClassDefFoundError e) {
if (instLogger.getLogger().isEnabled(Logger.ERROR)) {
instLogger.getLogger().log(Logger.ERROR, "failed to create verifier", e);
}
}
return null;
}
@Override
public byte[] forSSHAgent() throws JSchException {
if (isEncrypted()) {
throw new JSchException("key is encrypted.");
}
Buffer buf = new Buffer();
buf.putString(getKeyTypeName());
buf.putString(pub_array);
byte[] tmp = new byte[prv_array.length + pub_array.length];
System.arraycopy(prv_array, 0, tmp, 0, prv_array.length);
System.arraycopy(pub_array, 0, tmp, prv_array.length, pub_array.length);
buf.putString(tmp);
buf.putString(Util.str2byte(publicKeyComment));
byte[] result = new byte[buf.getLength()];
buf.getByte(result, 0, result.length);
return result;
}
@Override
public void dispose() {
super.dispose();
Util.bzero(prv_array);
}
}