├── README.md └── src └── com └── gilchris └── encryption └── PasswordDeriveBytes.java /README.md: -------------------------------------------------------------------------------- 1 | # PasswordDeriveBytesForJava 2 | a Java port of C# PasswordDeriveBytes class 3 | 4 | MS original PasswordDeriveBytes class contains a nonstandard extension of the PBKDF1 algorithm. So MS PasswordDeriveBytes is different of normal BKDF1. 5 | 6 | This class has made imitating MS original PasswordDeriveBytes. 7 | 8 | Still, it works for SHA-1, which is default for PasswordDeriveBytes. 9 | 10 | ## Behavior of C# PasswordDeriveBytes GetBytes with SHA1 11 | 12 | Below result is I tested C# PasswordDeriveBytes GetBytes function with SHA1 byte by byte. 13 | 'A' is return value length of first call of GetBytes function. 14 | 15 | | Argument of first call of GetBytes function | Return value of second call of GetBytes function | 16 | | --- | --- | 17 | | 1 ~ 9 | Runtime Error | 18 | | 10 ~ 19 | Skip (20 - A) bytes and read (20 - A) bytes in key stream + normal second result | 19 | | 21 ~ 39 | Skip (40 - A) bytes and read (40 - A) bytes in key stream + normal second result | 20 | | 40 ~ | normal second result | 21 | 22 | # PasswordDeriveBytes For PHP by Gabriel Castillo 23 | 24 | http://www.tyrodeveloper.com/2017/05/passwordderivebytes-en-php.html 25 | 26 | Thank you for porting! -------------------------------------------------------------------------------- /src/com/gilchris/encryption/PasswordDeriveBytes.java: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * Copyright (c) 2014, 2016 Changgun Lee 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 6 | * and associated documentation files (the "Software"), to deal in the Software without restriction, 7 | * including without limitation the rights to use, copy, modify, merge, publish, distribute, 8 | * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all copies or 12 | * substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING 15 | * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 16 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 17 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | */ 20 | package com.gilchris.encryption; 21 | 22 | import java.io.UnsupportedEncodingException; 23 | import java.security.DigestException; 24 | import java.security.MessageDigest; 25 | import java.security.NoSuchAlgorithmException; 26 | 27 | public class PasswordDeriveBytes { 28 | 29 | private String HashNameValue; 30 | private byte[] SaltValue; 31 | private int IterationsValue; 32 | 33 | private MessageDigest hash; 34 | private int state; 35 | private byte[] password; 36 | private byte[] initial; 37 | private byte[] output; 38 | private byte[] firstBaseOutput; 39 | private int position; 40 | private int hashnumber; 41 | private int skip; 42 | 43 | public PasswordDeriveBytes(String strPassword, byte[] rgbSalt) { 44 | Prepare(strPassword, rgbSalt, "SHA-1", 100); 45 | } 46 | 47 | public PasswordDeriveBytes(String strPassword, byte[] rgbSalt, String strHashName, int iterations) { 48 | Prepare(strPassword, rgbSalt, strHashName, iterations); 49 | } 50 | 51 | public PasswordDeriveBytes(byte[] password, byte[] salt) { 52 | Prepare(password, salt, "SHA-1", 100); 53 | } 54 | 55 | 56 | public PasswordDeriveBytes(byte[] password, byte[] salt, String hashName, int iterations) { 57 | Prepare(password, salt, hashName, iterations); 58 | } 59 | 60 | private void Prepare(String strPassword, byte[] rgbSalt, String strHashName, int iterations) { 61 | if (strPassword == null) 62 | throw new NullPointerException("strPassword"); 63 | 64 | byte[] pwd = null; 65 | try { 66 | pwd = strPassword.getBytes("ASCII"); 67 | } catch (UnsupportedEncodingException e) { 68 | e.printStackTrace(); 69 | } 70 | Prepare(pwd, rgbSalt, strHashName, iterations); 71 | } 72 | 73 | private void Prepare(byte[] password, byte[] rgbSalt, String strHashName, int iterations) { 74 | if (password == null) 75 | throw new NullPointerException("password"); 76 | 77 | this.password = password; 78 | 79 | state = 0; 80 | setSalt(rgbSalt); 81 | setHashName(strHashName); 82 | setIterationCount(iterations); 83 | 84 | initial = new byte[hash.getDigestLength()]; 85 | } 86 | 87 | public byte[] getSalt() { 88 | if (SaltValue == null) 89 | return null; 90 | return SaltValue; 91 | } 92 | 93 | public void setSalt(byte[] salt) { 94 | if (state != 0) { 95 | throw new SecurityException("Can't change this property at this stage"); 96 | } 97 | if (salt != null) 98 | SaltValue = salt; 99 | else 100 | SaltValue = null; 101 | } 102 | 103 | public String getHashName() { 104 | return HashNameValue; 105 | } 106 | 107 | public void setHashName(String hashName) { 108 | if (hashName == null) 109 | throw new NullPointerException("HashName"); 110 | if (state != 0) { 111 | throw new SecurityException("Can't change this property at this stage"); 112 | } 113 | HashNameValue = hashName; 114 | 115 | try { 116 | hash = MessageDigest.getInstance(hashName); 117 | } catch (NoSuchAlgorithmException e) { 118 | e.printStackTrace(); 119 | } 120 | } 121 | 122 | public int getIterationCount() { 123 | return IterationsValue; 124 | } 125 | 126 | public void setIterationCount(int iterationCount) { 127 | if (iterationCount < 1) 128 | throw new NullPointerException("HashName"); 129 | if (state != 0) { 130 | throw new SecurityException("Can't change this property at this stage"); 131 | } 132 | IterationsValue = iterationCount; 133 | } 134 | 135 | public byte[] GetBytes(int cb) throws DigestException { 136 | if (cb < 1) { 137 | throw new IndexOutOfBoundsException("cb"); 138 | } 139 | 140 | if (state == 0) { 141 | Reset(); 142 | state = 1; 143 | } 144 | 145 | byte[] result = new byte[cb]; 146 | int cpos = 0; 147 | // the initial hash (in reset) + at least one iteration 148 | int iter = Math.max(1, IterationsValue - 1); 149 | 150 | // start with the PKCS5 key 151 | if (output == null) { 152 | // calculate the PKCS5 key 153 | output = initial; 154 | 155 | // generate new key material 156 | for (int i = 0; i < iter - 1; i++) { 157 | output = hash.digest(output); 158 | } 159 | } 160 | 161 | while (cpos < cb) { 162 | byte[] output2 = null; 163 | if (hashnumber == 0) { 164 | // last iteration on output 165 | output2 = hash.digest(output); 166 | } else if (hashnumber < 1000) { 167 | byte[] n = Integer.toString(hashnumber).getBytes(); 168 | output2 = new byte[output.length + n.length]; 169 | for (int j = 0; j < n.length; j++) { 170 | output2[j] = n[j]; 171 | } 172 | System.arraycopy(output, 0, output2, n.length, output.length); 173 | // don't update output 174 | output2 = hash.digest(output2); 175 | } else { 176 | throw new SecurityException("too long"); 177 | } 178 | 179 | int rem = output2.length - position; 180 | int l = Math.min(cb - cpos, rem); 181 | System.arraycopy(output2, position, result, cpos, l); 182 | 183 | cpos += l; 184 | position += l; 185 | while (position >= output2.length) { 186 | position -= output2.length; 187 | hashnumber++; 188 | } 189 | } 190 | 191 | // saving first output length 192 | if (state == 1) { 193 | if (cb > 20) { 194 | skip = 40 - result.length; 195 | } else { 196 | skip = 20 - result.length; 197 | } 198 | firstBaseOutput = new byte[result.length]; 199 | System.arraycopy(result, 0, firstBaseOutput, 0, result.length); 200 | state = 2; 201 | } 202 | // processing second output 203 | else if (skip > 0) { 204 | byte[] secondBaseOutput = new byte[(firstBaseOutput.length + result.length)]; 205 | System.arraycopy(firstBaseOutput, 0, secondBaseOutput, 0, firstBaseOutput.length); 206 | System.arraycopy(result, 0, secondBaseOutput, firstBaseOutput.length, result.length); 207 | System.arraycopy(secondBaseOutput, skip, result, 0, skip); 208 | 209 | skip = 0; 210 | } 211 | 212 | return result; 213 | } 214 | 215 | public void Reset() throws DigestException { 216 | state = 0; 217 | position = 0; 218 | hashnumber = 0; 219 | skip = 0; 220 | 221 | if (SaltValue != null) { 222 | hash.update(password, 0, password.length); 223 | hash.update(SaltValue, 0, SaltValue.length); 224 | hash.digest(initial, 0, initial.length); 225 | } else { 226 | initial = hash.digest(password); 227 | } 228 | } 229 | } 230 | --------------------------------------------------------------------------------