2008-12-16 23 views
11

Có ai biết cách lấy vé dịch vụ từ Trung tâm phân phối khóa (KDC) bằng cách sử dụng Java GSS-API không?Cách lấy vé dịch vụ Kerberos qua GSS-API?

Tôi có một ứng dụng dày-khách hàng đầu tiên xác thực thông qua JAAS bằng cách sử dụng Krb5LoginModule để lấy TGT từ bộ nhớ vé (nền: Windows ví dụ: sử dụng triển khai kerberos và lưu trữ vé cấp trong khu vực bộ nhớ an toàn). Từ Trình quản lý đăng nhập, tôi nhận đối tượng Chủ đề chứa TGT. Bây giờ tôi hy vọng khi tôi tạo một đối tượng GSSCredential cụ thể cho dịch vụ của tôi, vé dịch vụ cũng sẽ được đưa vào các thông tin cá nhân của Subject (Tôi đã đọc ở đâu đó trên web). Vì vậy, tôi đã thử như sau:

// Exception handling ommitted 
LoginContext lc = new LoginContext("HelloEjbClient", new DialogCallbackHandler()); 
lc.login() 
Subject.doAs(lc.getSubject(), new PrivilegedAction() { 

    public Object run() { 
     GSSManager manager = GSSManager.getInstance(); 
     GSSName clientName = manager.createName("clientUser", GSSName.NT_USER_NAME); 
     GSSCredential clientCreds = manager.createCredential(clientName, 8 * 3600, createKerberosOid(), GSSCredential.INITIATE_ONLY); 

     GSSName serverName = manager.createName("[email protected]", GSSName.NT_HOSTBASED_SERVICE); 
     manager.createCredential(serverName, GSSCredential.INDEFINITE_LIFETIME, createKerberosOid(), GSSCredential.INITIATE_ONLY); 
     return null; 
    } 

    private Oid createKerberosOid() { 
     return new Oid("1.2.840.113554.1.2.2"); 
    } 

}); 

Đáng tiếc là tôi có được một GSSException: Không có thông tin hợp lệ được cung cấp (cấp Cơ chế: Không thể tìm thấy bất kỳ tgt Kerberos).

Trả lời

14

Sự hiểu biết của tôi về việc nhận được vé dịch vụ là sai. Tôi không cần phải có được các thông tin từ dịch vụ - điều này là không thể trên máy khách, bởi vì máy khách thực sự không có TGT cho máy chủ và do đó không có quyền nhận các thông tin dịch vụ. Điều thiếu sót ở đây là tạo một GSSContext mới và khởi tạo nó. Giá trị trả về từ phương thức này chứa vé dịch vụ, nếu tôi đã hiểu chính xác. Đây là một ví dụ mã hoạt động. Nó phải được thực hiện trong PrivilegedAction thay mặt cho một đối tượng đã đăng nhập:

GSSManager manager = GSSManager.getInstance(); 
    GSSName clientName = manager.createName("clientUser", GSSName.NT_USER_NAME); 
    GSSCredential clientCred = manager.createCredential(clientName, 
                 8 * 3600, 
                 createKerberosOid(), 
                 GSSCredential.INITIATE_ONLY); 

    GSSName serverName = manager.createName("[email protected]", GSSName.NT_HOSTBASED_SERVICE); 

    GSSContext context = manager.createContext(serverName, 
               createKerberosOid(), 
               clientCred, 
               GSSContext.DEFAULT_LIFETIME); 
    context.requestMutualAuth(true); 
    context.requestConf(false); 
    context.requestInteg(true); 

    byte[] outToken = context.initSecContext(new byte[0], 0, 0); 
    System.out.println(new BASE64Encoder().encode(outToken)); 
    context.dispose(); 

OutToken chứa sau đó chứa Vé dịch vụ. Tuy nhiên đây không phải là cách mà GSS-API được sử dụng. Mục tiêu của nó là giấu các chi tiết đó vào mã, vì vậy tốt hơn nên thiết lập GSSContext bằng GSS-API trên cả hai mặt. Nếu không, bạn thực sự nên biết những gì bạn đang làm vì lỗ hổng bảo mật tiềm năng. Để biết thêm thông tin, hãy đọc kỹ số Sun SSO tutorial with kerberos hơn tôi đã làm.

CHỈNH SỬA: Chỉ cần quên rằng tôi đang sử dụng Windows XP với SP2. Có một "tính năng" mới trong phiên bản Windows này không cho phép sử dụng TGT trong RAM Windows. Bạn phải chỉnh sửa registry để cho phép điều này. Để biết thêm thông tin, hãy xem chủ đề JGSS Troubleshooting page trong trường hợp bạn gặp phải "KrbException: KDC không hỗ trợ loại mã hóa (14)" như tôi đã làm.

+0

@Michael: Cảm ơn sự đóng góp của bạn. Như bạn có thể thấy tôi đã viết điều này khá lâu rồi nên tôi không nhớ chi tiết chính xác, nhưng tôi khá chắc chắn đây chỉ là một ví dụ. Bạn nghĩ gì có thể được thực hiện để cải thiện câu trả lời này? –

+0

kiểm tra liên kết của bạn đến hướng dẫn Sun SSO bằng kerberos và xem hình 6. Có vòng lặp mà tôi đã viết. –

+0

Tôi nghĩ rằng tôi không bao gồm vòng lặp vì tôi không thể tìm ra phương thức readToken() và sendToken (...) được cho là phải làm. –

7

Tôi gặp nhiều vấn đề khi sử dụng mã này, nhưng tôi có ít nhất một giải pháp. Tôi đăng nó ở đây, có lẽ nó sẽ giúp một số bạn ...

