All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.activemq.artemis.spi.core.security.scram.ScramUtils Maven / Gradle / Ivy

/*
 * Copyright 2016 Ognyan Bankov
 * 

* All rights reserved. Licensed 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. */ package org.apache.activemq.artemis.spi.core.security.scram; import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Base64; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; /** * Provides static methods for working with SCRAM/SASL */ public class ScramUtils { private static final byte[] INT_1 = new byte[] {0, 0, 0, 1}; private ScramUtils() { throw new AssertionError("non-instantiable utility class"); } /** * Generates salted password. * @param password Clear form password, i.e. what user typed * @param salt Salt to be used * @param iterationsCount Iterations for 'salting' * @param mac HMAC to be used * @return salted password * @throws ScramException */ public static byte[] generateSaltedPassword(final String password, byte[] salt, int iterationsCount, Mac mac) throws ScramException { SecretKeySpec key = new SecretKeySpec(password.getBytes(StandardCharsets.US_ASCII), mac.getAlgorithm()); try { mac.init(key); } catch (InvalidKeyException e) { throw new ScramException("Incompatible key", e); } mac.update(salt); mac.update(INT_1); byte[] result = mac.doFinal(); byte[] previous = null; for (int i = 1; i < iterationsCount; i++) { mac.update(previous != null ? previous : result); previous = mac.doFinal(); for (int x = 0; x < result.length; x++) { result[x] ^= previous[x]; } } return result; } /** * Creates HMAC * @param keyBytes key * @param hmacName HMAC name * @return Mac * @throws InvalidKeyException if internal error occur while working with SecretKeySpec * @throws NoSuchAlgorithmException if hmacName is not supported by the java */ public static Mac createHmac(final byte[] keyBytes, String hmacName) throws NoSuchAlgorithmException, InvalidKeyException { Mac mac = Mac.getInstance(hmacName); SecretKeySpec key = new SecretKeySpec(keyBytes, hmacName); mac.init(key); return mac; } /** * Computes HMAC byte array for given string * @param key key * @param hmacName HMAC name * @param string string for which HMAC will be computed * @return computed HMAC * @throws InvalidKeyException if internal error occur while working with SecretKeySpec * @throws NoSuchAlgorithmException if hmacName is not supported by the java */ public static byte[] computeHmac(final byte[] key, String hmacName, final String string) throws InvalidKeyException, NoSuchAlgorithmException { Mac mac = createHmac(key, hmacName); mac.update(string.getBytes(StandardCharsets.US_ASCII)); return mac.doFinal(); } public static byte[] computeHmac(final byte[] key, Mac hmac, final String string) throws ScramException { try { hmac.init(new SecretKeySpec(key, hmac.getAlgorithm())); } catch (InvalidKeyException e) { throw new ScramException("invalid key", e); } hmac.update(string.getBytes(StandardCharsets.US_ASCII)); return hmac.doFinal(); } /** * Checks if string is null or empty * @param string String to be tested * @return true if the string is null or empty, false otherwise */ public static boolean isNullOrEmpty(String string) { return string == null || string.length() == 0; // string.isEmpty() in Java 6 } /** * Computes the data associated with new password like salted password, keys, etc *

* This method is supposed to be used by a server when user provides new clear form password. We * don't want to save it that way so we generate salted password and store it along with other * data required by the SCRAM mechanism * @param passwordClearText Clear form password, i.e. as provided by the user * @param salt Salt to be used * @param iterations Iterations for 'salting' * @param mac HMAC name to be used * @param messageDigest Digest name to be used * @return new password data while working with SecretKeySpec * @throws ScramException */ public static NewPasswordByteArrayData newPassword(String passwordClearText, byte[] salt, int iterations, MessageDigest messageDigest, Mac mac) throws ScramException { byte[] saltedPassword = ScramUtils.generateSaltedPassword(passwordClearText, salt, iterations, mac); byte[] clientKey = ScramUtils.computeHmac(saltedPassword, mac, "Client Key"); byte[] storedKey = messageDigest.digest(clientKey); byte[] serverKey = ScramUtils.computeHmac(saltedPassword, mac, "Server Key"); return new NewPasswordByteArrayData(saltedPassword, salt, clientKey, storedKey, serverKey, iterations); } /** * Transforms NewPasswordByteArrayData into NewPasswordStringData into database friendly (string) * representation Uses Base64 to encode the byte arrays into strings * @param ba Byte array data * @return String data */ public static NewPasswordStringData byteArrayToStringData(NewPasswordByteArrayData ba) { return new NewPasswordStringData(Base64.getEncoder().encodeToString(ba.saltedPassword), Base64.getEncoder().encodeToString(ba.salt), Base64.getEncoder().encodeToString(ba.clientKey), Base64.getEncoder().encodeToString(ba.storedKey), Base64.getEncoder().encodeToString(ba.serverKey), ba.iterations); } /** * New password data in database friendly format, i.e. Base64 encoded strings */ @SuppressWarnings("unused") public static class NewPasswordStringData { /** * Salted password */ public final String saltedPassword; /** * Used salt */ public final String salt; /** * Client key */ public final String clientKey; /** * Stored key */ public final String storedKey; /** * Server key */ public final String serverKey; /** * Iterations for slating */ public final int iterations; /** * Creates new NewPasswordStringData * @param saltedPassword Salted password * @param salt Used salt * @param clientKey Client key * @param storedKey Stored key * @param serverKey Server key * @param iterations Iterations for slating */ public NewPasswordStringData(String saltedPassword, String salt, String clientKey, String storedKey, String serverKey, int iterations) { this.saltedPassword = saltedPassword; this.salt = salt; this.clientKey = clientKey; this.storedKey = storedKey; this.serverKey = serverKey; this.iterations = iterations; } } /** * New password data in byte array format */ @SuppressWarnings("unused") public static class NewPasswordByteArrayData { /** * Salted password */ public final byte[] saltedPassword; /** * Used salt */ public final byte[] salt; /** * Client key */ public final byte[] clientKey; /** * Stored key */ public final byte[] storedKey; /** * Server key */ public final byte[] serverKey; /** * Iterations for slating */ public final int iterations; /** * Creates new NewPasswordByteArrayData * @param saltedPassword Salted password * @param salt Used salt * @param clientKey Client key * @param storedKey Stored key * @param serverKey Server key * @param iterations Iterations for slating */ public NewPasswordByteArrayData(byte[] saltedPassword, byte[] salt, byte[] clientKey, byte[] storedKey, byte[] serverKey, int iterations) { this.saltedPassword = saltedPassword; this.salt = salt; this.clientKey = clientKey; this.storedKey = storedKey; this.serverKey = serverKey; this.iterations = iterations; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy