@Override
    public Boolean authenticate(final Host bookmark, final LoginCallback prompt, final CancelCallback cancel) throws BackgroundException {
        final Credentials credentials = bookmark.getCredentials();
        if(credentials.isPublicKeyAuthentication()) {
            if(log.isDebugEnabled()) {
                log.debug(String.format("Login using public key authentication with credentials %s", credentials));
            }
            final Local privKey = credentials.getIdentity();
            final Local pubKey;
            final FileKeyProvider provider;
            final AtomicBoolean canceled = new AtomicBoolean();
            try {
                final KeyFormat format = KeyProviderUtil.detectKeyFileFormat(
                        new InputStreamReader(privKey.getInputStream(), StandardCharsets.UTF_8), true);
                if(log.isInfoEnabled()) {
                    log.info(String.format("Reading private key %s with key format %s", privKey, format));
                }
                switch(format) {
                    case PKCS8:
                        provider = new PKCS8KeyFile.Factory().create();
                        pubKey = null;
                        break;
                    case OpenSSH: {
                        provider = new OpenSSHKeyFile.Factory().create();
                        final File f = OpenSSHKeyFileUtil.getPublicKeyFile(new File(privKey.getAbsolute()));
                        if(f != null) {
                            pubKey = LocalFactory.get(f.getAbsolutePath());
                        }
                        else {
                            pubKey = null;
                        }
                        break;
                    }
                    case OpenSSHv1: {
                        provider = new OpenSSHKeyV1KeyFile.Factory().create();
                        final File f = OpenSSHKeyFileUtil.getPublicKeyFile(new File(privKey.getAbsolute()));
                        if(f != null) {
                            pubKey = LocalFactory.get(f.getAbsolutePath());
                        }
                        else {
                            pubKey = null;
                        }
                        break;
                    }
                    case PuTTY:
                        provider = new PuTTYKeyFile.Factory().create();
                        pubKey = null;
                        break;
                    default:
                        throw new InteroperabilityException(String.format("Unknown key format for file %s", privKey.getName()));
                }
                provider.init(new InputStreamReader(privKey.getInputStream(), StandardCharsets.UTF_8),
                        pubKey != null ? new InputStreamReader(pubKey.getInputStream(), StandardCharsets.UTF_8) : null,
                        new PasswordFinder() {
                    @Override
                    public char[] reqPassword(Resource<?> resource) {
                        if(StringUtils.isEmpty(credentials.getIdentityPassphrase())) {
                            try {
                                // Use password prompt
                                final Credentials input = prompt.prompt(bookmark,
                                        LocaleFactory.localizedString("Private key password protected", "Credentials"),
                                        String.format("%s (%s)",
                                                LocaleFactory.localizedString("Enter the passphrase for the private key file", "Credentials"),
                                                privKey.getAbbreviatedPath()),
                                        new LoginOptions()
                                                .icon(bookmark.getProtocol().disk())
                                                .user(false).password(true)
                                );
                                credentials.setSaved(input.isSaved());
                                credentials.setIdentityPassphrase(input.getPassword());
                            }
                            catch(LoginCanceledException e) {
                                canceled.set(true);
                                // Return null if user cancels
                                return StringUtils.EMPTY.toCharArray();
                            }
                        }
                        return credentials.getIdentityPassphrase().toCharArray();
                    }

                    @Override
                    public boolean shouldRetry(Resource<?> resource) {
                        return false;
                    }
                });
                client.auth(credentials.getUsername(), new AuthPublickey(provider));
                return client.isAuthenticated();
            }
            catch(IOException e) {
                if(canceled.get()) {
                    throw new LoginCanceledException();
                }
                throw new SFTPExceptionMappingService().map(e);
            }
        }
        return false;
    }
