2012-06-26 7 views
14

Gần đây, một khách hàng của chúng tôi đã chuyển một số tệp quan trọng bất ngờ mà chúng tôi thu thập từ máy chủ ftp sang sftp. Ban đầu tôi đã được ấn tượng rằng nó sẽ được đơn giản để viết hoặc tìm thấy một tiện ích java có thể xử lý sftp, điều này chắc chắn đã không được chứng minh là trường hợp. Điều gì cũng đã phức tạp vấn đề này là chúng tôi đang cố gắng kết nối với máy chủ sftp từ một nền tảng cửa sổ (do đó, định nghĩa của nơi SSH_HOME là trên máy khách sẽ rất bối rối).Ví dụ đáng tin cậy về cách sử dụng SFTP bằng cách sử dụng xác thực khóa riêng tư công khai với Java

Tôi đã sử dụng thư viện apache-commons-vfs và đã quản lý để nhận được giải pháp đáng tin cậy cho hoạt động xác thực tên người dùng/mật khẩu, nhưng không có gì có thể xử lý xác thực khóa công khai/riêng tư một cách đáng tin cậy.

Ví dụ sau đây hoạt động để xác thực tên người dùng/mật khẩu, nhưng tôi muốn điều chỉnh nó để xác thực khóa cá nhân/khóa công khai.

public static void sftpGetFile(String server, String userName,String password, 
     String remoteDir, String localDir, String fileNameRegex) 
    { 

     File localDirFile = new File(localDir); 
     FileSystemManager fsManager = null; 

     if (!localDirFile.exists()) { 
      localDirFile.mkdirs(); 
     } 

     try { 
      fsManager = VFS.getManager(); 
     } catch (FileSystemException ex) { 
      LOGGER.error("Failed to get fsManager from VFS",ex); 
      throw new RuntimeException("Failed to get fsManager from VFS", ex); 
     } 

     UserAuthenticator auth = new StaticUserAuthenticator(null, userName,password); 

     FileSystemOptions opts = new FileSystemOptions(); 

     try { 
      DefaultFileSystemConfigBuilder.getInstance().setUserAuthenticator(opts, 
        auth); 
     } catch (FileSystemException ex) { 
      LOGGER.error("setUserAuthenticator failed", ex); 
      throw new RuntimeException("setUserAuthenticator failed", ex); 
     } 
     Pattern filePattern = Pattern.compile(fileNameRegex); 
     String startPath = "sftp://" + server + remoteDir; 
     FileObject[] children; 

     // Set starting path on remote SFTP server. 
     FileObject sftpFile; 
     try { 
      sftpFile = fsManager.resolveFile(startPath, opts); 

      LOGGER.info("SFTP connection successfully established to " + 
        startPath); 
     } catch (FileSystemException ex) { 
      LOGGER.error("SFTP error parsing path " + 
        remoteDir, 
        ex); 

      throw new RuntimeException("SFTP error parsing path " + 
        remoteDir, 
        ex); 
     } 

     // Get a directory listing 
     try { 
      children = sftpFile.getChildren(); 
     } catch (FileSystemException ex) { 
      throw new RuntimeException("Error collecting directory listing of " + 
        startPath, ex); 
     } 

     search: 
     for (FileObject f : children) { 
      try { 
       String relativePath = 
         File.separatorChar + f.getName().getBaseName(); 

       if (f.getType() == FileType.FILE) { 
        System.out.println("Examining remote file " + f.getName()); 

        if (!filePattern.matcher(f.getName().getPath()).matches()) { 
         LOGGER.info(" Filename does not match, skipping file ." + 
           relativePath); 
         continue search; 
        } 

        String localUrl = "file://" + localDir + relativePath; 
        String standardPath = localDir + relativePath; 
        System.out.println(" Standard local path is " + standardPath); 
        LocalFile localFile = 
          (LocalFile) fsManager.resolveFile(localUrl); 
        System.out.println(" Resolved local file name: " + 
          localFile.getName()); 

        if (!localFile.getParent().exists()) { 
         localFile.getParent().createFolder(); 
        } 

        System.out.println(" ### Retrieving file ###"); 
        localFile.copyFrom(f, 
          new AllFileSelector()); 
       } else { 
        System.out.println("Ignoring non-file " + f.getName()); 
       } 
      } catch (FileSystemException ex) { 
       throw new RuntimeException("Error getting file type for " + 
         f.getName(), ex); 
      } 

     } 

     FileSystem fs = null; 
     if (children.length > 0) { 
      fs = children[0].getFileSystem(); // This works even if the src is closed. 
      fsManager.closeFileSystem(fs); 
     } 
    } 

