KeyPairRSA.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.math.BigInteger;
class KeyPairRSA extends KeyPair {
private byte[] n_array; // modulus p multiply q
private byte[] pub_array; // e
private byte[] prv_array; // d e^-1 mod (p-1)(q-1)
private byte[] p_array; // prime p
private byte[] q_array; // prime q
private byte[] ep_array; // prime exponent p dmp1 == prv mod (p-1)
private byte[] eq_array; // prime exponent q dmq1 == prv mod (q-1)
private byte[] c_array; // coefficient iqmp == modinv(q, p) == q^-1 mod p
private int key_size = 1024;
KeyPairRSA(JSch.InstanceLogger instLogger) {
this(instLogger, null, null, null);
}
KeyPairRSA(JSch.InstanceLogger instLogger, byte[] n_array, byte[] pub_array, byte[] prv_array) {
super(instLogger);
this.n_array = n_array;
this.pub_array = pub_array;
this.prv_array = prv_array;
if (n_array != null) {
key_size = (new BigInteger(n_array)).bitLength();
}
}
@Override
void generate(int key_size) throws JSchException {
this.key_size = key_size;
try {
Class<? extends KeyPairGenRSA> c =
Class.forName(JSch.getConfig("keypairgen.rsa")).asSubclass(KeyPairGenRSA.class);
KeyPairGenRSA keypairgen = c.getDeclaredConstructor().newInstance();
keypairgen.init(key_size);
pub_array = keypairgen.getE();
prv_array = keypairgen.getD();
n_array = keypairgen.getN();
p_array = keypairgen.getP();
q_array = keypairgen.getQ();
ep_array = keypairgen.getEP();
eq_array = keypairgen.getEQ();
c_array = keypairgen.getC();
keypairgen = null;
} catch (Exception e) {
throw new JSchException(e.toString(), e);
}
}
private static final byte[] begin = Util.str2byte("-----BEGIN RSA PRIVATE KEY-----");
private static final byte[] end = Util.str2byte("-----END RSA PRIVATE KEY-----");
@Override
byte[] getBegin() {
return begin;
}
@Override
byte[] getEnd() {
return end;
}
@Override
byte[] getPrivateKey() {
int content = 1 + countLength(1) + 1 + // INTEGER
1 + countLength(n_array.length) + n_array.length + // INTEGER N
1 + countLength(pub_array.length) + pub_array.length + // INTEGER pub
1 + countLength(prv_array.length) + prv_array.length + // INTEGER prv
1 + countLength(p_array.length) + p_array.length + // INTEGER p
1 + countLength(q_array.length) + q_array.length + // INTEGER q
1 + countLength(ep_array.length) + ep_array.length + // INTEGER ep
1 + countLength(eq_array.length) + eq_array.length + // INTEGER eq
1 + countLength(c_array.length) + c_array.length; // INTEGER c
int total = 1 + countLength(content) + content; // SEQUENCE
byte[] plain = new byte[total];
int index = 0;
index = writeSEQUENCE(plain, index, content);
index = writeINTEGER(plain, index, new byte[1]); // 0
index = writeINTEGER(plain, index, n_array);
index = writeINTEGER(plain, index, pub_array);
index = writeINTEGER(plain, index, prv_array);
index = writeINTEGER(plain, index, p_array);
index = writeINTEGER(plain, index, q_array);
index = writeINTEGER(plain, index, ep_array);
index = writeINTEGER(plain, index, eq_array);
index = writeINTEGER(plain, index, c_array);
return plain;
}
@Override
boolean parse(byte[] plain) {
try {
int index = 0;
int length = 0;
if (vendor == VENDOR_PUTTY || vendor == VENDOR_PUTTY_V3) {
Buffer buf = new Buffer(plain);
buf.skip(plain.length);
try {
byte[][] tmp = buf.getBytes(4, "");
prv_array = tmp[0];
p_array = tmp[1];
q_array = tmp[2];
c_array = tmp[3];
} catch (JSchException e) {
if (instLogger.getLogger().isEnabled(Logger.ERROR)) {
instLogger.getLogger().log(Logger.ERROR, "failed to parse key", e);
}
return false;
}
getEPArray();
getEQArray();
return true;
}
if (vendor == VENDOR_FSECURE) {
if (plain[index] != 0x30) { // FSecure
Buffer buf = new Buffer(plain);
pub_array = buf.getMPIntBits();
prv_array = buf.getMPIntBits();
n_array = buf.getMPIntBits();
byte[] u_array = buf.getMPIntBits();
p_array = buf.getMPIntBits();
q_array = buf.getMPIntBits();
if (n_array != null) {
key_size = (new BigInteger(n_array)).bitLength();
}
getEPArray();
getEQArray();
getCArray();
return true;
}
if (instLogger.getLogger().isEnabled(Logger.ERROR)) {
instLogger.getLogger().log(Logger.ERROR, "failed to parse key");
}
return false;
}
// OPENSSH Key v1 Format
if (vendor == VENDOR_OPENSSH_V1) {
final Buffer prvKEyBuffer = new Buffer(plain);
int checkInt1 = prvKEyBuffer.getInt(); // uint32 checkint1
int checkInt2 = prvKEyBuffer.getInt(); // uint32 checkint2
if (checkInt1 != checkInt2) {
throw new JSchException("check failed");
}
String keyType = Util.byte2str(prvKEyBuffer.getString()); // string keytype
n_array = prvKEyBuffer.getMPInt(); // Modulus
pub_array = prvKEyBuffer.getMPInt(); // Public Exponent
prv_array = prvKEyBuffer.getMPInt(); // Private Exponent
c_array = prvKEyBuffer.getMPInt(); // iqmp (q^-1 mod p)
p_array = prvKEyBuffer.getMPInt(); // p (Prime 1)
q_array = prvKEyBuffer.getMPInt(); // q (Prime 2)
if (n_array != null) {
key_size = (new BigInteger(n_array)).bitLength();
}
publicKeyComment = Util.byte2str(prvKEyBuffer.getString());
getEPArray();
getEQArray();
return true;
}
/*
* Key must be in the following ASN.1 DER encoding, RSAPrivateKey ::= SEQUENCE { version
* Version, modulus INTEGER, -- n publicExponent INTEGER, -- e privateExponent INTEGER, -- d
* prime1 INTEGER, -- p prime2 INTEGER, -- q exponent1 INTEGER, -- d mod (p-1) exponent2
* INTEGER, -- d mod (q-1) coefficient INTEGER, -- (inverse of q) mod p otherPrimeInfos
* OtherPrimeInfos OPTIONAL }
*/
index++; // SEQUENCE
length = plain[index++] & 0xff;
if ((length & 0x80) != 0) {
int foo = length & 0x7f;
length = 0;
while (foo-- > 0) {
length = (length << 8) + (plain[index++] & 0xff);
}
}
if (plain[index] != 0x02)
return false;
index++; // INTEGER
length = plain[index++] & 0xff;
if ((length & 0x80) != 0) {
int foo = length & 0x7f;
length = 0;
while (foo-- > 0) {
length = (length << 8) + (plain[index++] & 0xff);
}
}
index += length;
index++;
length = plain[index++] & 0xff;
if ((length & 0x80) != 0) {
int foo = length & 0x7f;
length = 0;
while (foo-- > 0) {
length = (length << 8) + (plain[index++] & 0xff);
}
}
n_array = new byte[length];
System.arraycopy(plain, index, n_array, 0, length);
index += length;
index++;
length = plain[index++] & 0xff;
if ((length & 0x80) != 0) {
int foo = length & 0x7f;
length = 0;
while (foo-- > 0) {
length = (length << 8) + (plain[index++] & 0xff);
}
}
pub_array = new byte[length];
System.arraycopy(plain, index, pub_array, 0, length);
index += length;
index++;
length = plain[index++] & 0xff;
if ((length & 0x80) != 0) {
int foo = length & 0x7f;
length = 0;
while (foo-- > 0) {
length = (length << 8) + (plain[index++] & 0xff);
}
}
prv_array = new byte[length];
System.arraycopy(plain, index, prv_array, 0, length);
index += length;
index++;
length = plain[index++] & 0xff;
if ((length & 0x80) != 0) {
int foo = length & 0x7f;
length = 0;
while (foo-- > 0) {
length = (length << 8) + (plain[index++] & 0xff);
}
}
p_array = new byte[length];
System.arraycopy(plain, index, p_array, 0, length);
index += length;
index++;
length = plain[index++] & 0xff;
if ((length & 0x80) != 0) {
int foo = length & 0x7f;
length = 0;
while (foo-- > 0) {
length = (length << 8) + (plain[index++] & 0xff);
}
}
q_array = new byte[length];
System.arraycopy(plain, index, q_array, 0, length);
index += length;
index++;
length = plain[index++] & 0xff;
if ((length & 0x80) != 0) {
int foo = length & 0x7f;
length = 0;
while (foo-- > 0) {
length = (length << 8) + (plain[index++] & 0xff);
}
}
ep_array = new byte[length];
System.arraycopy(plain, index, ep_array, 0, length);
index += length;
index++;
length = plain[index++] & 0xff;
if ((length & 0x80) != 0) {
int foo = length & 0x7f;
length = 0;
while (foo-- > 0) {
length = (length << 8) + (plain[index++] & 0xff);
}
}
eq_array = new byte[length];
System.arraycopy(plain, index, eq_array, 0, length);
index += length;
index++;
length = plain[index++] & 0xff;
if ((length & 0x80) != 0) {
int foo = length & 0x7f;
length = 0;
while (foo-- > 0) {
length = (length << 8) + (plain[index++] & 0xff);
}
}
c_array = new byte[length];
System.arraycopy(plain, index, c_array, 0, length);
index += length;
if (n_array != null) {
key_size = (new BigInteger(n_array)).bitLength();
}
} catch (Exception e) {
if (instLogger.getLogger().isEnabled(Logger.ERROR)) {
instLogger.getLogger().log(Logger.ERROR, "failed to parse key", e);
}
return false;
}
return true;
}
@Override
public byte[] getPublicKeyBlob() {
byte[] foo = super.getPublicKeyBlob();
if (foo != null)
return foo;
if (pub_array == null)
return null;
byte[][] tmp = new byte[3][];
tmp[0] = sshrsa;
tmp[1] = pub_array;
tmp[2] = n_array;
return Buffer.fromBytes(tmp).buffer;
}
private static final byte[] sshrsa = Util.str2byte("ssh-rsa");
@Override
byte[] getKeyTypeName() {
return sshrsa;
}
@Override
public int getKeyType() {
return RSA;
}
@Override
public int getKeySize() {
return key_size;
}
@Override
public byte[] getSignature(byte[] data) {
return getSignature(data, "ssh-rsa");
}
@Override
public byte[] getSignature(byte[] data, String alg) {
try {
Class<? extends SignatureRSA> c =
Class.forName(JSch.getConfig(alg)).asSubclass(SignatureRSA.class);
SignatureRSA rsa = c.getDeclaredConstructor().newInstance();
rsa.init();
rsa.setPrvKey(prv_array, n_array);
rsa.update(data);
byte[] sig = rsa.sign();
byte[][] tmp = new byte[2][];
tmp[0] = Util.str2byte(alg);
tmp[1] = sig;
return Buffer.fromBytes(tmp).buffer;
} catch (Exception 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("ssh-rsa");
}
@Override
public Signature getVerifier(String alg) {
try {
Class<? extends SignatureRSA> c =
Class.forName(JSch.getConfig(alg)).asSubclass(SignatureRSA.class);
SignatureRSA rsa = c.getDeclaredConstructor().newInstance();
rsa.init();
if (pub_array == null && n_array == null && getPublicKeyBlob() != null) {
Buffer buf = new Buffer(getPublicKeyBlob());
buf.getString();
pub_array = buf.getString();
n_array = buf.getString();
}
rsa.setPubKey(pub_array, n_array);
return rsa;
} catch (Exception e) {
if (instLogger.getLogger().isEnabled(Logger.ERROR)) {
instLogger.getLogger().log(Logger.ERROR, "failed to create verifier", e);
}
}
return null;
}
static KeyPair fromSSHAgent(JSch.InstanceLogger instLogger, Buffer buf) throws JSchException {
byte[][] tmp = buf.getBytes(8, "invalid key format");
byte[] n_array = tmp[1];
byte[] pub_array = tmp[2];
byte[] prv_array = tmp[3];
KeyPairRSA kpair = new KeyPairRSA(instLogger, n_array, pub_array, prv_array);
kpair.c_array = tmp[4]; // iqmp
kpair.p_array = tmp[5];
kpair.q_array = tmp[6];
kpair.publicKeyComment = Util.byte2str(tmp[7]);
kpair.vendor = VENDOR_OPENSSH;
return kpair;
}
@Override
public byte[] forSSHAgent() throws JSchException {
if (isEncrypted()) {
throw new JSchException("key is encrypted.");
}
Buffer buf = new Buffer();
buf.putString(sshrsa);
buf.putString(n_array);
buf.putString(pub_array);
buf.putString(prv_array);
buf.putString(getCArray());
buf.putString(p_array);
buf.putString(q_array);
buf.putString(Util.str2byte(publicKeyComment));
byte[] result = new byte[buf.getLength()];
buf.getByte(result, 0, result.length);
return result;
}
private byte[] getEPArray() {
if (ep_array == null) {
ep_array = (new BigInteger(prv_array)).mod(new BigInteger(p_array).subtract(BigInteger.ONE))
.toByteArray();
}
return ep_array;
}
private byte[] getEQArray() {
if (eq_array == null) {
eq_array = (new BigInteger(prv_array)).mod(new BigInteger(q_array).subtract(BigInteger.ONE))
.toByteArray();
}
return eq_array;
}
private byte[] getCArray() {
if (c_array == null) {
c_array = (new BigInteger(q_array)).modInverse(new BigInteger(p_array)).toByteArray();
}
return c_array;
}
@Override
public void dispose() {
super.dispose();
Util.bzero(prv_array);
}
}