[英]Use JKS keystore for public key authentication with Ganymed SSH
我正在嘗試從java密鑰庫中提取私鑰,然后將其提供給Ganymed SSH,以便與公鑰認證建立連接。 然而,它拒絕連接。
我可以通過Cygwin( ssh -i
)成功連接以下程序生成的文件,但程序本身無法在同一台機器上通過身份驗證。 我究竟做錯了什么?
我沒有得到異常, Connection.authenticateWithPublicKey(String,char [],String)只返回false,所以這不應該是格式化問題。 如果我保留未加密的私鑰並不重要,結果是一樣的。 此外,我能夠使用putty-gen和ssh-keygen生成的文件連接類似於此的程序。
您需要以下內容進行編譯(bouncycastle和ganymed):
使用以下代碼生成密鑰庫:
keytool -genkeypair -keystore keystore.jks -alias myalias -storepass password -keypass password -keyalg RSA -dname CN=myalias,O=example.com -storetype JKS -validity 365 -v
代碼(期望主機和端口為args以及工作目錄中的上述密鑰庫):
import ch.ethz.ssh2.Connection;
import java.io.ByteArrayOutputStream;
import java.io.CharArrayWriter;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.KeyStore;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.interfaces.DSAParams;
import java.security.interfaces.DSAPublicKey;
import java.security.interfaces.RSAPublicKey;
import javax.xml.bind.DatatypeConverter;
import org.bouncycastle.openssl.PEMEncryptor;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
import org.bouncycastle.openssl.jcajce.JcePEMEncryptorBuilder;
public class KeystoreGanymedSSH {
public static void main(String[] args)
throws Exception {
String keystorePath = "keystore.jks";
char[] password = "password".toCharArray();
String alias = "myalias";
String host = args[0];
int port = Integer.parseInt(args[1]);
// keystore init
KeyStore keystore = KeyStore.getInstance("JKS");
InputStream in;
try {
in = new FileInputStream(keystorePath);
} catch (FileNotFoundException ex) {
System.out.println("Generate keystore using this command:");
System.out.println("keytool -genkeypair -keystore keystore.jks"
+ " -alias myalias -storepass password -keypass password"
+ " -keyalg RSA -dname CN=myalias,O=example.com -storetype"
+ " JKS -validity 365 -v");
throw ex;
}
try {
keystore.load(in, password);
} finally {
in.close();
}
// get public key in OpenSSH format
String authorizedKeysEntry = genAuthorizedKeysEntry(keystore, alias);
if (authorizedKeysEntry == null) {
throw new Exception("could not generate authorized_keys entry");
}
System.out.println("Public key for pasting into OpenSSH authorized_keys file (always same):");
System.out.println(authorizedKeysEntry);
System.out.println();
Writer writer;
// write to file
writer = new OutputStreamWriter(
new FileOutputStream(new File("authorized_keys")), "UTF-8");
try {
writer.write(authorizedKeysEntry);
} finally {
writer.close();
}
// obtain PEM encrypted char[]
Key key = keystore.getKey(alias, password);
writer = new CharArrayWriter();
JcaPEMWriter pw = new JcaPEMWriter(writer);
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
PEMEncryptor encryptor = new JcePEMEncryptorBuilder("DES-EDE3-CBC")
.setSecureRandom(random).build(password);
pw.writeObject(key, encryptor);
pw.flush();
char[] privateKey = ((CharArrayWriter)writer).toCharArray();
System.out.println("Encrypted private key (changes on each run):");
System.out.println(new String(privateKey));
String name = "RSA".equals(key.getAlgorithm()) ? "id_rsa" : "id_dsa";
writer = new OutputStreamWriter(
new FileOutputStream(new File(name)), "UTF-8");
try {
writer.write(privateKey);
} finally {
writer.close();
}
// attempt ganymed connection
Connection connection = null;
try {
System.out.println("Connecting to " + host + ":" + port);
connection = new Connection(host, port);
connection.connect(); // no known_hosts
if (!connection.isAuthMethodAvailable(alias, "publickey")) {
System.out.println("Public key auth is not available.");
return;
}
boolean result = connection.authenticateWithPublicKey(
alias, privateKey, new String(password));
System.out.println(result ? "Authentication success." : "Authentication failure.");
} finally {
if (connection != null) {
connection.close();
}
}
}
private static String genAuthorizedKeysEntry(
KeyStore keystore, String alias) throws GeneralSecurityException, IOException {
Certificate[] chain = keystore.getCertificateChain(alias);
if (chain == null || chain.length <= 0) {
return null;
}
PublicKey publicKey = chain[0].getPublicKey();
if ("RSA".equals(publicKey.getAlgorithm())) {
RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
String type = "ssh-rsa";
dos.writeInt(type.getBytes("UTF-8").length);
dos.write(type.getBytes("UTF-8"));
byte[] exponent = rsaPublicKey.getPublicExponent().toByteArray();
dos.writeInt(exponent.length);
dos.write(exponent);
byte[] modulus = rsaPublicKey.getModulus().toByteArray();
dos.writeInt(modulus.length);
dos.write(modulus);
String encoded = DatatypeConverter.printBase64Binary(
baos.toByteArray());
return type + " " + encoded + " " + alias;
} else if ("DSA".equals(publicKey.getAlgorithm())) {
DSAPublicKey dsaPublicKey = (DSAPublicKey) publicKey;
DSAParams params = dsaPublicKey.getParams();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
String type = "ssh-dss";
dos.writeInt(type.getBytes("UTF-8").length);
dos.write(type.getBytes("UTF-8"));
byte[] p = params.getP().toByteArray();
dos.writeInt(p.length);
dos.write(p);
byte[] q = params.getQ().toByteArray();
dos.writeInt(q.length);
dos.write(q);
byte[] g = params.getG().toByteArray();
dos.writeInt(g.length);
dos.write(g);
byte[] y = dsaPublicKey.getY().toByteArray();
dos.writeInt(y.length);
dos.write(y);
String encoded = DatatypeConverter.printBase64Binary(
baos.toByteArray());
return type + " " + encoded + " " + alias;
} else {
return null;
}
}
}
樣本輸出:
Public key for pasting into OpenSSH authorized_keys file (always same):
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCJLXgRaVdbZNuCsTgUsw2UPGdEA4La8ggQZWkevgAEMrgF+YYT2uN6BYDgD7hzs3ZTLXz2KUQLkMe7xLvimAsg6YXUi46IGEkTSOBFR0yYj+12O2BNbAxOXLIDIMBK5bsDwnuOsFedbeILFU4DaV+igJKO1zHWNbmbmd4RlfrIgH7Blfce8zSVkEdLkqEmydbg4xmj6r+MlzA5HSNZJILivb1XYNnoLjRH1SwUC8Rj6bjgBdNEXLOH0FNpCatHk9R00GaSZjcDZRKNAKnBSEIpw01TKaJlyQUTGqYGjK7UIbbafwMuYKR1rIzkyh4Usxvd3FvMdmKQSUeCnZU296YF myalias
Encrypted private key (changes on each run):
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,86DF7B50D4E319F6
QGngqwq+NXQGee7pEVROnwvIX6cyzo9QjdKtlherLCIYQjD4zagCvCd4vOUmwe0S
g/KaPeq8tanU0KB6m/WfAsgfOAkR+ujIod0hkUoCZp/Jg9LO2Me7ZFDNW/cBqbW0
CifaqDmOVgJ+HnZHJouMZyPf+To8SDdMSJZQ/3Wc2ZQn6BIhEjLzdz0hSVXGz2Gs
wmVYy4oedjB2f+dQliEnwFXBOusRMfqgiPqkdQj38voipqYBmPHUYSity0HGsRlo
KgovSeQhEDPT8GyYVJcKZLV3BUipvNvKJBP613wBZsuCtvhUNNNOfVFeTkD8+7PG
q2YzF3nasOM471EHyj18zPZ+DdPQMDPHtpVFQXJCFlf7xlGVAesPPUFJICXFE5OZ
JRAJC8+7WuT8O974uT9zDLeV5XLJRJK6o8dYXtZBK0YMpZy91b9axYDeVQh+Sfd+
V/T5RQ2osXg72lDBtz6vzeyMGj+Y9PQwZb97tdRW1X/ON4Eiiz/+1SVeDbWilo29
gMSPl4wb379Dvi7Z+b5OTGoF+F1p7Cp48sUEgIP8vPXinoOhBLdy1zq8oNVbo7PS
M5+41PKL4ao8pL9BCOALZpzP2R9LxoHjjL1auaWMzKLECtiEDvgu4GJeTVXvg7sO
yptecswVCF8fY+pV8dTtYU3vUs4UsdC9PG9IhqeRbML9dX7htsgkBmHdYAq5WOS/
RREuU+jyrCnc6kpOhIK/1wMOoIMOBnJ8EJXpMJaZtNwOQr05bOfFvozOEe35JwnP
NElP7CYBIvQrTyfrRxtJE+ntQO+uJbIvxFDY0EoQJX6YPFr0V7rnWy4W1yH0Yv6E
pmwERGYr1lbBIpxjTzTwZ3r845EUEwiwEt3+xfepBh3HUXg/mZYUw4cEz3HbzZDT
tWRPFpsBaicfatzbqvL7Teq1V8baUj1CW0wrANZbHc0FvSzuHMygub2ARgM3QAMj
L5yaITjH8/Tnbew7jPi5kSTXdNUnAJf3M/m6DC7svJtx+1Xwd0tfzp3GHYLaT+Mm
vOu8R5g/JJvBVMTzP8gyI32jDViRuHHwyFOlyJ35IrRCkW8i+aBmG1iT2WANWRai
2ujX4Gc+M2VncUdFR9MoCxUOy/7qKDcGNMpk8sgIi6Pc8SLiodueiWP3W6AXJKvs
u5akyk7jj8zq9+fe85T/cZ8lYe81hd3oA/9b/9cs8sdlhTmYjfUr1FgFHNyFPwdV
QnyayxeAy3xvoYXBBr7JrmWXLDTHghhMBHGHW7imoLNN8QZtTF+pGWzsxNcAVbEz
kmLll9ki0CUIbfufszp/b05OBC2M0EHn9uW61bwbiZfWxhfTlC2zHNHpig6zQhHu
q8n//KgHB5LDctGHoeqlUwoLbt78wd0bAD23GeZ2q1CdB6FYxoMYL8FuVOnxoUh3
fquXzH0wjv3Qm4Rwit+8zSdbD/+QbtJ2c/ZguUy4T3phI5BGzhLh2IDO8T9B6y5B
MmTyFjfZjVj+zU4F0BAIzzLlYTl332ecMj87StoNazqIF5Dj2ZqjUtF46MDeMZjO
tRvpIi8bWBm78rFNC51TZSBcfw714yOxHsPU0PqUMQMCgXawcDkTt2645/+ZZQtk
-----END RSA PRIVATE KEY-----
Connecting to 10.0.3.138:22
Authentication failure.
Edit01
我用jsch和sshj嘗試了這個,所有這些都無法連接。 私鑰提取的上述代碼必須有問題。 令我困惑的是我可以用ssh -i
連接。 另外,如果我將上面的私鑰加載到putty-gen中,我會得到與我的程序輸出相同的authorized_keys字符串(已存在於我的遠程機器的.ssh/authorized_keys
)。 但是,如果我將其保存為ppk文件,然后嘗試與它建立一個putty會話,它也不會通過(服務器拒絕我們的密鑰)。
我發布的代碼沒有錯。 這對我來說只是一個巨大的失敗。
我使用密鑰庫別名作為用戶名,該用戶名在遠程linux框中不作為用戶帳戶存在 。 創建帳戶后,一切正常。 我正在編輯錯誤的authorized_keys
文件(在其他一些帳戶上),並希望它通過一些模糊的黑魔法工作,只有小妖精,獨角獸和顯然是我的大腦。
至於為什么它在我做ssh -i
時起作用...默認值。 愚蠢的默認值。 我根本沒有指定用戶名,因此它默認為Cygwin當前正在使用的用戶名,而且它恰好與我在編輯authorized_keys
文件的遠程帳戶名稱相同。
大。 真棒。 剛剛好。 我需要找一座橋梁讓自己從現在開始。
在我的代碼中執行的操作與ganymed,jsch和sshj一起使用(對其他兩個API進行一些小的修改)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.