/** 
* Tool to retrieve a kerberos ticket. This one will not be stored in the windows ticket cache. 
*/ 
public final class KerberosTicketRetriever 
{ 
    private final static Oid KERB_V5_OID; 
    private final static Oid KRB5_PRINCIPAL_NAME_OID; 

    static { 
     try 
     { 
      KERB_V5_OID = new Oid("1.2.840.113554.1.2.2"); 
      KRB5_PRINCIPAL_NAME_OID = new Oid("1.2.840.113554.1.2.2.1"); 

     } catch (final GSSException ex) 
     { 
      throw new Error(ex); 
     } 
    } 

    /** 
    * Not to be instanciated 
    */ 
    private KerberosTicketRetriever() {}; 

    /** 
    * 
    */ 
    private static class TicketCreatorAction implements PrivilegedAction 
    { 
     final String userPrincipal; 
     final String applicationPrincipal; 

     private StringBuffer outputBuffer; 

     /** 
     * 
     * @param userPrincipal p.ex. <tt>[email protected]</tt> 
     * @param applicationPrincipal p.ex. <tt>HTTP/webserver.myfirm.com</tt> 
     */ 
     private TicketCreatorAction(final String userPrincipal, final String applicationPrincipal) 
     { 
      this.userPrincipal = userPrincipal; 
      this.applicationPrincipal = applicationPrincipal; 
     } 

     private void setOutputBuffer(final StringBuffer newOutputBuffer) 
     { 
      outputBuffer = newOutputBuffer; 
     } 

     /** 
     * Only calls {@link #createTicket()} 
     * @return <tt>null</tt> 
     */ 
     public Object run() 
     { 
      try 
      { 
       createTicket(); 
      } 
      catch (final GSSException ex) 
      { 
       throw new Error(ex); 
      } 

      return null; 
     } 

     /** 
     * 
     * @throws GSSException 
     */ 
     private void createTicket() throws GSSException 
     { 
      final GSSManager manager = GSSManager.getInstance(); 
      final GSSName clientName = manager.createName(userPrincipal, KRB5_PRINCIPAL_NAME_OID); 
      final GSSCredential clientCred = manager.createCredential(clientName, 
        8 * 3600, 
        KERB_V5_OID, 
        GSSCredential.INITIATE_ONLY); 

      final GSSName serverName = manager.createName(applicationPrincipal, KRB5_PRINCIPAL_NAME_OID); 

      final GSSContext context = manager.createContext(serverName, 
        KERB_V5_OID, 
        clientCred, 
        GSSContext.DEFAULT_LIFETIME); 
      context.requestMutualAuth(true); 
      context.requestConf(false); 
      context.requestInteg(true); 

      final byte[] outToken = context.initSecContext(new byte[0], 0, 0); 

      if (outputBuffer !=null) 
      { 
       outputBuffer.append(String.format("Src Name: %s\n", context.getSrcName())); 
       outputBuffer.append(String.format("Target : %s\n", context.getTargName())); 
       outputBuffer.append(new BASE64Encoder().encode(outToken)); 
       outputBuffer.append("\n"); 
      } 

      context.dispose(); 
     } 
    } 

    /** 
    * 
    * @param realm p.ex. <tt>MYFIRM.COM</tt> 
    * @param kdc p.ex. <tt>kerbserver.myfirm.com</tt> 
    * @param applicationPrincipal cf. {@link #TicketCreatorAction(String, String)} 
    * @throws GSSException 
    * @throws LoginException 
    */ 
    static public String retrieveTicket(
      final String realm, 
      final String kdc, 
      final String applicationPrincipal) 
    throws GSSException, LoginException 
    { 

     // create the jass-config-file 
     final File jaasConfFile; 
     try 
     { 
      jaasConfFile = File.createTempFile("jaas.conf", null); 
      final PrintStream bos = new PrintStream(new FileOutputStream(jaasConfFile)); 
      bos.print(String.format(
        "Krb5LoginContext { com.sun.security.auth.module.Krb5LoginModule required refreshKrb5Config=true useTicketCache=true debug=true ; };" 
      )); 
      bos.close(); 
      jaasConfFile.deleteOnExit(); 
     } 
     catch (final IOException ex) 
     { 
      throw new IOError(ex); 
     } 

     // set the properties 
     System.setProperty("java.security.krb5.realm", realm); 
     System.setProperty("java.security.krb5.kdc", kdc); 
     System.setProperty("java.security.auth.login.config",jaasConfFile.getAbsolutePath()); 

     // get the Subject(), i.e. the current user under Windows 
     final Subject subject = new Subject(); 
     final LoginContext lc = new LoginContext("Krb5LoginContext", subject, new DialogCallbackHandler()); 
     lc.login(); 

     // extract our principal 
     final Set<Principal> principalSet = subject.getPrincipals(); 
     if (principalSet.size() != 1) 
      throw new AssertionError("No or several principals: " + principalSet); 
     final Principal userPrincipal = principalSet.iterator().next(); 

     // now try to execute the SampleAction as the authenticated Subject 
     // action.run() without doAsPrivileged leads to 
     // No valid credentials provided (Mechanism level: Failed to find any Kerberos tgt) 
     final TicketCreatorAction action = new TicketCreatorAction(userPrincipal.getName(), applicationPrincipal); 
     final StringBuffer outputBuffer = new StringBuffer(); 
     action.setOutputBuffer(outputBuffer); 
     Subject.doAsPrivileged(lc.getSubject(), action, null); 

     return outputBuffer.toString(); 
    } 

    public static void main (final String args[]) throws Throwable 
    { 
     final String ticket = retrieveTicket("MYFIRM.COM", "kerbserver", "HTTP/webserver.myfirm.com"); 
     System.out.println(ticket); 
    } 
} 
Các vấn đề liên quan