Tôi đã có chìa khóa riêng tư của tôi được lưu trữ tại một địa điểm nổi tiếng và khóa công khai của tôi đã được distrubuted đến máy chủ (chúng tôi đã thử nghiệm mà các phím này hoạt động thành công khi kết nối sử dụng các công cụ khác)

Tôi có chơi xung quanh với cách thêm dòng sau

SftpFileSystemConfigBuilder.getInstance().setIdentities(this.opts, new File[]{new File("c:/Users/bobtbuilder/.ssh/id_dsa.ppk")}); 

này thành công sẽ tải private key vào toàn bộ khuôn khổ nhưng không bao giờ sau đó nó sử dụng rằng chìa khóa để xác thực hơn nữa.

Bất kỳ trợ giúp hoặc chỉ đường nào được đón nồng nhiệt nhất

+0

Isnt 'ppk' một điều putty? Bạn không nên sử dụng 'pem'? – prodigitalson

+0

Có thể, nhưng không phải là máy chủ/khách hàng sftp bất khả tri về cách tạo khóa. Tôi nghĩ điều quan trọng là họ chỉ tuân theo chuẩn mở-ssh? – grumblebee

Trả lời

14

Sau nhiều lần khám phá, cuối cùng tôi cũng đã tự mình trả lời. Dường như nhiều sự phiền toái của tôi là phải làm theo định dạng của khóa riêng và khóa công khai

privateKey phải ở định dạng openSSH publicKey vì bất kỳ lý do gì chỉ có thể được dán từ cửa sổ puttyGen (xuất khóa công khai dường như luôn cung cấp nó với thiếu tiêu đề có nghĩa là cửa sổ máy chủ FreeSSHd không thể sử dụng nó)

Nhưng dù sao đây là mã của tôi cuối cùng tôi đã đưa ra bao gồm javadoc, vì vậy hy vọng sẽ tiết kiệm được một số người khác đau tôi đã đi qua

/** 
* Fetches a file from a remote sftp server and copies it to a local file location. The authentication method used 
* is public/private key authentication. <br><br> 

* IMPORTANT: Your private key must be in the OpenSSH format, also it must not have a passphrase associated with it. 
* (currently the apache-commons-vfs2 library does not support passphrases)<p> 
* 
* Also remember your public key needs to be on the sftp server. If you were connecting as user 'bob' then your 
* public key will need to be in '.ssh/bob' on the server (the location of .ssh will change depending on the type 
* of sftp server) 
* 
* @param server The server we care connection to 
* @param userName The username we are connection as 
* @param openSSHPrivateKey The location of the private key (which must be in openSSH format) on the local machine 
* @param remoteDir The directory from where you want to retrieve the file on the remote machine (this is in reference to SSH_HOME, SSH_HOME is the direcory you 
* automatically get directed to when connecting) 
* @param remoteFile The name of the file on the remote machine to be collected (does not support wild cards) 
* @param localDir The direcoty on the local machine where you want the file to be copied to 
* @param localFileName The name you wish to give to retrieved file on the local machine 
* @throws IOException - Gets thrown is there is any problem fetching the file 
*/ 
public static void sftpGetFile_keyAuthentication(String server, String userName, String openSSHPrivateKey, 
    String remoteDir,String remoteFile, String localDir, String localFileName) throws IOException 
{ 

    FileSystemOptions fsOptions = new FileSystemOptions(); 
    FileSystemManager fsManager = null; 
    String remoteURL = "sftp://" + userName + "@" + server + "/" + remoteDir + "/" + remoteFile; 
    String localURL = "file://" + localDir + "/" + localFileName; 

    try { 
     SftpFileSystemConfigBuilder.getInstance().setStrictHostKeyChecking(fsOptions, "no"); 
     SftpFileSystemConfigBuilder.getInstance().setIdentities(fsOptions, new File[]{new File(openSSHPrivateKey)}); 
     fsManager = VFS.getManager(); 
     FileObject remoteFileObject = fsManager.resolveFile(remoteURL, fsOptions); 
     LocalFile localFile = 
        (LocalFile) fsManager.resolveFile(localURL); 
     localFile.copyFrom(remoteFileObject, 
        new AllFileSelector()); 
    } catch (FileSystemException e) { 
     LOGGER.error("Problem retrieving from " + remoteURL + " to " + localURL,e); 
     throw new IOException(e); 
    } 
} 
+0

