
org.jclouds.crypto.Sha512Crypt Maven / Gradle / Ivy
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/*
Sha512Crypt.java
Created: 18 December 2007
Last Changed By: $Author: broccol $
Version: $Revision: 7692 $
Last Mod Date: $Date: 2007-12-30 01:55:31 -0600 (Sun, 30 Dec 2007) $
Java Port By: James Ratcliff, [email protected]
This class implements the new generation, scalable, SHA512-based
Unix 'crypt' algorithm developed by a group of engineers from Red
Hat, Sun, IBM, and HP for common use in the Unix and Linux
/etc/shadow files.
The Linux glibc library (starting at version 2.7) includes support
for validating passwords hashed using this algorithm.
The algorithm itself was released into the Public Domain by Ulrich
Drepper . A discussion of the rationale and
development of this algorithm is at
http://people.redhat.com/drepper/sha-crypt.html
and the specification and a sample C language implementation is at
http://people.redhat.com/drepper/SHA-crypt.txt
This Java Port is
Copyright (c) 2008 The University of Texas at Austin.
All rights reserved.
Redistribution and use in source and binary form are permitted
provided that distributions retain this entire copyright notice
and comment. Neither the name of the University nor the names of
its contributors may be used to endorse or promote products
derived from this software without specific prior written
permission. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE.
*/
package org.jclouds.crypto;
import java.security.MessageDigest;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.encryption.internal.JCECrypto;
import com.google.common.base.Throwables;
/**
* This class defines a method,
* {@link Sha512Crypt#Sha512_crypt(java.lang.String, java.lang.String, int)
* Sha512_crypt()}, which takes a password and a salt string and generates a
* Sha512 encrypted password entry.
*
* This class implements the new generation, scalable, SHA512-based Unix 'crypt'
* algorithm developed by a group of engineers from Red Hat, Sun, IBM, and HP
* for common use in the Unix and Linux /etc/shadow files.
*
* The Linux glibc library (starting at version 2.7) includes support for
* validating passwords hashed using this algorithm.
*
* The algorithm itself was released into the Public Domain by Ulrich Drepper
* <[email protected]>. A discussion of the rationale and development of
* this algorithm is at
*
* http://people.redhat.com/drepper/sha-crypt.html
*
* and the specification and a sample C language implementation is at
*
* http://people.redhat.com/drepper/SHA-crypt.txt
*/
public class Sha512Crypt {
public static com.google.common.base.Function function() {
return Function.INSTANCE;
}
public static enum Function implements com.google.common.base.Function {
INSTANCE;
private Crypto crypto;
Function() {
try {
this.crypto = new JCECrypto();
} catch (Exception e) {
Throwables.propagate(e);
}
}
@Override
public String apply(String input) {
return Sha512Crypt.makeShadowLine(input, null, crypto);
}
@Override
public String toString() {
return "sha512Crypt()";
}
}
static private final String sha512_salt_prefix = "$6$";
static private final String sha512_rounds_prefix = "rounds=";
static private final int SALT_LEN_MAX = 16;
static private final int ROUNDS_DEFAULT = 5000;
static private final int ROUNDS_MIN = 1000;
static private final int ROUNDS_MAX = 999999999;
static private final String SALTCHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
static private final String itoa64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
/**
* This method actually generates an Sha512 crypted password hash from a
* plaintext password and a salt.
*
*
* The resulting string will be in the form
* '$6$<rounds=n>$<salt>$<hashed mess>
*
*
* @param password
* Plaintext password
*
* @param shadowPrefix
* An encoded salt/rounds which will be consulted to determine the
* salt and round count, if not null
*
* @return The Sha512 Unix Crypt hash text for the password
*/
public static String makeShadowLine(String password, @Nullable String shadowPrefix, Crypto crypto) {
MessageDigest ctx = crypto.sha512();
MessageDigest alt_ctx = crypto.sha512();
byte[] alt_result;
byte[] temp_result;
byte[] p_bytes = null;
byte[] s_bytes = null;
int cnt, cnt2;
int rounds = ROUNDS_DEFAULT; // Default number of rounds.
StringBuffer buffer;
/* -- */
if (shadowPrefix != null) {
if (shadowPrefix.startsWith(sha512_salt_prefix)) {
shadowPrefix = shadowPrefix.substring(sha512_salt_prefix.length());
}
if (shadowPrefix.startsWith(sha512_rounds_prefix)) {
String num = shadowPrefix.substring(sha512_rounds_prefix.length(), shadowPrefix.indexOf('$'));
int srounds = Integer.valueOf(num).intValue();
shadowPrefix = shadowPrefix.substring(shadowPrefix.indexOf('$') + 1);
rounds = Math.max(ROUNDS_MIN, Math.min(srounds, ROUNDS_MAX));
}
if (shadowPrefix.length() > SALT_LEN_MAX) {
shadowPrefix = shadowPrefix.substring(0, SALT_LEN_MAX);
}
} else {
java.util.Random randgen = new java.util.Random();
StringBuffer saltBuf = new StringBuffer();
while (saltBuf.length() < 16) {
int index = (int) (randgen.nextFloat() * SALTCHARS.length());
saltBuf.append(SALTCHARS.substring(index, index + 1));
}
shadowPrefix = saltBuf.toString();
}
byte[] key = password.getBytes();
byte[] salts = shadowPrefix.getBytes();
ctx.reset();
ctx.update(key, 0, key.length);
ctx.update(salts, 0, salts.length);
alt_ctx.reset();
alt_ctx.update(key, 0, key.length);
alt_ctx.update(salts, 0, salts.length);
alt_ctx.update(key, 0, key.length);
alt_result = alt_ctx.digest();
for (cnt = key.length; cnt > 64; cnt -= 64) {
ctx.update(alt_result, 0, 64);
}
ctx.update(alt_result, 0, cnt);
for (cnt = key.length; cnt > 0; cnt >>= 1) {
if ((cnt & 1) != 0) {
ctx.update(alt_result, 0, 64);
} else {
ctx.update(key, 0, key.length);
}
}
alt_result = ctx.digest();
alt_ctx.reset();
for (cnt = 0; cnt < key.length; ++cnt) {
alt_ctx.update(key, 0, key.length);
}
temp_result = alt_ctx.digest();
p_bytes = new byte[key.length];
for (cnt2 = 0, cnt = p_bytes.length; cnt >= 64; cnt -= 64) {
System.arraycopy(temp_result, 0, p_bytes, cnt2, 64);
cnt2 += 64;
}
System.arraycopy(temp_result, 0, p_bytes, cnt2, cnt);
alt_ctx.reset();
for (cnt = 0; cnt < 16 + (alt_result[0] & 0xFF); ++cnt) {
alt_ctx.update(salts, 0, salts.length);
}
temp_result = alt_ctx.digest();
s_bytes = new byte[salts.length];
for (cnt2 = 0, cnt = s_bytes.length; cnt >= 64; cnt -= 64) {
System.arraycopy(temp_result, 0, s_bytes, cnt2, 64);
cnt2 += 64;
}
System.arraycopy(temp_result, 0, s_bytes, cnt2, cnt);
/*
* Repeatedly run the collected hash value through SHA512 to burn CPU
* cycles.
*/
for (cnt = 0; cnt < rounds; ++cnt) {
ctx.reset();
if ((cnt & 1) != 0) {
ctx.update(p_bytes, 0, key.length);
} else {
ctx.update(alt_result, 0, 64);
}
if (cnt % 3 != 0) {
ctx.update(s_bytes, 0, salts.length);
}
if (cnt % 7 != 0) {
ctx.update(p_bytes, 0, key.length);
}
if ((cnt & 1) != 0) {
ctx.update(alt_result, 0, 64);
} else {
ctx.update(p_bytes, 0, key.length);
}
alt_result = ctx.digest();
}
buffer = new StringBuffer(sha512_salt_prefix);
if (rounds != 5000) {
buffer.append(sha512_rounds_prefix);
buffer.append(rounds);
buffer.append("$");
}
buffer.append(shadowPrefix);
buffer.append("$");
buffer.append(b64_from_24bit(alt_result[0], alt_result[21], alt_result[42], 4));
buffer.append(b64_from_24bit(alt_result[22], alt_result[43], alt_result[1], 4));
buffer.append(b64_from_24bit(alt_result[44], alt_result[2], alt_result[23], 4));
buffer.append(b64_from_24bit(alt_result[3], alt_result[24], alt_result[45], 4));
buffer.append(b64_from_24bit(alt_result[25], alt_result[46], alt_result[4], 4));
buffer.append(b64_from_24bit(alt_result[47], alt_result[5], alt_result[26], 4));
buffer.append(b64_from_24bit(alt_result[6], alt_result[27], alt_result[48], 4));
buffer.append(b64_from_24bit(alt_result[28], alt_result[49], alt_result[7], 4));
buffer.append(b64_from_24bit(alt_result[50], alt_result[8], alt_result[29], 4));
buffer.append(b64_from_24bit(alt_result[9], alt_result[30], alt_result[51], 4));
buffer.append(b64_from_24bit(alt_result[31], alt_result[52], alt_result[10], 4));
buffer.append(b64_from_24bit(alt_result[53], alt_result[11], alt_result[32], 4));
buffer.append(b64_from_24bit(alt_result[12], alt_result[33], alt_result[54], 4));
buffer.append(b64_from_24bit(alt_result[34], alt_result[55], alt_result[13], 4));
buffer.append(b64_from_24bit(alt_result[56], alt_result[14], alt_result[35], 4));
buffer.append(b64_from_24bit(alt_result[15], alt_result[36], alt_result[57], 4));
buffer.append(b64_from_24bit(alt_result[37], alt_result[58], alt_result[16], 4));
buffer.append(b64_from_24bit(alt_result[59], alt_result[17], alt_result[38], 4));
buffer.append(b64_from_24bit(alt_result[18], alt_result[39], alt_result[60], 4));
buffer.append(b64_from_24bit(alt_result[40], alt_result[61], alt_result[19], 4));
buffer.append(b64_from_24bit(alt_result[62], alt_result[20], alt_result[41], 4));
buffer.append(b64_from_24bit((byte) 0x00, (byte) 0x00, alt_result[63], 2));
/*
* Clear the buffer for the intermediate result so that people attaching
* to processes or reading core dumps cannot get any information.
*/
ctx.reset();
return buffer.toString();
}
private static final String b64_from_24bit(byte B2, byte B1, byte B0, int size) {
int v = ((((int) B2) & 0xFF) << 16) | ((((int) B1) & 0xFF) << 8) | ((int) B0 & 0xff);
StringBuffer result = new StringBuffer();
while (--size >= 0) {
result.append(itoa64.charAt((int) (v & 0x3f)));
v >>>= 6;
}
return result.toString();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy