校验和失败:Kerberos/Spring/Active Directory (2008) [英] checksum failed: Kerberos / Spring / Active Directory (2008)

查看:25
本文介绍了校验和失败:Kerberos/Spring/Active Directory (2008)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们在让 Kerberos/AD 身份验证与 Spring Web 应用程序一起使用时遇到问题,我认为问题与 Kerberos 票证的加密类型和 Active Directory 域功能级别有关.

We are having trouble getting Kerberos/AD authentication to work with a Spring webapp, and I believe the problem has to do with encryption types for the Kerberos tickets and the Active Directory domain functional level.

基本设置是:

我有一个环境,其中 Active Directory 域功能级别是 Windows Server 2003 并且一切正常,如果客户端登录到域,则客户端按预期进行身份验证.使用kerbtray检查这个环境下的ticket,可以看到它们都有ticket加密类型和key加密类型RSADSI RC4-HMAC".

I have one environment where the Active Directory domain functional level is Windows Server 2003 and everything works fine, with clients authenticating as expected if they are logged on to the domain. Using kerbtray to examine the tickets in this environment I can see that they all have both ticket encryption type and key encryption type "RSADSI RC4-HMAC".

我有一个功能级别为 Windows Server 2008 的新域,这是身份验证不起作用的地方.尝试验证票证时返回的应用程序错误是:

I have a new domain with functional level Windows Server 2008, and this is where the authentication does not work. The application error returned when attempting to validate the ticket is:

Kerberos validation not successful...

Caused by: GSSException: Failure unspecified at GSS-API level (Mechanism level: Checksum failed)
    at sun.security.jgss.krb5.Krb5Context.acceptSecContext(Unknown Source)
    at sun.security.jgss.GSSContextImpl.acceptSecContext(Unknown Source)
    at sun.security.jgss.GSSContextImpl.acceptSecContext(Unknown Source)
    at sun.security.jgss.spnego.SpNegoContext.GSS_acceptSecContext(Unknown Source)
    at sun.security.jgss.spnego.SpNegoContext.acceptSecContext(Unknown Source)
    at sun.security.jgss.GSSContextImpl.acceptSecContext(Unknown Source)
    at sun.security.jgss.GSSContextImpl.acceptSecContext(Unknown Source)
    at org.springframework.security.extensions.kerberos.SunJaasKerberosTicketValidator$KerberosValidateAction.run(SunJaasKerberosTicketValidator.java:146)
    at org.springframework.security.extensions.kerberos.SunJaasKerberosTicketValidator$KerberosValidateAction.run(SunJaasKerberosTicketValidator.java:136)
    ... 34 more
Caused by: KrbException: Checksum failed
    at sun.security.krb5.internal.crypto.ArcFourHmacEType.decrypt(Unknown Source)
    at sun.security.krb5.internal.crypto.ArcFourHmacEType.decrypt(Unknown Source)
    at sun.security.krb5.EncryptedData.decrypt(Unknown Source)
    at sun.security.krb5.KrbApReq.authenticate(Unknown Source)
    at sun.security.krb5.KrbApReq.<init>(Unknown Source)
    at sun.security.jgss.krb5.InitSecContextToken.<init>(Unknown Source)
    ... 43 more
Caused by: java.security.GeneralSecurityException: Checksum failed
    at sun.security.krb5.internal.crypto.dk.ArcFourCrypto.decrypt(Unknown Source)
    at sun.security.krb5.internal.crypto.ArcFourHmac.decrypt(Unknown Source)

堆栈跟踪显示ArcfourCrypto.decrypt",因此大概是将 Kerberos 票证视为 RC4-HMAC.这次再次使用 kerbtray 检查票证,客户端上有 2 张票证用于域:krbtgt/.COM.两个票证的密钥加密类型为RSADS1 RC4-HMAC,一个票证加密类型也有这个,而另一个票证加密类型为Kerberos AES256-CTS-HMAC-SHA1-96".

