diff --git a/_test/tests/inc/PassHash.test.php b/_test/tests/inc/PassHash.test.php
index 1d34aa696804b8100dbdf5e1b38489c993839eb7..1bc4b95bceb7ea31dc3168e74b13fadb7eb57d95 100644
--- a/_test/tests/inc/PassHash.test.php
+++ b/_test/tests/inc/PassHash.test.php
@@ -17,6 +17,24 @@ class PassHash_test extends DokuWikiTest {
         $this->assertEquals('fbdb1d1b18aa6c08324b7d64b71fb76370690e1d', PassHash::hmac('sha1','',''));
         $this->assertEquals('80070713463e7749b90c2dc24911e275', PassHash::hmac('md5','The quick brown fox jumps over the lazy dog','key'));
         $this->assertEquals('de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9', PassHash::hmac('sha1','The quick brown fox jumps over the lazy dog','key'));
+    }
+
+    function test_djangopbkdf2() {
+        if(!function_exists('hash_pbkdf2') || !in_array('sha256', hash_algos())){
+            $this->markTestSkipped('missing hash functions for djangopbkdf2 password tests');
+            return;
+        }
 
+        $ph = new PassHash();
+        $knownpasses = array (
+            'pbkdf2_sha256$24000$LakQQ2OOTO1v$dmUgz8V7zcpaoBSA3MV76J5a4rzrszF0NpxGx6HRBbE=',
+            'pbkdf2_sha256$24000$PXogIZpE4gaK$F/P/L5SRrbb6taOGEr4w6DhxjMzNAj1jEWTPyAUn8WU=',
+            'pbkdf2_sha256$24000$vtn5APnhirmB$/jzJXYvm78X8/FCOMhGUmcCy0iWhtk0L1hcBWN1AYZc=',
+            'pbkdf2_sha256$24000$meyCtGKrS5Ai$vkMfMzB/yGFKplmXujgtfl3OGR27AwOQmP+YeRP6lbw=',
+            'pbkdf2_sha256$24000$M8ecC8zfqLmJ$l6cIa/Od+m56VMm9hJbdPNhTXZykPVbUGGTPx7/VRE4=',
+        );
+        foreach($knownpasses as $known) {
+            $this->assertTrue($ph->verify_hash('P4zzW0rd!', $known));
+        }
     }