@Test(expected = LoginCanceledException.class)
    public void testAuthenticateOpenSSHKeyWithPassword() throws Exception {
        final Local key = new Local(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString());
        try {
            new DefaultLocalTouchFeature().touch(key);
            IOUtils.copy(new StringReader("-----BEGIN RSA PRIVATE KEY-----\n" +
                "Proc-Type: 4,ENCRYPTED\n" +
                "DEK-Info: AES-128-CBC,356A353DAFDC2E16E8BD0EE23A6201A2\n" +
                "\n" +
                "pvpC2RA1TteVsp584fSZ6RYFz97CF8tJXfyP4/8UdzpIVM8VXXlk4g3AnyI9/JD0\n" +
                "4/0dzNqGvg/84xUpDpdJp/w8fWJ8IzE7RXf1xDfg0xavr2iegp2aZBd48KVKImwU\n" +
                "yJlzy27VmVvIvWID2zPrNhOWzr4AdnP/NprLfhHPQDHV5gRcS92s6vFktZOPzNtQ\n" +
                "3O+j3O5MAyf/MpgPH4BTubOjcuZuZg3AJCjEPxLlwrRfxqXkRXXMB7XxDFdK7LQ/\n" +
                "fQnJzikcrYXFio8+DJhBg7OyOnlAmC0I85YomZJ+8C3A3bye9PakIxHJn/qNIQew\n" +
                "BujHxPVmnezjFzStr/SyfLE2a+RZu84Jm6u9+DuJYF5/Vo6yv6+zubsVaflXp5fS\n" +
                "SAogS0quWfoqoiUfhgCuOZlqv/aeo/BEetUEdHVi4KTdeSpcfrJa4CphXd8TwEPN\n" +
                "L4NFSc+8CeGayO45o5lXeQiKa4UH2oPEBaANHK4SQPKJ9NdyTlFN/O1c77kCvG4W\n" +
                "4thchQkUvwqwTYXwx9jNW3x7FBytJwmhi9DpzHMa/LFRrnedarFPDgep4E40NjRB\n" +
                "fy877Wd+KJTlrHjyQR13wgtlGZdcTO5QzLseztxqdaD14Dn7jPF/YJBDaj65Jw1N\n" +
                "+G6EB0zN70WL7Y3+2HnSLNZWEnLhletzfwbjVqr+Vg4XB2HQKH52gCyh+ITPEjqR\n" +
                "wU00oMJvGf518U+6awxzb3zwnoxMrFwcnaLqwsZNQ5CjmYVE/yERSK47OMYCNQl4\n" +
                "0Xxa9mWYBqWlfdMurkGCD6OuUWMx5t4mcpSg30UEQNBEVfrVk6t480iztgVJprEO\n" +
                "vhepM2nw326PH5VYAoXH+OmEezjI1AmHKqpbB/y9UQv6ZjEyUT70Tbs9JBtU4dze\n" +
                "Yha1Dc0+eYkUvZ5AjENQ/Bvfdyit4xxbDrU6TbFmyHpHwMPCNkcgO0u/Mgtc5Hmc\n" +
                "Gi6RaxUaxSZ2IlpJDNkqAzmv1Xr+M9TxbF2gZY+TJHUt/mc1rFpTl2qZ/tK/Ei1U\n" +
                "8TBVJHcNNwHiHtm/NpREYTmzu0s8X602JgXrkBxkM40NGVRqd08jaULhxdWcTmzW\n" +
                "pweib9WhIrvjTNZTAjjGku625qLihDt5jtbJxspM2dLGfcG4zgYgRr4u9HA+60oD\n" +
                "l1oNjz8IfBuJLJ3rwENI6oX9FW7huKc/XV1hP72/l2VhfuxtTufdjbaiwwiwObRA\n" +
                "O+zwB8NPWRG6UYj9IAWjASoPXOoyhk/f1fzvTH7xeO35QjkCICln095T+hNMZRiC\n" +
                "VpCCKsQGY2O30D9OJlnTpylBAq/Q/HXNL8Jj2f/rZRqDGzidj2No5mun/pZ3uwzr\n" +
                "CRrEpvfFuf8g1EnPQXmdlYRi/nmtBKsiQr0GWVzIOzNRi/tgsV0tyUgBT9QL4JKt\n" +
                "/z54PrlBK74I9SWcBv9EwCAfL9YdZ7mW0iWrmUUmcpuJcRUXnKvTynTpq/l6GE8+\n" +
                "Ld5saHMVWt7GlEbM3Fjqfvj7/dbtcy3TTmy0Vx4GbKzsaPytAb2jgLGn8bQfjQzp\n" +
                "hnPC1l+r7ebV7tBR216+6PmsXQu7atqgbGjb7Dh+GP8Ak73F8v6LPtyz+tAOYwpB\n" +
                "-----END RSA PRIVATE KEY-----\n"), key.getOutputStream(false), StandardCharsets.UTF_8);
            final AtomicBoolean b = new AtomicBoolean();
            // Reconnect
            session.disconnect();
            session.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback());
            session.getHost().getCredentials().setIdentity(key);
            assertTrue(new SFTPPublicKeyAuthentication(session.getClient()).authenticate(session.getHost(), new DisabledLoginCallback() {
                @Override
                public Credentials prompt(final Host bookmark, String username, String title, String reason, LoginOptions options) throws LoginCanceledException {
                    b.set(true);
                    throw new LoginCanceledException();
                }
            }, new DisabledCancelCallback()));
            assertTrue(b.get());

        }
        finally {
            key.delete();
        }
    }