Chỉ là một câu hỏi ngu ngốc ... Tại sao có một khóa riêng, khó khăn nó phải là chìa khóa công cộng? – LStrike

+0

Khóa công khai nằm trên máy chủ (từ xa). Nó sẽ đưa ra một "thách thức" khi máy khách (cục bộ) cố kết nối. Khóa riêng được sử dụng để giải mã cả thách thức và ký vào phản hồi mà sau đó được xác nhận bằng khóa công khai (từ xa). Do đó, khóa riêng được sử dụng cục bộ làm bằng chứng mạnh mẽ rằng người dùng địa phương là đúng. – sofend

6

Bài đăng và câu trả lời này rất hữu ích, cảm ơn bạn rất nhiều.

Tôi chỉ muốn thêm tích hợp vào câu lệnh "hiện tại thư viện apache-commons-vfs2 không hỗ trợ cụm mật khẩu", vì tôi đã làm nó cùng với cụm mật khẩu và nó hoạt động.

Bạn phải nhập thư viện jsch vào dự án của mình (tôi đã sử dụng 0.1.49) và triển khai giao diện "com.jcraft.jsch.UserInfo".

Something như thế này nên được tốt:

public class SftpUserInfo implements UserInfo { 

    public String getPassphrase() { 
     return "yourpassphrase"; 
    } 

    public String getPassword() { 
     return null; 
    } 

    public boolean promptPassphrase(String arg0) { 
     return true; 
    } 

    public boolean promptPassword(String arg0) { 
     return false; 
    } 
} 

Và sau đó bạn có thể thêm nó vào SftpFileSystemConfigBuilder theo cách này:

SftpFileSystemConfigBuilder.getInstance().setUserInfo(fsOptions, new SftpUserInfo()); 

Hope this helps.

+0

Cụm từ vượt qua cụm từ mật khẩu này đã giúp rất nhiều .. Cảm ơn bạn rất nhiều. –

+0

trả lại đúng; trong promptPassphrase() là cực kỳ quan trọng nếu bạn có cụm mật khẩu. Câu trả lời tuyệt vời! +1 – nilesh

4

Tôi đoán đây là những gì bạn đang tìm kiếm -

/** 
* @param args 
*/ 
public static void main(String[] args) { 

    /*Below we have declared and defined the SFTP HOST, PORT, USER 
      and Local private key from where you will make connection */ 
    String SFTPHOST = "10.20.30.40"; 
    int SFTPPORT = 22; 
    String SFTPUSER = "kodehelp"; 
    // this file can be id_rsa or id_dsa based on which algorithm is used to create the key 
    String privateKey = "/home/kodehelp/.ssh/id_rsa"; 
    String SFTPWORKINGDIR = "/home/kodehelp/"; 

    JSch jSch = new JSch(); 
    Session  session  = null; 
    Channel  channel  = null; 
    ChannelSftp channelSftp = null; 
    try { 
     jSch.addIdentity(privateKey); 
     System.out.println("Private Key Added."); 
     session = jSch.getSession(SFTPUSER,SFTPHOST,SFTPPORT); 
     System.out.println("session created."); 

     java.util.Properties config = new java.util.Properties(); 
     config.put("StrictHostKeyChecking", "no"); 
     session.setConfig(config); 
     session.connect(); 
     channel = session.openChannel("sftp"); 
     channel.connect(); 
     System.out.println("shell channel connected...."); 
     channelSftp = (ChannelSftp)channel; 
     channelSftp.cd(SFTPWORKINGDIR); 
     System.out.println("Changed the directory..."); 
    } catch (JSchException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } catch (SftpException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    }finally{ 
     if(channelSftp!=null){ 
      channelSftp.disconnect(); 
      channelSftp.exit(); 
     } 
     if(channel!=null) channel.disconnect(); 

     if(session!=null) session.disconnect(); 
    } 
} 

Xem thêm tại

http://kodehelp.com/sftp-connection-public-key-authentication-java/

+1

Trong khi liên kết này có thể trả lời câu hỏi, tốt hơn nên bao gồm các phần thiết yếu của câu trả lời ở đây và cung cấp liên kết để tham khảo. Câu trả lời chỉ liên kết có thể trở thành không hợp lệ nếu trang được liên kết thay đổi. –

+1

Đã thêm đoạn mã và liên kết để tham khảo –