"Недействительный приватный ключ" при использовании JSch

Я использую следующий код для работы с Git в приложении Java. У меня есть действительный ключ (используйте его постоянно), и этот конкретный код уже работал для меня с тем же ключом и git-репозиторием, но теперь я получаю следующее исключение:

неверный приватный ключ: [B @59c40796.

На этой линии:

jSch.addIdentity("<key_path>/private_key.pem");

Мой полный код:

    String remoteURL = "ssh://[email protected]<git_repository>";
    TransportConfigCallback transportConfigCallback = new SshTransportConfigCallback();
    File gitFolder = new File(workingDirectory);
    if (gitFolder.exists()) FileUtils.delete(gitFolder, FileUtils.RECURSIVE);

    Git git = Git.cloneRepository()
            .setURI(remoteURL)
            .setTransportConfigCallback(transportConfigCallback)
            .setDirectory(new File(workingDirectory))
            .call();
}


private static class SshTransportConfigCallback implements TransportConfigCallback {
    private final SshSessionFactory sshSessionFactory = new JschConfigSessionFactory() {
        @Override
        protected void configure(OpenSshConfig.Host hc, Session session) {
            session.setConfig("StrictHostKeyChecking", "no");
        }

        @Override
        protected JSch createDefaultJSch(FS fs) throws JSchException {
            JSch jSch = super.createDefaultJSch(fs);
            jSch.addIdentity("<key_path>/private_key.pem");

            return jSch;
        }
    };

После поиска в Интернете я изменил createDefaultJSch на использование pemWriter:

@Override
protected JSch createDefaultJSch(FS fs) throws JSchException {
    JSch jSch = super.createDefaultJSch(fs);
    byte[] privateKeyPEM = null;

    try {
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");

        List<String> lines = Files.readAllLines(Paths.get("<my_key>.pem"), StandardCharsets.US_ASCII);
        PKCS8EncodedKeySpec privSpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(String.join("", lines)));
        RSAPrivateKey privKey = (RSAPrivateKey) keyFactory.generatePrivate(privSpec);

        PKCS8Generator pkcs8 = new PKCS8Generator(privKey);

        StringWriter writer = new StringWriter();
        PemWriter pemWriter = new PemWriter(writer);
        pemWriter.writeObject(pkcs8);

        privateKeyPEM = writer.toString().getBytes("US-ASCII");

    } catch (Exception e) {
        e.printStackTrace();
    }

    jSch.addIdentity("git", privateKeyPEM, null, null);

    return jSch;
}

Но все равно получаю исключение "неверный приватный ключ".

Ответ 1

Я тоже наткнулся на эту проблему. при запуске Jgit на Mac для некоторых пользователей мы видели следующее исключение:

org.eclipse.jgit.transport.JschConfigSessionFactory.getSession(JschConfigSessionFactory.java:160)
    at org.eclipse.jgit.transport.SshTransport.getSession(SshTransport.java:137)
    at org.eclipse.jgit.transport.TransportGitSsh$SshFetchConnection.<init>(TransportGitSsh.java:274)
    at org.eclipse.jgit.transport.TransportGitSsh.openFetch(TransportGitSsh.java:169)
    at org.eclipse.jgit.transport.FetchProcess.executeImp(FetchProcess.java:136)
    at org.eclipse.jgit.transport.FetchProcess.execute(FetchProcess.java:122)
    at org.eclipse.jgit.transport.Transport.fetch(Transport.java:1236)
    at org.eclipse.jgit.api.FetchCommand.call(FetchCommand.java:234)
    ... 17 more
Caused by: com.jcraft.jsch.JSchException: invalid privatekey: [[email protected]
    at com.jcraft.jsch.KeyPair.load(KeyPair.java:664)
    at com.jcraft.jsch.KeyPair.load(KeyPair.java:561)
    at com.jcraft.jsch.IdentityFile.newInstance(IdentityFile.java:40)
    at com.jcraft.jsch.JSch.addIdentity(JSch.java:407)
    at com.jcraft.jsch.JSch.addIdentity(JSch.java:367)
    at org.eclipse.jgit.transport.JschConfigSessionFactory.getJSch(JschConfigSessionFactory.java:276)
    at org.eclipse.jgit.transport.JschConfigSessionFactory.createSession(JschConfigSessionFactory.java:220)
    at org.eclipse.jgit.transport.JschConfigSessionFactory.createSession(JschConfigSessionFactory.java:176)
    at org.eclipse.jgit.transport.JschConfigSessionFactory.getSession(JschConfigSessionFactory.java:110)

Основной причиной было обнаружено несоответствие закрытого ключа ssh. Исключение произошло только для пользователей с ключом более нового вида ed25519, который выводит этот заголовок ключа:

-----BEGIN OPENSSH PRIVATE KEY-----

вместо доброго RSA:

-----BEGIN RSA PRIVATE KEY-----

регенерация ключа RSA (ssh-keygen -t rsa) ssh-keygen -t rsa исключение.

Отредактируйте следующие комментарии: Если у вас OpenSSH 7.8 и выше, вам может понадобиться добавить -m PEM в команду генерации: ssh-keygen -t rsa -m PEM

Ответ 2

Последние версии OpenSSH (7.8 и новее) по умолчанию генерируют ключи в новом формате OpenSSH, который начинается с:

-----BEGIN OPENSSH PRIVATE KEY-----

JSch не поддерживает этот формат ключа.


Вы можете использовать ssh-keygen для преобразования ключа в классический формат OpenSSH:

ssh-keygen -p -f file -m pem -P passphrase -N passphrase

(если ключ не зашифрован парольной фразой, используйте "" вместо passphrase)


Если вы используете Windows, вы можете использовать PuTTYgen (из пакета PuTTY). Загрузите ключ и перейдите в "Преобразования"> "Экспорт ключа OpenSSH". Для ключей RSA будет использоваться классический формат.


Если вы создаете новый ключ с помощью ssh-keygen, просто добавьте -m PEM чтобы сгенерировать новый ключ в классическом формате:

ssh-keygen -m PEM

Ответ 3

  1. Вы читаете файл с именем .pem и де-base64 все это и обрабатываете результат как PKCS8-незашифрованный, по-видимому, успешно. Это означает, что файл НЕ был PEM-форматом. Формат PEM как минимум ДОЛЖЕН иметь действительные строки dash-BEGIN и dash-END, которые, если их не удалить, приводят к тому, что de-base64 либо дает сбой, либо ошибается. (Некоторые форматы PEM также имеют заголовки в стиле 822, которые должны обрабатываться.)

  2. Вы, кажется, используете BouncyCastle, но в моих версиях нет конструктора PKCS8Generator который принимает только RSAPrivateKey. Самое близкое, что работает, это JcaPKCS8Generator (RSAPrivateKey implements PrivateKey, OutputEncryptor=null) (т. JcaPKCS8Generator (RSAPrivateKey implements PrivateKey, OutputEncryptor=null) но связанный класс и два аргумента, а не один).

  3. PemWriter буферизован, и вы не StringWriter его, прежде чем посмотреть на базовый StringWriter. В результате writer.toString().getBytes() - это пустой массив нулевой длины, который JSch справедливо считает недействительным.

С исправленными # 2 и # 3 и использованием моего ввода и вызовом JSch напрямую, а не через JGit, это работает для меня.