You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 2 Next »

JIRA gives you the choice of storing user records internally, or delegating to an external 'User Directory', like Active Directory, LDAP or Atlassian Crowd.

Many smaller orgs start off with internal user records, but later want to migrate users to LDAP, for ease of management, or to allow authentication with non-Atlassian LDAP-aware systems.

Generating LDAP (LDIF) records from JIRA's cwd_* tables is not hard, but how about password hashes?

On this page we'll describe how to convert JIRA credential hashes:

{PKCS5S2}U48fu6LonjKCk0VmHPsgLrKf1/i1o/wxLXblOTa6P8eXvvJTU4iRb0fpRlO3xA0J

into a format understandable by OpenLDAP (with the  pw-pbkdf2  module loaded):

{PBKDF2}10000$cjsPF6FcSW9CDwmpREtZog$qWi06T.6SSapuTtDsFn/2DPacsc

This will let you migrate user records from Jira into LDAP without forcing everyone to reset their password.


Those familiar with JIRA's database will know about the cwd_user  table, where JIRA stores user data:

redradish_jira=> select * from cwd_user where user_name='jturner';
┌─[ RECORD 1 ]────────┬───────────────────────────────────────────────────────────────────────────┐
│ id                  │ 10000                                                                     │
│ directory_id        │ 1                                                                         │
│ user_name           │ jturner                                                                   │
│ lower_user_name     │ jturner                                                                   │
│ active              │ 1                                                                         │
│ created_date        │ 2013-09-02 18:14:34.078712+10                                             │
│ updated_date        │ 2018-02-23 10:33:48.481+11                                                │
│ first_name          │ Jeff                                                                      │
│ lower_first_name    │ jeff                                                                      │
│ last_name           │ Turner                                                                    │
│ lower_last_name     │ turner                                                                    │
│ display_name        │ Jeff Turner                                                               │
│ lower_display_name  │ jeff turner                                                               │
│ email_address       │ jeff@redradishtech.com                                                    │
│ lower_email_address │ jeff@redradishtech.com                                                    │
│ credential          │ {PKCS5S2}U48fu6LonjKCk0VmHPsgLrKf1/i1o/wxLXblOTa6P8eXvvJTU4iRb0fpRlO3xA0J │
│ deleted_externally  │ ␀                                                                         │
│ external_id         │ a330dede-18f8-4745-ac8d-d2ec2bcabedc                                      │
└─────────────────────┴───────────────────────────────────────────────────────────────────────────┘


Atlassian's PKCS5S2 format

What exactly is that PKCS5S2 format JIRA uses for password hashes?

'PKCS5S2' refers to "PKCS #5: Password-Based Cryptography Specification Version 2.0", a document available in RFC form which provides "recommendations for the implementation of password-based cryptography" . The recommendations include the use of the PBKDF2 'key derivation function', of which HMAC-SHA-1 is an example. Apparently.

Anyhow, the format is succinctly explained in the passlib.hash.atlassian_pbkdf2_sha1  Python library's docs:

  • generates a random 16-byte salt
  • feeds the salt plus password into our PBKDF2 function, which applies a hash (HMAC-SHA1) 10,000 times, yielding a a 32-byte hash
  • concatenates salt and hash, and base64-encodes them

(This Python library can be used from the command-line - see Resetting a user password in the database)

If you have a commercial Jira license, you can also download the source at https://my.atlassian.com and take a look (unpack dependencySources/atlassian-password-encoder-*-sources.jar and look at DefaultPasswordEncoder and PKCS5S2PasswordHashGenerator).

So, easy enough. Let's unpack our sample password:

$ credential='{PKCS5S2}U48fu6LonjKCk0VmHPsgLrKf1/i1o/wxLXblOTa6P8eXvvJTU4iRb0fpRlO3xA0J'
$ credential="${credential#'{PKCS5S2}'}"							# Chop off the identifier
$ echo $credential
U48fu6LonjKCk0VmHPsgLrKf1/i1o/wxLXblOTa6P8eXvvJTU4iRb0fpRlO3xA0J
$ echo -n "$credential" | base64 -d | xxd
00000000: 538f 1fbb a2e8 9e32 8293 4566 1cfb 202e  S......2..Ef.. .
00000010: b29f d7f8 b5a3 fc31 2d76 e539 36ba 3fc7  .......1-v.96.?.
00000020: 97be f253 5388 916f 47e9 4653 b7c4 0d09  ...SS..oG.FS....

The first 16 bytes is our salt, and the remainder is our hash:

$ salt="$(echo -n "$credential" | base64 -d | head -c16)"
$ hash="$(echo -n "$credential" | base64 -d | tail -c32)"

OpenLDAP's

OpenLDAP supports PBKDF2. The problem is that its format is different:

{PBKDF2}<Iteration>$<Adapted Base64 Salt>$<Adapted Base64 DK

We know the iteration count (10000). We know the salt, and we know the hash (derived key). We just need to reorder the elements.

"Adapted Base64" is, per the passlib docs,

  • No labels