The stack trace shows "ArcfourCrypto.decrypt" so presumably is treating the Kerberos ticket as RC4-HMAC. Using kerbtray again to examine the tickets this time there are 2 tickets on the client for the domain: krbtgt/.COM. Both of tickets have key encryption type RSADS1 RC4-HMAC, one also has this for ticket encryption type, but the other has "Kerberos AES256-CTS-HMAC-SHA1-96".

我不确定这是否是问题的原因,但这是我在这两种环境中能够找到的唯一可以解释身份验证异常的差异.我尝试过更改 AD 加密策略,尝试过 IE 和 Firefox,以及我能想到的几乎所有其他方法,但都没有奏效.

I don't know for sure that this is the cause of the problem, but it is the only difference I've been able to find in the two environments that might explain the authentication exception. I've tried changing the AD encryption policy, tried IE and Firefox, and pretty much everything else I could think of, but nothing has worked.

任何解决此问题的帮助将不胜感激.我更愿意在 java 端修复它,因为我可能无法对生产 AD 设置规定太多.

Any help addressing this would be much appreciated. I'd prefer to fix it on the java end as I probably can't dictate too much about the production AD setup.

推荐答案

问题似乎出在 keytab 上.有一些动作序列会导致一些特定的密钥表文件状态:(A) keytab 适用于 Java,但不适用于 k5start/kinit;(B) keytab 不适用于 Java,但适用于 k5start/kinit;(C) keytab 适用于它们.

The problem seems to be in the keytab. There are some action sequences leading to some specific keytab file states: (A) keytab works with Java but does not work with k5start/kinit; (B) keytab does not work with Java, but works with k5start/kinit; (C) keytab works with both them.

允许检查 Java 是否可以使用密钥表文件进行身份验证的简短 Java 代码:

The short Java code that allows to check if Java can authenticate using the keytab file:

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import javax.security.auth.Subject;

import com.sun.security.auth.module.Krb5LoginModule;

/**
 * This is simple Java program that tests ability to authenticate 
 * with Kerberos using the JDK implementation.
 * 
 * The program uses no libraries but JDK itself.
 */
public class Krb {

  private void loginImpl(final String propertiesFileName) throws Exception {
    System.out.println("NB: system property to specify the krb5 config: [java.security.krb5.conf]");
    //System.setProperty("java.security.krb5.conf", "/etc/krb5.conf");

    System.out.println(System.getProperty("java.version"));

    System.setProperty("sun.security.krb5.debug", "true");

    final Subject subject = new Subject();

    final Krb5LoginModule krb5LoginModule = new Krb5LoginModule();
    final Map<String,String> optionMap = new HashMap<String,String>();

    if (propertiesFileName == null) {
      //optionMap.put("ticketCache", "/tmp/krb5cc_1000");
      optionMap.put("keyTab", "/etc/krb5.keytab");
      optionMap.put("principal", "foo"); // default realm

      optionMap.put("doNotPrompt", "true");
      optionMap.put("refreshKrb5Config", "true");
      optionMap.put("useTicketCache", "true");
      optionMap.put("renewTGT", "true");
      optionMap.put("useKeyTab", "true");
      optionMap.put("storeKey", "true");
      optionMap.put("isInitiator", "true");
    } else {
      File f = new File(propertiesFileName);
      System.out.println("======= loading property file ["+f.getAbsolutePath()+"]");
      Properties p = new Properties();
      InputStream is = new FileInputStream(f);
      try {
        p.load(is);
      } finally {
        is.close();
      }
      optionMap.putAll((Map)p);
    }
    optionMap.put("debug", "true"); // switch on debug of the Java implementation

    krb5LoginModule.initialize(subject, null, new HashMap<String,String>(), optionMap);

    boolean loginOk = krb5LoginModule.login();
    System.out.println("======= login:  " + loginOk);

    boolean commitOk = krb5LoginModule.commit();
    System.out.println("======= commit: " + commitOk);

    System.out.println("======= Subject: " + subject);
  }

  public static void main(String[] args) throws Exception {
    System.out.println("A property file with the login context can be specified as the 1st and the only paramater.");
    final Krb krb = new Krb();
    krb.loginImpl(args.length == 0 ? null : args[0]);
  }
}

,以及要使用的属性文件:

, and the property file to use:

#ticketCache=/tmp/krb5cc_1000
keyTab=/etc/krb5.keytab
principal=foo

doNotPrompt=true
refreshKrb5Config=true
useTicketCache=true
renewTGT=true
useKeyTab=true
storeKey=true
isInitiator=true

(下面我们假设krb/kdc已经正确安装配置,用kdb5_util创建数据库.每个命令序列的起始状态是:keytab文件删除,token缓存删除,用户foo"从数据库.)

(Below we assume that krb/kdc is correctly installed and configured, the database is created with kdb5_util. The starting state of each command sequence is: the keytab file deleted, token cache is deleted, user "foo" is deleted from the database.)

以下操作序列导致密钥表状态 (A):

The following action sequence leads to the keytab state (A):

$ echo -e "foo
foo" | kadmin.local -q "addprinc foo"
$ echo -e "foo
foo" | kadmin.local -q "ktadd foo"
$ java -cp . Krb ./krb5.properties 
# Now java auth okay, but the following command fails:
$ k5start foo
Kerberos initialization for foo@EXAMPLE.COM
Password for foo@EXAMPLE.COM: 
k5start: error getting credentials: Decrypt integrity check failed
$

<小时>

以下操作序列导致密钥表状态 (B):


The following action sequence leads to the keytab state (B):

$ echo -e "foo
foo" | kadmin.local -q "addprinc foo"
$ echo -e "foo
foo" | kadmin.local -q "ktadd foo"
$ echo -e "foo
foo" | kadmin.local -q "cpw foo"
$ java -cp . Krb ./krb5.properties 
A property file with the login context can be specified as the 1st and the only paramater.
NB: system property to specify the krb5 config: [java.security.krb5.conf]
1.6.0_33
======= loading property file [/tmp/krb-test/yhadoop-common/./krb5.properties]
Debug is  true storeKey true useTicketCache true useKeyTab true doNotPrompt true ticketCache is null isInitiator true KeyTab is /etc/krb5.keytab refreshKrb5Config is true principal is foo tryFirstPass is false useFirstPass is false storePass is false clearPass is false
Refreshing Kerberos configuration
Config name: /etc/krb5.conf
>>> KdcAccessibility: reset
>>> KdcAccessibility: reset
Acquire TGT from Cache
>>>KinitOptions cache name is /tmp/krb5cc_0
Principal is foo@EXAMPLE.COM
null credentials from Ticket Cache
>>> KeyTabInputStream, readName(): EXAMPLE.COM
>>> KeyTabInputStream, readName(): foo
>>> KeyTab: load() entry length: 49; type: 23
Added key: 23version: 3
Ordering keys wrt default_tkt_enctypes list
default etypes for default_tkt_enctypes: 23.
0: EncryptionKey: keyType=23 kvno=3 keyValue (hex dump)=
0000: 5F 7F 9B 42 BB 02 51 81   32 05 1D 7B C0 9F 19 C0  _..B..Q.2.......


principal's key obtained from the keytab
Acquire TGT using AS Exchange
default etypes for default_tkt_enctypes: 23.
>>> KrbAsReq calling createMessage
>>> KrbAsReq in createMessage
>>> KrbKdcReq send: kdc=localhost UDP:88, timeout=30000, number of retries =3, #bytes=128
>>> KDCCommunication: kdc=localhost UDP:88, timeout=30000,Attempt =1, #bytes=128
>>> KrbKdcReq send: #bytes read=611
>>> KrbKdcReq send: #bytes read=611
>>> KdcAccessibility: remove localhost:88
>>> EType: sun.security.krb5.internal.crypto.ArcFourHmacEType
Checksum failed !
                [Krb5LoginModule] authentication failed 
Checksum failed
Exception in thread "main" javax.security.auth.login.LoginException: Checksum failed
        at com.sun.security.auth.module.Krb5LoginModule.attemptAuthentication(Krb5LoginModule.java:696)
        at com.sun.security.auth.module.Krb5LoginModule.login(Krb5LoginModule.java:542)
        at Krb.loginImpl(Krb.java:65)
        at Krb.main(Krb.java:77)
Caused by: KrbException: Checksum failed
        at sun.security.krb5.internal.crypto.ArcFourHmacEType.decrypt(ArcFourHmacEType.java:85)
        at sun.security.krb5.internal.crypto.ArcFourHmacEType.decrypt(ArcFourHmacEType.java:77)
        at sun.security.krb5.EncryptedData.decrypt(EncryptedData.java:168)
        at sun.security.krb5.KrbAsRep.<init>(KrbAsRep.java:87)
        at sun.security.krb5.KrbAsReq.getReply(KrbAsReq.java:446)
        at sun.security.krb5.Credentials.sendASRequest(Credentials.java:401)
        at sun.security.krb5.Credentials.acquireTGT(Credentials.java:350)
        at com.sun.security.auth.module.Krb5LoginModule.attemptAuthentication(Krb5LoginModule.java:672)
        ... 3 more
Caused by: java.security.GeneralSecurityException: Checksum failed
        at sun.security.krb5.internal.crypto.dk.ArcFourCrypto.decrypt(ArcFourCrypto.java:388)
        at sun.security.krb5.internal.crypto.ArcFourHmac.decrypt(ArcFourHmac.java:74)
        at sun.security.krb5.internal.crypto.ArcFourHmacEType.decrypt(ArcFourHmacEType.java:83)
        ... 10 more
$

但是k5start foo"在这种状态下是可以的,以及kinit foo".

But the "k5start foo" is okay in this state, as well as "kinit foo".

并且以下动作序列导致状态(C):

And the following action sequence leads to state (C):

$ echo -e "foo
foo" | kadmin.local -q "addprinc foo"
$ ktutil 
ktutil:  addent -password -p foo -k 1 -e rc4-hmac
Password for foo@EXAMPLE.COM: 
ktutil:  wkt /etc/krb5.keytab
ktutil:  q

之后 k5start/kinit 和 java 验证都给出了肯定的结果.

after that both k5start/kinit and the java verification give positive result.

环境:

yum list krb5-appl-servers krb5-libs krb5-server krb5-workstation kstart pam_krb5 
...
Installed Packages
krb5-libs.x86_64                                                                            1.9-33.el6_3.3                                                                      @updates
krb5-server.x86_64                                                                          1.9-33.el6_3.3                                                                      @updates
krb5-workstation.x86_64                                                                     1.9-33.el6_3.3                                                                      @updates
kstart.x86_64                                                                               4.1-2.el6                                                                           @epel   
...
$ cat /etc/redhat-release 
CentOS release 6.3 (Final)
$ java -version
java version "1.6.0_33"
Java(TM) SE Runtime Environment (build 1.6.0_33-b03)
Java HotSpot(TM) 64-Bit Server VM (build 20.8-b03, mixed mode)

在 Java 7 中也观察到相同的行为.在使用 MIT 的 kerberos 5-1.10.3 从源代码分发版编译的 Ubuntu precision (12.04.1 LTS) 上也观察到了相同的行为.

Also the same behavior observed with Java 7. Also the same behavior was observed on Ubuntu precise (12.04.1 LTS) with MIT's kerberos 5-1.10.3 compiled from the source distribution.

这篇关于校验和失败:Kerberos/Spring/Active Directory (2008)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