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