View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.codec.digest;
18  
19  import java.security.MessageDigest;
20  import java.security.NoSuchAlgorithmException;
21  import java.util.Arrays;
22  import java.util.regex.Matcher;
23  import java.util.regex.Pattern;
24  
25  import org.apache.commons.codec.Charsets;
26  
27  /**
28   * SHA2-based Unix crypt implementation.
29   * <p>
30   * Based on the C implementation released into the Public Domain by Ulrich Drepper &lt;drepper@redhat.com&gt;
31   * http://www.akkadia.org/drepper/SHA-crypt.txt
32   * <p>
33   * Conversion to Kotlin and from there to Java in 2012 by Christian Hammers &lt;ch@lathspell.de&gt; and likewise put
34   * into the Public Domain.
35   * <p>
36   * This class is immutable and thread-safe.
37   *
38   * @version $Id: Sha2Crypt.html 889935 2013-12-11 05:05:13Z ggregory $
39   * @since 1.7
40   */
41  public class Sha2Crypt {
42  
43      /** Default number of rounds if not explicitly specified. */
44      private static final int ROUNDS_DEFAULT = 5000;
45  
46      /** Maximum number of rounds. */
47      private static final int ROUNDS_MAX = 999999999;
48  
49      /** Minimum number of rounds. */
50      private static final int ROUNDS_MIN = 1000;
51  
52      /** Prefix for optional rounds specification. */
53      private static final String ROUNDS_PREFIX = "rounds=";
54  
55      /** The number of bytes the final hash value will have (SHA-256 variant). */
56      private static final int SHA256_BLOCKSIZE = 32;
57  
58      /** The prefixes that can be used to identify this crypt() variant (SHA-256). */
59      static final String SHA256_PREFIX = "$5$";
60  
61      /** The number of bytes the final hash value will have (SHA-512 variant). */
62      private static final int SHA512_BLOCKSIZE = 64;
63  
64      /** The prefixes that can be used to identify this crypt() variant (SHA-512). */
65      static final String SHA512_PREFIX = "$6$";
66  
67      /** The pattern to match valid salt values. */
68      private static final Pattern SALT_PATTERN = Pattern
69              .compile("^\\$([56])\\$(rounds=(\\d+)\\$)?([\\.\\/a-zA-Z0-9]{1,16}).*");
70  
71      /**
72       * Generates a libc crypt() compatible "$5$" hash value with random salt.
73       * <p>
74       * See {@link Crypt#crypt(String, String)} for details.
75       *
76       * @throws RuntimeException
77       *             when a {@link java.security.NoSuchAlgorithmException} is caught.
78       */
79      public static String sha256Crypt(final byte[] keyBytes) {
80          return sha256Crypt(keyBytes, null);
81      }
82  
83      /**
84       * Generates a libc6 crypt() compatible "$5$" hash value.
85       * <p>
86       * See {@link Crypt#crypt(String, String)} for details.
87       *
88       * @throws IllegalArgumentException
89       *             if the salt does not match the allowed pattern
90       * @throws RuntimeException
91       *             when a {@link java.security.NoSuchAlgorithmException} is caught.
92       */
93      public static String sha256Crypt(final byte[] keyBytes, String salt) {
94          if (salt == null) {
95              salt = SHA256_PREFIX + B64.getRandomSalt(8);
96          }
97          return sha2Crypt(keyBytes, salt, SHA256_PREFIX, SHA256_BLOCKSIZE, MessageDigestAlgorithms.SHA_256);
98      }
99  
100     /**
101      * Generates a libc6 crypt() compatible "$5$" or "$6$" SHA2 based hash value.
102      * <p>
103      * This is a nearly line by line conversion of the original C function. The numbered comments are from the
104      * algorithm description, the short C-style ones from the original C code and the ones with "Remark" from me.
105      * <p>
106      * See {@link Crypt#crypt(String, String)} for details.
107      *
108      * @param keyBytes
109      *            plaintext that should be hashed
110      * @param salt
111      *            real salt value without prefix or "rounds="
112      * @param saltPrefix
113      *            either $5$ or $6$
114      * @param blocksize
115      *            a value that differs between $5$ and $6$
116      * @param algorithm
117      *            {@link MessageDigest} algorithm identifier string
118      * @return complete hash value including prefix and salt
119      * @throws IllegalArgumentException
120      *             if the given salt is {@code null} or does not match the allowed pattern
121      * @throws IllegalArgumentException
122      *             when a {@link NoSuchAlgorithmException} is caught
123      * @see MessageDigestAlgorithms
124      */
125     private static String sha2Crypt(final byte[] keyBytes, final String salt, final String saltPrefix,
126                                     final int blocksize, final String algorithm) {
127 
128         final int keyLen = keyBytes.length;
129 
130         // Extracts effective salt and the number of rounds from the given salt.
131         int rounds = ROUNDS_DEFAULT;
132         boolean roundsCustom = false;
133         if (salt == null) {
134             throw new IllegalArgumentException("Salt must not be null");
135         }
136 
137         final Matcher m = SALT_PATTERN.matcher(salt);
138         if (m == null || !m.find()) {
139             throw new IllegalArgumentException("Invalid salt value: " + salt);
140         }
141         if (m.group(3) != null) {
142             rounds = Integer.parseInt(m.group(3));
143             rounds = Math.max(ROUNDS_MIN, Math.min(ROUNDS_MAX, rounds));
144             roundsCustom = true;
145         }
146         final String saltString = m.group(4);
147         final byte[] saltBytes = saltString.getBytes(Charsets.UTF_8);
148         final int saltLen = saltBytes.length;
149 
150         // 1. start digest A
151         // Prepare for the real work.
152         MessageDigest ctx = DigestUtils.getDigest(algorithm);
153 
154         // 2. the password string is added to digest A
155         /*
156          * Add the key string.
157          */
158         ctx.update(keyBytes);
159 
160         // 3. the salt string is added to digest A. This is just the salt string
161         // itself without the enclosing '$', without the magic salt_prefix $5$ and
162         // $6$ respectively and without the rounds=<N> specification.
163         //
164         // NB: the MD5 algorithm did add the $1$ salt_prefix. This is not deemed
165         // necessary since it is a constant string and does not add security
166         // and /possibly/ allows a plain text attack. Since the rounds=<N>
167         // specification should never be added this would also create an
168         // inconsistency.
169         /*
170          * The last part is the salt string. This must be at most 16 characters and it ends at the first `$' character
171          * (for compatibility with existing implementations).
172          */
173         ctx.update(saltBytes);
174 
175         // 4. start digest B
176         /*
177          * Compute alternate sha512 sum with input KEY, SALT, and KEY. The final result will be added to the first
178          * context.
179          */
180         MessageDigest altCtx = DigestUtils.getDigest(algorithm);
181 
182         // 5. add the password to digest B
183         /*
184          * Add key.
185          */
186         altCtx.update(keyBytes);
187 
188         // 6. add the salt string to digest B
189         /*
190          * Add salt.
191          */
192         altCtx.update(saltBytes);
193 
194         // 7. add the password again to digest B
195         /*
196          * Add key again.
197          */
198         altCtx.update(keyBytes);
199 
200         // 8. finish digest B
201         /*
202          * Now get result of this (32 bytes) and add it to the other context.
203          */
204         byte[] altResult = altCtx.digest();
205 
206         // 9. For each block of 32 or 64 bytes in the password string (excluding
207         // the terminating NUL in the C representation), add digest B to digest A
208         /*
209          * Add for any character in the key one byte of the alternate sum.
210          */
211         /*
212          * (Remark: the C code comment seems wrong for key length > 32!)
213          */
214         int cnt = keyBytes.length;
215         while (cnt > blocksize) {
216             ctx.update(altResult, 0, blocksize);
217             cnt -= blocksize;
218         }
219 
220         // 10. For the remaining N bytes of the password string add the first
221         // N bytes of digest B to digest A
222         ctx.update(altResult, 0, cnt);
223 
224         // 11. For each bit of the binary representation of the length of the
225         // password string up to and including the highest 1-digit, starting
226         // from to lowest bit position (numeric value 1):
227         //
228         // a) for a 1-digit add digest B to digest A
229         //
230         // b) for a 0-digit add the password string
231         //
232         // NB: this step differs significantly from the MD5 algorithm. It
233         // adds more randomness.
234         /*
235          * Take the binary representation of the length of the key and for every 1 add the alternate sum, for every 0
236          * the key.
237          */
238         cnt = keyBytes.length;
239         while (cnt > 0) {
240             if ((cnt & 1) != 0) {
241                 ctx.update(altResult, 0, blocksize);
242             } else {
243                 ctx.update(keyBytes);
244             }
245             cnt >>= 1;
246         }
247 
248         // 12. finish digest A
249         /*
250          * Create intermediate result.
251          */
252         altResult = ctx.digest();
253 
254         // 13. start digest DP
255         /*
256          * Start computation of P byte sequence.
257          */
258         altCtx = DigestUtils.getDigest(algorithm);
259 
260         // 14. for every byte in the password (excluding the terminating NUL byte
261         // in the C representation of the string)
262         //
263         // add the password to digest DP
264         /*
265          * For every character in the password add the entire password.
266          */
267         for (int i = 1; i <= keyLen; i++) {
268             altCtx.update(keyBytes);
269         }
270 
271         // 15. finish digest DP
272         /*
273          * Finish the digest.
274          */
275         byte[] tempResult = altCtx.digest();
276 
277         // 16. produce byte sequence P of the same length as the password where
278         //
279         // a) for each block of 32 or 64 bytes of length of the password string
280         // the entire digest DP is used
281         //
282         // b) for the remaining N (up to 31 or 63) bytes use the first N
283         // bytes of digest DP
284         /*
285          * Create byte sequence P.
286          */
287         final byte[] pBytes = new byte[keyLen];
288         int cp = 0;
289         while (cp < keyLen - blocksize) {
290             System.arraycopy(tempResult, 0, pBytes, cp, blocksize);
291             cp += blocksize;
292         }
293         System.arraycopy(tempResult, 0, pBytes, cp, keyLen - cp);
294 
295         // 17. start digest DS
296         /*
297          * Start computation of S byte sequence.
298          */
299         altCtx = DigestUtils.getDigest(algorithm);
300 
301         // 18. repeast the following 16+A[0] times, where A[0] represents the first
302         // byte in digest A interpreted as an 8-bit unsigned value
303         //
304         // add the salt to digest DS
305         /*
306          * For every character in the password add the entire password.
307          */
308         for (int i = 1; i <= 16 + (altResult[0] & 0xff); i++) {
309             altCtx.update(saltBytes);
310         }
311 
312         // 19. finish digest DS
313         /*
314          * Finish the digest.
315          */
316         tempResult = altCtx.digest();
317 
318         // 20. produce byte sequence S of the same length as the salt string where
319         //
320         // a) for each block of 32 or 64 bytes of length of the salt string
321         // the entire digest DS is used
322         //
323         // b) for the remaining N (up to 31 or 63) bytes use the first N
324         // bytes of digest DS
325         /*
326          * Create byte sequence S.
327          */
328         // Remark: The salt is limited to 16 chars, how does this make sense?
329         final byte[] sBytes = new byte[saltLen];
330         cp = 0;
331         while (cp < saltLen - blocksize) {
332             System.arraycopy(tempResult, 0, sBytes, cp, blocksize);
333             cp += blocksize;
334         }
335         System.arraycopy(tempResult, 0, sBytes, cp, saltLen - cp);
336 
337         // 21. repeat a loop according to the number specified in the rounds=<N>
338         // specification in the salt (or the default value if none is
339         // present). Each round is numbered, starting with 0 and up to N-1.
340         //
341         // The loop uses a digest as input. In the first round it is the
342         // digest produced in step 12. In the latter steps it is the digest
343         // produced in step 21.h. The following text uses the notation
344         // "digest A/C" to describe this behavior.
345         /*
346          * Repeatedly run the collected hash value through sha512 to burn CPU cycles.
347          */
348         for (int i = 0; i <= rounds - 1; i++) {
349             // a) start digest C
350             /*
351              * New context.
352              */
353             ctx = DigestUtils.getDigest(algorithm);
354 
355             // b) for odd round numbers add the byte sequense P to digest C
356             // c) for even round numbers add digest A/C
357             /*
358              * Add key or last result.
359              */
360             if ((i & 1) != 0) {
361                 ctx.update(pBytes, 0, keyLen);
362             } else {
363                 ctx.update(altResult, 0, blocksize);
364             }
365 
366             // d) for all round numbers not divisible by 3 add the byte sequence S
367             /*
368              * Add salt for numbers not divisible by 3.
369              */
370             if (i % 3 != 0) {
371                 ctx.update(sBytes, 0, saltLen);
372             }
373 
374             // e) for all round numbers not divisible by 7 add the byte sequence P
375             /*
376              * Add key for numbers not divisible by 7.
377              */
378             if (i % 7 != 0) {
379                 ctx.update(pBytes, 0, keyLen);
380             }
381 
382             // f) for odd round numbers add digest A/C
383             // g) for even round numbers add the byte sequence P
384             /*
385              * Add key or last result.
386              */
387             if ((i & 1) != 0) {
388                 ctx.update(altResult, 0, blocksize);
389             } else {
390                 ctx.update(pBytes, 0, keyLen);
391             }
392 
393             // h) finish digest C.
394             /*
395              * Create intermediate result.
396              */
397             altResult = ctx.digest();
398         }
399 
400         // 22. Produce the output string. This is an ASCII string of the maximum
401         // size specified above, consisting of multiple pieces:
402         //
403         // a) the salt salt_prefix, $5$ or $6$ respectively
404         //
405         // b) the rounds=<N> specification, if one was present in the input
406         // salt string. A trailing '$' is added in this case to separate
407         // the rounds specification from the following text.
408         //
409         // c) the salt string truncated to 16 characters
410         //
411         // d) a '$' character
412         /*
413          * Now we can construct the result string. It consists of three parts.
414          */
415         final StringBuilder buffer = new StringBuilder(saltPrefix);
416         if (roundsCustom) {
417             buffer.append(ROUNDS_PREFIX);
418             buffer.append(rounds);
419             buffer.append("$");
420         }
421         buffer.append(saltString);
422         buffer.append("$");
423 
424         // e) the base-64 encoded final C digest. The encoding used is as
425         // follows:
426         // [...]
427         //
428         // Each group of three bytes from the digest produces four
429         // characters as output:
430         //
431         // 1. character: the six low bits of the first byte
432         // 2. character: the two high bits of the first byte and the
433         // four low bytes from the second byte
434         // 3. character: the four high bytes from the second byte and
435         // the two low bits from the third byte
436         // 4. character: the six high bits from the third byte
437         //
438         // The groups of three bytes are as follows (in this sequence).
439         // These are the indices into the byte array containing the
440         // digest, starting with index 0. For the last group there are
441         // not enough bytes left in the digest and the value zero is used
442         // in its place. This group also produces only three or two
443         // characters as output for SHA-512 and SHA-512 respectively.
444 
445         // This was just a safeguard in the C implementation:
446         // int buflen = salt_prefix.length() - 1 + ROUNDS_PREFIX.length() + 9 + 1 + salt_string.length() + 1 + 86 + 1;
447 
448         if (blocksize == 32) {
449             B64.b64from24bit(altResult[0], altResult[10], altResult[20], 4, buffer);
450             B64.b64from24bit(altResult[21], altResult[1], altResult[11], 4, buffer);
451             B64.b64from24bit(altResult[12], altResult[22], altResult[2], 4, buffer);
452             B64.b64from24bit(altResult[3], altResult[13], altResult[23], 4, buffer);
453             B64.b64from24bit(altResult[24], altResult[4], altResult[14], 4, buffer);
454             B64.b64from24bit(altResult[15], altResult[25], altResult[5], 4, buffer);
455             B64.b64from24bit(altResult[6], altResult[16], altResult[26], 4, buffer);
456             B64.b64from24bit(altResult[27], altResult[7], altResult[17], 4, buffer);
457             B64.b64from24bit(altResult[18], altResult[28], altResult[8], 4, buffer);
458             B64.b64from24bit(altResult[9], altResult[19], altResult[29], 4, buffer);
459             B64.b64from24bit((byte) 0, altResult[31], altResult[30], 3, buffer);
460         } else {
461             B64.b64from24bit(altResult[0], altResult[21], altResult[42], 4, buffer);
462             B64.b64from24bit(altResult[22], altResult[43], altResult[1], 4, buffer);
463             B64.b64from24bit(altResult[44], altResult[2], altResult[23], 4, buffer);
464             B64.b64from24bit(altResult[3], altResult[24], altResult[45], 4, buffer);
465             B64.b64from24bit(altResult[25], altResult[46], altResult[4], 4, buffer);
466             B64.b64from24bit(altResult[47], altResult[5], altResult[26], 4, buffer);
467             B64.b64from24bit(altResult[6], altResult[27], altResult[48], 4, buffer);
468             B64.b64from24bit(altResult[28], altResult[49], altResult[7], 4, buffer);
469             B64.b64from24bit(altResult[50], altResult[8], altResult[29], 4, buffer);
470             B64.b64from24bit(altResult[9], altResult[30], altResult[51], 4, buffer);
471             B64.b64from24bit(altResult[31], altResult[52], altResult[10], 4, buffer);
472             B64.b64from24bit(altResult[53], altResult[11], altResult[32], 4, buffer);
473             B64.b64from24bit(altResult[12], altResult[33], altResult[54], 4, buffer);
474             B64.b64from24bit(altResult[34], altResult[55], altResult[13], 4, buffer);
475             B64.b64from24bit(altResult[56], altResult[14], altResult[35], 4, buffer);
476             B64.b64from24bit(altResult[15], altResult[36], altResult[57], 4, buffer);
477             B64.b64from24bit(altResult[37], altResult[58], altResult[16], 4, buffer);
478             B64.b64from24bit(altResult[59], altResult[17], altResult[38], 4, buffer);
479             B64.b64from24bit(altResult[18], altResult[39], altResult[60], 4, buffer);
480             B64.b64from24bit(altResult[40], altResult[61], altResult[19], 4, buffer);
481             B64.b64from24bit(altResult[62], altResult[20], altResult[41], 4, buffer);
482             B64.b64from24bit((byte) 0, (byte) 0, altResult[63], 2, buffer);
483         }
484 
485         /*
486          * Clear the buffer for the intermediate result so that people attaching to processes or reading core dumps
487          * cannot get any information.
488          */
489         // Is there a better way to do this with the JVM?
490         Arrays.fill(tempResult, (byte) 0);
491         Arrays.fill(pBytes, (byte) 0);
492         Arrays.fill(sBytes, (byte) 0);
493         ctx.reset();
494         altCtx.reset();
495         Arrays.fill(keyBytes, (byte) 0);
496         Arrays.fill(saltBytes, (byte) 0);
497 
498         return buffer.toString();
499     }
500 
501     /**
502      * Generates a libc crypt() compatible "$6$" hash value with random salt.
503      * <p>
504      * See {@link Crypt#crypt(String, String)} for details.
505      *
506      * @throws RuntimeException
507      *             when a {@link java.security.NoSuchAlgorithmException} is caught.
508      */
509     public static String sha512Crypt(final byte[] keyBytes) {
510         return sha512Crypt(keyBytes, null);
511     }
512 
513     /**
514      * Generates a libc6 crypt() compatible "$6$" hash value.
515      * <p>
516      * See {@link Crypt#crypt(String, String)} for details.
517      *
518      * @throws IllegalArgumentException
519      *             if the salt does not match the allowed pattern
520      * @throws RuntimeException
521      *             when a {@link java.security.NoSuchAlgorithmException} is caught.
522      */
523     public static String sha512Crypt(final byte[] keyBytes, String salt) {
524         if (salt == null) {
525             salt = SHA512_PREFIX + B64.getRandomSalt(8);
526         }
527         return sha2Crypt(keyBytes, salt, SHA512_PREFIX, SHA512_BLOCKSIZE, MessageDigestAlgorithms.SHA_512);
528     }
529 }