基于Kerberos认证的TCP通信
来源:互联网 发布:精通javascript pdf 编辑:程序博客网 时间:2024/06/17 15:14
1 前言
kerberos是一种安全协议,它涉及到三个部分:KDC、 Server端 与 Client端的代码。对于Kerberos 的整个认证过程,有很多介绍。但是对于如何在一个已有TCP程序中进行kerberos认证,一直不知道如何使用。前段时间在oracle的文件中,找到一个server与client的样例,结合这个用例,将它进行改写,便于理解。
前置说明:
- KDC需要保证能正常运行
- 有对应的的princal, 如在我们的例子中,就需要保证admin与lch这两个princal必须存在
2 代码介绍
2.1 服务端代码说明
package com.test.gssapi.sample1;import org.ietf.jgss.*;import java.io.*;import java.net.Socket;import java.net.ServerSocket;public class TestServer { private int localPort; private ServerSocket ss; private boolean iskerberos; private Socket socket = null; private GSSContext context = null; public TestServer(int port) { this.localPort = port; this.iskerberos = false; } public TestServer(int port, boolean iskerberos) { this.localPort = port; this.iskerberos = iskerberos; } public void receive() throws IOException, GSSException { this.ss = new ServerSocket(localPort); while(true) { socket = ss.accept(); DataInputStream inStream = new DataInputStream(socket.getInputStream()); DataOutputStream outStream = new DataOutputStream(socket.getOutputStream()); this.initKerberos(inStream, outStream); int length = inStream.readInt(); byte[] token = new byte[length]; token = new byte[length]; System.out.println("Will read token of size " + token.length); inStream.readFully(token); String s = new String(token); System.out.println(s); //1. 发送回去的消息 byte[] token1 = "Receive Client Message".getBytes(); outStream.writeInt(token1.length); outStream.write(token1); outStream.flush(); this.destroy(); } } private void initKerberos( DataInputStream inStream, DataOutputStream outStream) throws GSSException, IOException { if(!this.iskerberos) { return; } GSSManager manager = GSSManager.getInstance(); context = manager.createContext((GSSCredential) null); byte[] token = null; while (!context.isEstablished()) { token = new byte[inStream.readInt()]; System.out.println( "Will read input token of size " + token.length + " for processing by acceptSecContext"); inStream.readFully(token); token = context.acceptSecContext(token, 0, token.length); // Send a token to the peer if one was generated by // acceptSecContext if (token != null) { System.out.println("Will send token of size " + token.length + " from acceptSecContext."); outStream.writeInt(token.length); outStream.write(token); outStream.flush(); } } System.out.print("Context Established! "); System.out.println("Client is " + context.getSrcName()); System.out.println("Server is " + context.getTargName()); } private void destroy() { if(this.socket != null) { try { this.socket.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } this.socket = null; } if(this.context != null) { try { this.context.dispose(); } catch (GSSException e) { // TODO Auto-generated catch block e.printStackTrace(); } this.context = null; } } public static void main(String[] args) throws IOException, GSSException { int localPort = Integer.parseInt(args[0]); boolean iskerberos = false; if(args.length >= 2) { iskerberos = Boolean.parseBoolean(args[1]); } TestServer server = new TestServer(localPort, iskerberos); server.receive(); }}
2.2 客户端代码
package com.test.gssapi.sample1;import org.ietf.jgss.*;import java.net.Socket;import java.net.UnknownHostException;import java.io.IOException;import java.io.DataInputStream;import java.io.DataOutputStream;public class TestClient { private String srvPrincal; private String srvIP; private int srvPort; private boolean isKerberos; private Socket socket; private DataInputStream inStream; private DataOutputStream outStream; private GSSContext context; public TestClient(String srvPrincal, String srvIp, int srvPort, boolean iskerberos) { this.srvPrincal = srvPrincal; this.srvIP = srvIp; this.srvPort = srvPort; this.context = null; this.isKerberos = iskerberos; try { this.initSocket(); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } public TestClient(String srvPrincal, String srvIp, int srvPort) { this.srvPrincal = srvPrincal; this.srvIP = srvIp; this.srvPort = srvPort; this.context = null; this.isKerberos = false; try { this.initSocket(); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } public void destroy() throws IOException, GSSException { if (this.inStream != null) { this.inStream.close(); } if (this.outStream != null) { this.outStream.close(); } if (this.socket != null) { this.socket.close(); } if (this.context != null) { this.context.dispose(); } } private void initSocket() throws UnknownHostException, IOException { this.socket = new Socket(srvIP, srvPort); this.inStream = new DataInputStream(socket.getInputStream()); this.outStream = new DataOutputStream(socket.getOutputStream()); System.out.println("Connected to server: " + this.socket.getInetAddress()); } private void initKerberos() throws GSSException, IOException { if (!isKerberos) { return; } /** * Oid用来表示GSS-API这种机制。 这个值是协议中定义的(RFC 1964),这一个部分是固定的 */ Oid krb5Oid = new Oid("1.2.840.113554.1.2.2"); GSSManager manager = GSSManager.getInstance(); /* * Create a GSSName out of the server's name. The null indicates that this * application does not wish to make any claims about the syntax of this name * and that the underlying mechanism should try to parse it as per whatever * default syntax it chooses. */ /** * 创建一个GSSName,需要注意的是第一个参数srvPrincal,服务器收到消息后,会与自己的princal进行比较, * 如果不正确,就会抛出异常. */ GSSName serverName = manager.createName(srvPrincal, null); /* * Create a GSSContext for mutual authentication with the server. - serverName * is the GSSName that represents the server. - krb5Oid is the Oid that * represents the mechanism to use. The client chooses the mechanism to use. - * null is passed in for client credentials - DEFAULT_LIFETIME lets the * mechanism decide how long the context can remain valid. Note: Passing in null * for the credentials asks GSS-API to use the default credentials. This means * that the mechanism will look among the credentials stored in the current * Subject to find the right kind of credentials that it needs. */ System.out.println("init kerberos .. 1"); GSSContext context = manager.createContext(serverName, krb5Oid, null, GSSContext.DEFAULT_LIFETIME); // Set the desired optional features on the context. The client // chooses these options. context.requestMutualAuth(true); // Mutual authentication context.requestConf(true); // Will use confidentiality later context.requestInteg(true); // Will use integrity later System.out.println("init kerberos .. 2"); // Do the context eastablishment loop byte[] token = new byte[0]; while (!context.isEstablished()) { // token is ignored on the first call // 这里进行kerberos认证,即调用JaaS的代码进行认证。认证完成后,返回 // 这里我们可以把它GSS-api将JaaS的代码集成了。kerberos信息的确认是在此函数中进行的 token = context.initSecContext(token, 0, token.length); // Send a token to the server if one was generated by // initSecContext if (token != null) { System.out.println("Will send token of size " + token.length + " from initSecContext."); outStream.writeInt(token.length); outStream.write(token); outStream.flush(); } // If the client is done with context establishment // then there will be no more tokens to read in this loop if (!context.isEstablished()) { token = new byte[inStream.readInt()]; System.out .println("Will read input token of size " + token.length + " for processing by initSecContext"); inStream.readFully(token); } } System.out.println("Context Established! "); System.out.println("Client is " + context.getSrcName()); System.out.println("Server is " + context.getTargName()); } public void sendMessage() throws UnknownHostException, IOException, GSSException { // Obtain the command-line arguments and parse the port number System.setProperty("java.security.krb5.conf", "/etc/krb5.conf"); //判断是不是需要初始化kerberos this.initKerberos(); //发送消息 String msg = "Hello Server "; byte[] messageBytes = msg.getBytes(); outStream.writeInt(messageBytes.length); outStream.write(messageBytes); outStream.flush(); //收到服务端传回来的消息 byte[] token = new byte[0]; token = new byte[inStream.readInt()]; System.out.println("Will read token of size " + token.length); inStream.readFully(token); String s = new String(token); System.out.println(s); System.out.println("Exiting... "); this.destroy(); } public static void main(String[] args) throws IOException, GSSException { String princal = args[0]; String ip = args[1]; int port = Integer.parseInt(args[2]); boolean iskerberos = false; if(args.length >= 4) { iskerberos = Boolean.parseBoolean(args[3]); } TestClient client = new TestClient(princal, ip, port, iskerberos); client.sendMessage(); }}
2.3 配置文件说明
还管是服务端还是客户端都需要使用到jaas的配置文件,这个配置文件,在下面结合程序的执行来进行说明
2.4 程序的执行
需要特别指出的,对于客户端/服务端,需要保证KDC正常可用,而且其中包含了正常的princal信息。下面介绍它的执行过程
2.4.1 不带Kerberos认证的通信,即我们正常的连接方式
服务端执行命令:
java -classpath kerberosTest.jar com.test.gssapi.sample1.TestServer 9111
客户端执行命令:
java -classpath kerberosTest.jar com.test.gssapi.sample1.TestClient admin 192.168.1.56 9111
此时就是一个普通的TCP通信协议
2.4.2 加入Kerberos认证
服务端的命令:
[root@freeipa56 testKerberos]# java -classpath kerberosTest.jar -Djava.security.krb5.realm=EXAMPLE.COM -Djava.security.krb5.kdc=freeipa56.example.com -Djavax.security.auth.useSubjectCredsOnly=false -Djava.security.auth.login.config=server.conf com.test.gssapi.sample1.TestServer 9111 true
参数说明: 服务端一共有两个参数, 9111表示端口号, true表示启用kerberos,如果不输入,则表明不启用kerberos
客户端命令:
java -classpath kerberosTest.jar -Djava.security.krb5.realm=EXAMPLE.COM -Djava.security.krb5.kdc=freeipa56.example.com -Djavax.security.auth.useSubjectCredsOnly=false -Djava.security.auth.login.config=client.conf com.test.gssapi.sample1.TestClient admin 192.168.1.56 9111 true
参数说明:
客户端参数一共四个参数,
admin: 表示服务端对应的princal为admin, 服务端认证的princal必须为这个
192.168.1.56: 表明服务端对应的IP
9111: 为服务端监听的端口
true: 表明启用kerberos
Kerberos提供了多种认证方式,这些认证方式都是通过java.security.auth.login.config对应的配置文件来设置的。下面对于分几种情况说明.
2.4.2.1 通过输入用户名与密码的方式
服务端配置文件(即server.conf与client.conf):
[root@freeipa56 testKerberos]# cat server.conf com.sun.security.jgss.accept { com.sun.security.auth.module.Krb5LoginModule required storeKey=true; };
客户端的配置文件client.conf
[root@freeipa56 testKerberos]# cat client.conf com.sun.security.jgss.initiate { com.sun.security.auth.module.Krb5LoginModule required;};
此时客户端的打屏信息:
[lch@freeipa56 testKerberos]$ java -classpath kerberosTest.jar -Djava.security.krb5.realm=EXAMPLE.COM -Djava.security.krb5.kdc=freeipa56.example.com -Djavax.security.auth.useSubjectCredsOnly=false -Djava.security.auth.login.config=client.conf com.test.gssapi.sample1.TestClient admin 192.168.1.56 9111 trueConnected to server: /192.168.1.56Kerberos username [lch]: lch <------ 提示输入princalKerberos password for lch: <----- 提示输入相应的密码Will send token of size 558 from initSecContext.Will read input token of size 108 for processing by initSecContextContext Established! Client is lch@EXAMPLE.COMServer is adminWill read token of size 22Receive Client MessageExiting...
而服务端:
[root@freeipa56 testKerberos]# java -classpath kerberosTest.jar -Djava.security.krb5.realm=EXAMPLE.COM -Djava.security.krb5.kdc=freeipa56.example.com -Djavax.security.auth.useSubjectCredsOnly=false -Djava.security.auth.login.config=bcsLogin.conf com.test.gssapi.sample1.TestServer 9111 trueWill read input token of size 579 for processing by acceptSecContextKerberos username [root]: admin <---- 提示输入服务端的princal名称 Kerberos password for admin: <--- 提示输入相应的密码Will send token of size 108 from acceptSecContext.Context Established! Client is lch@EXAMPLE.COMServer is admin@EXAMPLE.COMWill read token of size 13Hello Server
2.4.2.2 通过TGT的方式
服务端的配置与2.4.2.1相同,client通过获取ticket的方式来进行认证
服务端的配置文件:
[root@freeipa56 testKerberos]# cat server.conf com.sun.security.jgss.accept { com.sun.security.auth.module.Krb5LoginModule required storeKey=true; };
客户端配置文件:
[lch@freeipa56 testKerberos]$ cat client.conf com.sun.security.jgss.initiate { com.sun.security.auth.module.Krb5LoginModule required useKeyTab=false useTicketCache=true;};
查看当前环境的kerberos信息,
[lch@freeipa56 testKerberos]$ klistTicket cache: FILE:/tmp/krb5cc_500Default principal: lch@EXAMPLE.COMValid starting Expires Service principal11/15/17 11:08:47 11/16/17 11:08:46 krbtgt/EXAMPLE.COM@EXAMPLE.COM
执行客户端命令:
[lch@freeipa56 testKerberos]$ java -classpath kerberosTest.jar -Djava.security.krb5.realm=EXAMPLE.COM -Djava.security.krb5.kdc=freeipa56.example.com -Djavax.security.auth.useSubjectCredsOnly=false -Djava.security.auth.login.config=client.conf com.test.gssapi.sample1.TestClient admin 192.168.1.56 9111 trueConnected to server: /192.168.1.56Will send token of size 579 from initSecContext.Will read input token of size 108 for processing by initSecContextContext Established! Client is lch@EXAMPLE.COMServer is adminWill read token of size 22Receive Client MessageExiting...
客户端程序不需要输入用户名密码(服务端与)
2.4.2.3 服务端通过keytab文件
服务端的配置文件
[root@freeipa56 testKerberos]# cat server.conf com.sun.security.jgss.accept { com.sun.security.auth.module.Krb5LoginModule required useKeyTab=true storeKey=true useTicketCache=false keyTab="/root/admin.headless.keytab" principal="admin@EXAMPLE.COM";};
客户端的配置文件:
[lch@freeipa56 testKerberos]$ cat client.conf com.sun.security.jgss.initiate { com.sun.security.auth.module.Krb5LoginModule required useKeyTab=false useTicketCache=true;};
执行服务端程序与客户端程序就会发现服务端不再需要输入认证信息了。
3. 总结
Kerberos提供多种认证方式,而我们可以通过java.security.auth.login.config对应的配置参数进行认证
- 基于Kerberos认证的TCP通信
- hadoop的kerberos认证
- 基于Windows RDP实现客户端的Kerberos认证
- CDH 的Kerberos认证配置
- CDH 的Kerberos认证配置
- CDH 的Kerberos认证配置
- Hadoop集群的kerberos认证
- 基于串口的TCP通信
- 基于TCP的Socket通信
- 基于TCP的socket通信
- 基于TCP的Socket通信
- Kerberos认证
- Kerberos认证
- Kerberos认证
- Kerberos:面向开放式网络的认证服务
- Kerberos认证问题的调试试验
- Spark连接需Kerberos认证的HBase
- 基于TCP的最简单的通信
- React 更新元素时钟
- Linux内核驱动基础-设备树相关总线使用
- NYOJ 86 找球号(一)(二分)
- 最新版HttpClient工具类
- MySQL-添加列,添加或修改字段的注释
- 基于Kerberos认证的TCP通信
- singleton
- 日期控件使用
- 宏函数和自定义函数的区别
- 图片不拉伸
- Servlet中MultipartRequest实现上传文件
- 创建maven项目,更改jdk编译版本
- 解决编译apache出现的问题:configure: error: APR not found . Please read the documentation
- 服务治理总结