-}
\ No newline at end of file
+}
diff --git a/_test/tests/inc/auth_password.test.php b/_test/tests/inc/auth_password.test.php
index 5067e2ca1acf23fb26bf3972b022d91e190632dc..71b0dfb4c4a9e1de2dbb54dcba118ea443665352 100644
--- a/_test/tests/inc/auth_password.test.php
+++ b/_test/tests/inc/auth_password.test.php
@@ -2,7 +2,7 @@
 
 class auth_password_test extends DokuWikiTest {
 
-    // hashes for the password foo$method, using abcdefgh as salt
+    // hashes for the password foo$method, using abcdefgh12345678912345678912345678 as salt
     var $passes = array(
         'smd5'  => '$1$abcdefgh$SYbjm2AEvSoHG7Xapi8so.',
         'apr1'  => '$apr1$abcdefgh$C/GzYTF4kOVByYLEoD5X4.',
@@ -24,14 +24,24 @@ class auth_password_test extends DokuWikiTest {
             // Check SHA512 only if available in this PHP
             $this->passes['sha512'] = '$6$abcdefgh12345678$J9.zOcgx0lotwZdcz0uulA3IVQMinZvFZVjA5vapRLVAAqtay23XD4xeeUxQ3B4JvDWYFBIxVWW1tOYlHX13k1';
         }
+        if(function_exists('hash_pbkdf2')) {
+            if(in_array('sha256', hash_algos())) {
+                $this->passes['djangopbkdf2_sha256'] = 'pbkdf2_sha256$24000$abcdefgh1234$R23OyZJ0nGHLG6MvPNfEkV5AOz3jUY5zthByPXs2gn0=';
+            }
+            if(in_array('sha1', hash_algos())) {
+                $this->passes['djangopbkdf2_sha1'] = 'pbkdf2_sha1$24000$abcdefgh1234$pOliX4vV1hgOv7lFNURIHHx41HI=';
+            }
+        }
     }
 
 
     function test_cryptPassword(){
         foreach($this->passes as $method => $hash){
             $info = "testing method $method";
-            $this->assertEquals(auth_cryptPassword('foo'.$method, $method,'abcdefgh12345678912345678912345678'),
-                $hash, $info);
+            $this->assertEquals(
+                $hash,
+                auth_cryptPassword('foo'.$method, $method,'abcdefgh12345678912345678912345678'),
+                $info);
         }
     }
 
diff --git a/inc/PassHash.class.php b/inc/PassHash.class.php
index 0701c4116fa7756d0e4c2f8d4124af55eb53ce65..bcfea1dfc35d61af784d290a56ac901bc7312103 100644
--- a/inc/PassHash.class.php
+++ b/inc/PassHash.class.php
@@ -42,8 +42,15 @@ class PassHash {
             $magic  = 'P';
         } elseif(preg_match('/^\$H\$(.{31})$/', $hash, $m)) {
             $method = 'pmd5';
-            $salt   = $m[1];
-            $magic  = 'H';
+            $salt = $m[1];
+            $magic = 'H';
+        } elseif(preg_match('/^pbkdf2_(\w+?)\$(\d+)\$(.{12})\$/', $hash, $m)) {
+            $method = 'djangopbkdf2';
+            $magic = array(
+                'algo' => $m[1],
+                'iter' => $m[2],
+            );
+            $salt = $m[3];
         } elseif(preg_match('/^sha1\$(.{5})\$/', $hash, $m)) {
             $method = 'djangosha1';
             $salt   = $m[1];
@@ -83,7 +90,8 @@ class PassHash {
 
         //crypt and compare
         $call = 'hash_'.$method;
-        if($this->$call($clear, $salt, $magic) === $hash) {
+        $newhash = $this->$call($clear, $salt, $magic);
+        if($newhash === $hash) {
             return true;
         }
         return false;
@@ -435,6 +443,69 @@ class PassHash {
         return 'md5$'.$salt.'$'.md5($salt.$clear);
     }
 
+    /**
+     * Password hashing method 'djangopbkdf2'
+     *
+     * An algorithm and iteration count should be given in the opts array.
+     * Defaults to sha256 and 24000 iterations
+     *
+     * @param string $clear The clear text to hash
+     * @param string $salt  The salt to use, null for random
+     * @param array $opts ('algo' => hash algorithm, 'iter' => iterations)
+     * @return string Hashed password
+     * @throws Exception when PHP is missing support for the method/algo
+     */
+    public function hash_djangopbkdf2($clear, $salt=null, $opts=array()) {
+        $this->init_salt($salt, 12);
+        if(empty($opts['algo'])) {
+            $algo = 'sha256';
+        } else {
+            $algo = $opts['algo'];
+        }
+        if(empty($opts['iter'])) {
+            $iter = 24000;
+        } else {
+            $iter = (int) $opts['iter'];
+        }
+        if(!function_exists('hash_pbkdf2')) {
+            throw new Exception('This PHP installation has no PBKDF2 support');
+        }
+        if(!in_array($algo, hash_algos())) {
+            throw new Exception("This PHP installation has no $algo support");
+        }
+
+        $hash = base64_encode(hash_pbkdf2($algo, $clear, $salt, $iter, 0, true));
+        return "pbkdf2_$algo\$$iter\$$salt\$$hash";
+    }
+
+    /**
+     * Alias for djangopbkdf2 defaulting to sha256 as hash algorithm
+     *
+     * @param string $clear The clear text to hash
+     * @param string $salt  The salt to use, null for random
+     * @param array $opts ('iter' => iterations)
+     * @return string Hashed password
+     * @throws Exception when PHP is missing support for the method/algo
+     */
+    public function hash_djangopbkdf2_sha256($clear, $salt=null, $opts=array()) {
+        $opts['algo'] = 'sha256';
+        return $this->hash_djangopbkdf2($clear, $salt, $opts);
+    }
+
+    /**
+     * Alias for djangopbkdf2 defaulting to sha1 as hash algorithm
+     *
+     * @param string $clear The clear text to hash
+     * @param string $salt  The salt to use, null for random
+     * @param array $opts ('iter' => iterations)
+     * @return string Hashed password
+     * @throws Exception when PHP is missing support for the method/algo
+     */
+    public function hash_djangopbkdf2_sha1($clear, $salt=null, $opts=array()) {
+        $opts['algo'] = 'sha1';
+        return $this->hash_djangopbkdf2($clear, $salt, $opts);
+    }
+
     /**
      * Passwordhashing method 'bcrypt'
      *
diff --git a/lib/plugins/config/settings/config.metadata.php b/lib/plugins/config/settings/config.metadata.php
index c362a9f1af144e5a7104a29dda7f277f4e747e5d..5323e71f61a770d62c1ca8952fef30bdfa635d64 100644
--- a/lib/plugins/config/settings/config.metadata.php
+++ b/lib/plugins/config/settings/config.metadata.php
@@ -137,7 +137,10 @@ $meta['_authentication'] = array('fieldset');
 $meta['useacl']      = array('onoff','_caution' => 'danger');
 $meta['autopasswd']  = array('onoff');
 $meta['authtype']    = array('authtype','_caution' => 'danger');
-$meta['passcrypt']   = array('multichoice','_choices' => array('smd5','md5','apr1','sha1','ssha','lsmd5','crypt','mysql','my411','kmd5','pmd5','hmd5','mediawiki','bcrypt','djangomd5','djangosha1','sha512'));
+$meta['passcrypt']   = array('multichoice','_choices' => array(
+    'smd5','md5','apr1','sha1','ssha','lsmd5','crypt','mysql','my411','kmd5','pmd5','hmd5',
+    'mediawiki','bcrypt','djangomd5','djangosha1','djangopbkdf2_sha1','djangopbkdf2_sha256','sha512'
+));
 $meta['defaultgroup']= array('string');
 $meta['superuser']   = array('string','_caution' => 'danger');
 $meta['manager']     = array('string');