All you need to know about Keytab files

Whether you are currently using them or planning to issue one, here is (I hope) all you need to know about those little binary files.

It's a Kerberos thing

If you use or plan to use keytabs, it means that you are planning to add Kerberos support to a system which can't do it otherwise (Kerberos being the native authentication protocol used in Active Directory since the Windows 2000 family, not the only one though). Windows systems which are joined to an Active Directory domain are automatically set up for Kerberos. There is nothing to do to "enable" Kerberos on Windows. Applications running on Unix/Linux or applications running on Windows but within a Java environment (those could but do not really access Windows APIs) which want to be a part of the forest Single-Sign-On might need a keytab file. The keytab file contains the identity of an Active Directory account (userPrincipalName) as well as the encryption keys necessary to encrypt/decrypt Kerberos tickets.

In other words, you need Keytab files to bring SSO to non domain-joined applications.

How do I generate a Keytab?

You will use the utility KTPASS.EXE. It is a part of the RSAT and will be present if you enabled the Active Directory Directory Services tools (always use the most recent version of ktpass, see the section: Why should I always use the most recent version of ktpass?).

Let's take an example. You have a "service/application" account in the verenatex.com domain with the following information:

  • sAMAccountName: app1
  • userPrincipalName: app1@verenatex.com
  • servicePrincipalName: <not set>
  • "password" (not an actual attribute): Password1

Now I run the following command to generate a keytab file for this account:

 ktpass /out app1.keytab /princ http/app1.verenatex.com@verenatex.com /mapuser app1 /crypto AES256-SHA1 /ptype KRB5_NT_PRINCIPAL /pass Password2 /target vdc01.verenatex.com
  • /out specifies the name of the output file, here app1.keytab
  • /princ specifies the userPrincipalName as well as a value which will be added to the servicePrincipalName of the mapped account
  • /mapuser is the account for which the keytab will be generated
  • /crypto is the encrytion type used for the keytab, here AES256-SHA1
  • /ptype is set to KRB5_NT_PRINCIPAL which is in general what you should use unless instructed otherwise by the application documentation
  • /pass is the new password for the mapped account (by default it will perform a password reset operation)
  • /target is used to indicate which domain controller will be queried (it is optional, but can be useful if you have more than one domain in your forest)

And here is the output:

 Successfully mapped http/app1.verenatex.com to app1.
Password successfully set!
Key created.
Output keytab to app1.keytab:
Keytab version: 0x502
keysize 90 http/app1.verenatex.com@verenatex.com ptype 1 (KRB5_NT_PRINCIPAL)

And this is what the account looks like now:

  • sAMAccountName: app1
  • userPrincipalName: http/app1.verenatex.com@verenatex.com
  • servicePrincipalName: http/app1.verenatex.com@verenatex.com
  • "password" (not an actual attribute): Password2

Note that the userPrincipalName has been updated with the value you provided in the parameter /princ. You can prevent this with the parameter -SetUPN at the end of the command. In that case, the UPN will not be touched and the value specified with /princ will only be added to the servicePrincipalName (SPN).

Also note that if the account already had a SPN, the new value will be added to the list of SPNs (it is a multi-value attribute). Unless you specify the parameter /mapop set. In that case the previous values are overwritten by the new value.

There are several things to consider for the password. You can specify it and set it during the creation of the keytab, or if you already know the password, you can just specify it in the command line. Then it will be used to generate the key but the password will not be reset. Or you can even ask for a random password. Because after all, if the keytab is solely used for an application or a service, we don't really enter the password anywhere, why not a random password?

I will share these examples and their final results with highlighted differences, so you can understand what those commands do and pick the one you need:

Command line Object Before Object After
ktpass/out app1.keytab/princ http/app1.verenatex.com@verenatex.com/mapuser app1/crypto AES256-SHA1/ptype KRB5_NT_PRINCIPAL/pass Password2/target vdc01.verenatex.com sAMAccountName: app1userPrincipalName: app1@verenatex.comservicePrincipalName: <not set>"password": Password1 sAMAccountName: app1userPrincipalName: http/app1.verenatex.com@verenatex.com servicePrincipalName: http/app1.verenatex.com@verenatex.com "password": Password2
ktpassktpass/out app1.keytab/princ http/app1.verenatex.com@verenatex.com-SetUPN/mapuser app1/crypto AES256-SHA1/ptype KRB5_NT_PRINCIPAL/pass Password2/target vdc01.verenatex.com sAMAccountName: app1userPrincipalName: app1@verenatex.comservicePrincipalName: <not set>"password": Password1 sAMAccountName: app1userPrincipalName: app1@verenatex.comservicePrincipalName: http/app1.verenatex.com@verenatex.com "password": Password2
ktpass/out app1.keytab/princ http/app1.verenatex.com@verenatex.com/mapuser app1/crypto AES256-SHA1/ptype KRB5_NT_PRINCIPAL/pass Password2/target vdc01.verenatex.com sAMAccountName: app1userPrincipalName: app1@verenatex.comservicePrincipalName: http/test"password": Password1 sAMAccountName: app1userPrincipalName: http/app1.verenatex.com@verenatex.com servicePrincipalName: http/test, http/app1.verenatex.com@verenatex.com "password": Password2
ktpass/out app1.keytab/princ http/app1.verenatex.com@verenatex.com/mapuser app1/mapop set/crypto AES256-SHA1/ptype KRB5_NT_PRINCIPAL/pass Password2/target vdc01.verenatex.com sAMAccountName: app1userPrincipalName: app1@verenatex.comservicePrincipalName: http/test"password": Password1 sAMAccountName: app1userPrincipalName: http/app1.verenatex.com@verenatex.com servicePrincipalName: http/app1.verenatex.com@verenatex.com "password": Password2
ktpass/out app1.keytab/princ http/app1.verenatex.com@verenatex.com/mapuser app1/crypto AES256-SHA1/ptype KRB5_NT_PRINCIPAL/pass Password1-SetPass/target vdc01.verenatex.com sAMAccountName: app1userPrincipalName: app1@verenatex.comservicePrincipalName: <not set>"password": Password1 sAMAccountName: app1userPrincipalName: http/app1.verenatex.com@verenatex.com servicePrincipalName: http/app1.verenatex.com@verenatex.com"password": Password1
ktpass/out app1.keytab/princ http/app1.verenatex.com@verenatex.com/mapuser app1/crypto AES256-SHA1/ptype KRB5_NT_PRINCIPAL+RdnPass +maxPass 45/target vdc01.verenatex.com sAMAccountName: app1userPrincipalName: app1@verenatex.comservicePrincipalName: <not set>"password": Password1 sAMAccountName: app1userPrincipalName: http/app1.verenatex.com@verenatex.com servicePrincipalName: http/app1.verenatex.com@verenatex.com"password": mN=q!{/YBz95v1^doS2=QK#|lRFG

I know that those parameters -SetUPN or -SetPass look counter intuitive since they do the opposite of what it suggests. But this is because the "-" in the front means the negation of the parameter. The default value for those two are actually +SetUPN and +SetPass. Hope that clears the confusion.

The account in AD doesn't have to be a user account by the way. It could also be a computer account. Although I don't really see why you'd go that way. The main difference that comes to my mind is that the password expiration policy does not apply to computer accounts. But that's not very different from a user account with the password set not to expire.

It is not encrypted!

This is what a keytab file looks like if you open it with notepad:

We can see the principal name specified during the creation and a bit of gibberish. Looks like encrypted stuff BUT IT IS NOT. This is the plain text (well plain bytes) of the Kerberos encryption key. This is what it looks like if you parse the binary file in PowerShell (I used a self-written parser) :

It's the output of a small code I wrote that parses the content according to the documented structure. There is no decryption involved since it is in the clear. And because the encryption type in this case is rc4-hmac (see: RFC4757 The RC4-HMAC Kerberos Encryption Types Used by Microsoft Windows), the key that you see here in the clear is the actual NTHash of the user (which is way enough to impersonate the user by performing pass the hash or even to try some Kerberos roasting). If you've used AES encryption, it would be the AES key used by Windows to encrypt/decrypt Kerberos tickets. 

So, it seems pretty obvious that you will have to deal with Keytab files with the same precautions you use when dealing with passwords.

  1. Do not share them over not encrypted emails
  2. Do not store them in a file share
  3. Once deployed on the application, make sure the file is also protected from other users (chmod, or ACL for Java on Windows) and follow credentials hygiene principals (ie. making sure that only the actual admins of the server can access that server's file system)

If you have some data classification project going on in your organization, it is maybe the occasion to also clarify the level of confidentiality required by keytab files in your policy.

What permissions do I need to generate a keytab?

First, I'll emphasize on the following: you don't need to be connected locally on a domain controller to generate a Keytab file. I see this so often that I had to add it. To generate a Keytab you might need a few permissions on the directory but the "Allow logon locally" privilege on the domain controllers is not one of them. As a matter of fact the only permission required to generate a keytab file is the NTFS "write" permission on the folder where the file will be written (specified by the /out parameter). And you can do that from your workstation or from an administration server as long as you have the right feature installed (RSAT with ADDS tools to use ktpass). That's it.

Now, depending on the parameter you chose in the ktpass command line, you might need more, but that's your choice, not because it is a requirement.

Here are a few things which could take place behind the scenes when you generate a Keytab file:

  • The password of of the account for which the keytab file will be generated will be reset with the a provided value. Unless specified otherwise with the parameter -SetPass.
  • If the userPrincipalName of the account in Active Directory is different than the one specified in the command line, then it will be updated. Unless you set the parameter -SetUPN.
  • If the mapped account does not have a servicePrincipalName with the value specified in the /princ parameter, a servicePrincipalName with the value equal to the value of the /princ paramter will be added to the account (added or replaced if the parameter "/modop set" is also specified). If you don't have the permissions, it will display a warning but the keytab will still be generated.
  • If you want the account to be a DES encryption only account, you will need the permission to change the userAccountControl attribute (BUT, you don't want that, YOU DO NOT WANT DES, see the section What Encryption Types should I pick?)

I summarize the permissions you need on the object depending on the parameter you specify on the ktpass command:

Parameter Permissions on the account
/princ If the value is already set with the same value: noneElse permission to modify the userPrincipalNamePermission to modify the servicePrincipalName
/princ -SetUPN None
/pass Permission to reset the password
/pass -SetPass None

So if you already know the password of the account, do not wish to modify the userPrincipalName and that the servicePrincipalName is already set as you need, you can generate the keytab file for an account with no permissions at all on Active Directory:

 ktpass /out app1.keytab /princ app1@verenatex.com -SetUPN /mapuser app1 /crypto AES256-SHA1 /ptype KRB5_NT_PRINCIPAL /pass Password1 -SetPass /target vdc01.verenatex.com

You will receive this message: "WARNING: Unable to set SPN mapping data." that you can safely ignore if the SPN was already set on the account.

So, can I delegate the creation of keytabs? Well, as you can see just above, in certain conditions, you don't even need to delegate anything. If the UPN and SPN are not changing and if the password is known, then you really have nothing to do from a delegation perspective. You just need to use the ktpass tool with the right parameters. What if you want to delegate the creation because the conditions mentioned are not met? It depends. If you already have a team in your organization in charge of managing "service/application" accounts, you can ask them to do it. But very often the team in charge of managing those accounts is the AD team anyways. And if you'd like to extend this to another team, you need to understand that giving the permissions to a non AD savvy person to create/modify/delete servicePrincipalName is a bad idea. Not to mention changing the userPrincipalName. There are security implications to these decisions (account spoofing, incorrect or duplicate SPN...).
You can cut the work in two, and let the team already in charge of "service/application" accounts do the modification of the UPN and SPN (if required), and leave the team asking for the keytab the duty to generate it assuming that they know the password. Then you don't have the challenge of sharing the generated  keytab with the asking team, since they would already have it.
You can also keep the generation of the keytab in the AD team (or again the team within your organization already in charge of "service/application" accounts) then you keep control of everything (including the encryption type, which is important). And you also have a clear trace of what keytabs have been generated (this can be useful, see the next section: When do I need to regenerate the keytab). But there is the issue of sending the keytab back to the asking team...

When do I need to regenerate the keytab?

There are only a few events on an account that will invalidate a keytab and require the file to be regenerated.

If you change the principal names you might have to change it. "Might" because it depends on how the client asks for a ticket and what the application is actually doing with the keytab file. But because there are some aspects of it which you do not control (you don't know how the applications or systems parsed the keytab nor how they planned to use it), if you changed the userPrincipalName, you will certainly have to regenerate the keytab.

If you want to change the encryption type, you will also have to regenerate the keytab file with the right /crypto parameter (see the section What Encryption Type should I pick? for more details on that).

If you change the password, it is obvious that the encryption keys (derived from the password) will change. Hence you will have to regenerate the keytab file. What could be a bit more obscure is that you also have to regenerate the keytab even if you reset the password of the account with the same value! Why? Because of the Vno (also called KVno for Key Version Number). Not only do we use the password to generate the encryption keys, we also store the version of the password in the keytab file. So if the password changed 10 times on the account before generating the keytab, and you do not specify -SetPass parameter, the keytab you generate now will be good for the version 11 of the password. The Key version is a part of the Kerberos ticket generated by the domain controllers and sent by the user. So if it is not matching what is on the keytab, even if the value of the password is the same, then the ticket will be discarded. If you want to know what is the current version number of the key, you can query the calculated attribute: msDS-KeyVersionNumber. You can find it on the Attribute Editor of the Users and Computer console:

Or in PowerShell (note that because it is a calculated attribute, if you were to perform a search the scope has to be "base" and the attribute has to be explicitly asked):

 Get-ADUser -Identity app1 -Properties "msDS-KeyVersionNumber"

The output will be:

 DistinguishedName : CN=app1,OU=Accounts,DC=verenatex,DC=com
... 
msDS-KeyVersionNumber : 12
UserPrincipalName : app1@verenatex.com
...

And this value really represents how many times the password attribute has changed since the account exists . You can also see it that way, by looking at the replication metadata of the account (if you want to play with metadata, have a look here):

 Get-ADUser -Identity app1 | Get-ADReplicationAttributeMetadata -Properties unicodePwd -Server vdc01.verenatex.com

The output will be:

 AttributeName : unicodePwd
...
Version : 12
...

This also explains why, if the account is authoritatively restored, keytab files also have to be regenerated. To ensure the restored objects will win all replication conflicts, the attribute version of all the attributes of the objects in the scope of an authoritative restore will by default be incremented by 100.000. Then in my example, if you restore the account app1, the new msDS-KeyVersionNumber value will be 100.012 and the keytab already generated would have an incorrect Vno. Unless! (yes it can get complicated), you want all tickets to be generated with the Vno 1 (like Windows 2000 Server domain controllers used to do). Then there is a way, see the dsHeuristics documentation here. My recommendation: ensure that you include a section about keytabs in your Disaster Recovery document. If you recover the object from the AD Recycle Bin, there is nothing to do, this will restore the version as it was before being deleted.

If you want to know what the key version of your keytab is and you did not save the output of the ktpass command, have a look at the next section: I found a keyab, what should I do?

I found a keytab, what should I do?

If you "found" a keytab in an open and unexpected location, you will have to assume it is compromised and change the keys it contains. In other words, you'll have to reset the password of the account and regenerate the keytab. But wait, how do I know what account the keytab has been generated for? You can simply use ktpass with the /in parameter:

 ktpass /in test.keytab

This will be the output telling you what is the principal and the encryption key. It even tells you what the version of the key (vno) was:

 Existing keytab:

Keytab version: 0x502
keysize 61 test@verenatex.com ptype 1 (KRB5_NT_PRINCIPAL) vno 24 etype 0x17 (RC4-HMAC) keylength 16 (0xfdbbac01d5608b5d8ae1adbde5ddb459)

WARNING: No principal name specified.

You can ignore the warning. Technically a keytab can contain several keys and principals (although the Windows version of ktpass doesn't offer the capability to have multiple principals), if it was the case you can ask ktpass to show only the key material of a specific principal by specifying /princ too.

What Encryption Type should I pick?

This one is a bit tricky because the good practices sometimes go against what the application can support. The encryption type algorithm has to be supported by the system (or Java framework) the application is running on and by the domain controllers. Since the Windows Server 2008 domain functional level (DFL), and assuming that the password of the account has been set after the DFL has been raised, Active Directory can and will do AES. And that will be the recommendation, to pick the AES256-SHA1. But what if the application cannot? Well, you will have to pick another one... And maybe the old school one (RC4-HMAC-NT, used by legacy Windows operating systems).

You should not use any DES based encryptions. The IETF is pretty clear for awhile now, see section Security Considerations on the Kerberos RFC3061. DES encryption for Kerberos is even disabled by default since Windows Server 2008 domain controllers.

More on encryption types here: Windows Configurations for Kerberos Supported Encryption Type.

So pick the highest the application can support. What if the application supports only DES based encryption? Well, then it is time to update the application. Why would you lower the security of your entire domain just to accommodate an application which clearly lives in the past?

Do not opt for /crypto all but rather for /crypto AES256-SHA1.

Now, let's say you wish to just change the crypto. Then you do not need any permission assuming that you already know the password. Just proceed with:

 ktpass /out app1.keytab /princ app1@verenatex.com -SetUPN /mapuser app1 /crypto AES256-SHA1 /ptype KRB5_NT_PRINCIPAL /pass Password1 -SetPass /target vdc01.verenatex.com

As long as you specify the right password, the keytab will contain the right keys.

Why should I always use the most recent version of ktpass?

The latest version on ktpass has always more parameters than the old ones. You can use the ktpass locally on the Windows Server 2016 server (or RSAT of Windows 10) even if the domain controllers are still running Windows Server 2008 R2. Also, some versions of ktpass have different default behavior (different default parameters) to avoid surprises, always use the most recent one available. Wait, there's even better... If you use ktpass from the Windows Server 2003 Support Tools SP2, there is quite a weird a bug. When you use that version, and if LDAPs is enabled in your environment, the tool will incorrectly set the attribute userPassword of the account with the actual cleartext password, available for every single authenticated user to read (lol?). What is the userPassword attribute? It is mostly there for LDAP compliance, but it is not used by Windows for logon (unless you enable it, which I have never seen anywhere). So make sure you (or your one of your predecessors) have not done this in the past by running an LDAP query with the following filter:

 Get-ADObject -LDAPFilter "(&(servicePrincipalName=*)(userPassword=*))"

If you have something in the output, and if it is an actual password, then delete the value for account and reset the password and regenerate the keytab file. And speaking on bugs, the current documentation is also a bit weird (will be update soon, I hope)... I'd say if you want a more accurate version of the help file, you'll have to stick to the good old: ktpass /?.

More questions about keytabs?

Please send them in the comments section, I'll add them up here!