| | |
| | | * |
| | | * Copyright 2006-2009 Sun Microsystems, Inc. |
| | | * Portions Copyright 2011-2016 ForgeRock AS. |
| | | * Portions Copyright 2026 3A Systems, LLC. |
| | | */ |
| | | package org.opends.server.extensions; |
| | | |
| | |
| | | /** The identity mapper that will be used to map ID strings to user entries. */ |
| | | private IdentityMapper<?> identityMapper; |
| | | |
| | | /** The message digest engine that will be used to create the MD5 digests. */ |
| | | private MessageDigest md5Digest; |
| | | |
| | | /** The lock that will be used to provide threadsafe access to the message digest. */ |
| | | private Object digestLock; |
| | | /** |
| | | * The message digest engines that will be used to create the MD5 digests. |
| | | * MessageDigest is not thread-safe, so a per-thread instance is used |
| | | * instead of a shared instance guarded by a lock: hashing under a global |
| | | * lock serializes all concurrent CRAM-MD5 binds. |
| | | */ |
| | | private ThreadLocal<MessageDigest> md5Digest; |
| | | |
| | | /** The random number generator that we will use to create the server challenge. */ |
| | | private SecureRandom randomGenerator; |
| | |
| | | currentConfig = configuration; |
| | | |
| | | // Initialize the variables needed for the MD5 digest creation. |
| | | digestLock = new Object(); |
| | | randomGenerator = new SecureRandom(); |
| | | |
| | | try |
| | | { |
| | | md5Digest = MessageDigest.getInstance("MD5"); |
| | | // Fail fast at initialization time if the algorithm is unavailable. |
| | | MessageDigest.getInstance("MD5"); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | |
| | | throw new InitializationException(message, e); |
| | | } |
| | | |
| | | md5Digest = ThreadLocal.withInitial(() -> { |
| | | try |
| | | { |
| | | return MessageDigest.getInstance("MD5"); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | throw new IllegalStateException(e); |
| | | } |
| | | }); |
| | | |
| | | // Create and fill the iPad and oPad arrays. |
| | | iPad = new byte[HMAC_MD5_BLOCK_LENGTH]; |
| | | oPad = new byte[HMAC_MD5_BLOCK_LENGTH]; |
| | |
| | | byte[] p = password.toByteArray(); |
| | | byte[] c = challenge.toByteArray(); |
| | | |
| | | // Grab a lock to protect the MD5 digest generation. |
| | | synchronized (digestLock) |
| | | MessageDigest md5Digest = this.md5Digest.get(); |
| | | |
| | | // If the password is longer than the HMAC-MD5 block length, then use an |
| | | // MD5 digest of the password rather than the password itself. |
| | | if (p.length > HMAC_MD5_BLOCK_LENGTH) |
| | | { |
| | | // If the password is longer than the HMAC-MD5 block length, then use an |
| | | // MD5 digest of the password rather than the password itself. |
| | | if (p.length > HMAC_MD5_BLOCK_LENGTH) |
| | | { |
| | | p = md5Digest.digest(p); |
| | | } |
| | | |
| | | // Create byte arrays with data needed for the hash generation. |
| | | byte[] iPadAndData = new byte[HMAC_MD5_BLOCK_LENGTH + c.length]; |
| | | System.arraycopy(iPad, 0, iPadAndData, 0, HMAC_MD5_BLOCK_LENGTH); |
| | | System.arraycopy(c, 0, iPadAndData, HMAC_MD5_BLOCK_LENGTH, c.length); |
| | | |
| | | byte[] oPadAndHash = new byte[HMAC_MD5_BLOCK_LENGTH + MD5_DIGEST_LENGTH]; |
| | | System.arraycopy(oPad, 0, oPadAndHash, 0, HMAC_MD5_BLOCK_LENGTH); |
| | | |
| | | // Iterate through the bytes in the key and XOR them with the iPad and |
| | | // oPad as appropriate. |
| | | for (int i=0; i < p.length; i++) |
| | | { |
| | | iPadAndData[i] ^= p[i]; |
| | | oPadAndHash[i] ^= p[i]; |
| | | } |
| | | |
| | | // Copy an MD5 digest of the iPad-XORed key and the data into the array to |
| | | // be hashed. |
| | | System.arraycopy(md5Digest.digest(iPadAndData), 0, oPadAndHash, |
| | | HMAC_MD5_BLOCK_LENGTH, MD5_DIGEST_LENGTH); |
| | | |
| | | // Return an MD5 digest of the resulting array. |
| | | return md5Digest.digest(oPadAndHash); |
| | | p = md5Digest.digest(p); |
| | | } |
| | | |
| | | // Create byte arrays with data needed for the hash generation. |
| | | byte[] iPadAndData = new byte[HMAC_MD5_BLOCK_LENGTH + c.length]; |
| | | System.arraycopy(iPad, 0, iPadAndData, 0, HMAC_MD5_BLOCK_LENGTH); |
| | | System.arraycopy(c, 0, iPadAndData, HMAC_MD5_BLOCK_LENGTH, c.length); |
| | | |
| | | byte[] oPadAndHash = new byte[HMAC_MD5_BLOCK_LENGTH + MD5_DIGEST_LENGTH]; |
| | | System.arraycopy(oPad, 0, oPadAndHash, 0, HMAC_MD5_BLOCK_LENGTH); |
| | | |
| | | // Iterate through the bytes in the key and XOR them with the iPad and |
| | | // oPad as appropriate. |
| | | for (int i=0; i < p.length; i++) |
| | | { |
| | | iPadAndData[i] ^= p[i]; |
| | | oPadAndHash[i] ^= p[i]; |
| | | } |
| | | |
| | | // Copy an MD5 digest of the iPad-XORed key and the data into the array to |
| | | // be hashed. |
| | | System.arraycopy(md5Digest.digest(iPadAndData), 0, oPadAndHash, |
| | | HMAC_MD5_BLOCK_LENGTH, MD5_DIGEST_LENGTH); |
| | | |
| | | // Return an MD5 digest of the resulting array. |
| | | return md5Digest.digest(oPadAndHash); |
| | | } |
| | | |
| | | @Override |
| | |
| | | * |
| | | * Copyright 2006-2008 Sun Microsystems, Inc. |
| | | * Portions Copyright 2013-2016 ForgeRock AS. |
| | | * Portions Copyright 2026 3A Systems, LLC. |
| | | */ |
| | | package org.opends.server.extensions; |
| | | |
| | |
| | | private static final String CLASS_NAME = |
| | | "org.opends.server.extensions.MD5PasswordStorageScheme"; |
| | | |
| | | /** The message digest that will actually be used to generate the MD5 hashes. */ |
| | | private MessageDigest messageDigest; |
| | | |
| | | /** The lock used to provide threadsafe access to the message digest. */ |
| | | private Object digestLock; |
| | | /** |
| | | * The message digests used to generate the MD5 hashes. |
| | | * MessageDigest is not thread-safe, so a per-thread instance is used |
| | | * instead of a shared instance guarded by a lock: hashing under a global |
| | | * lock serializes all concurrent bind password verifications. |
| | | */ |
| | | private ThreadLocal<MessageDigest> messageDigest; |
| | | |
| | | /** |
| | | * Creates a new instance of this password storage scheme. Note that no |
| | |
| | | { |
| | | try |
| | | { |
| | | messageDigest = MessageDigest.getInstance(MESSAGE_DIGEST_ALGORITHM_MD5); |
| | | // Fail fast at initialization time if the algorithm is unavailable. |
| | | MessageDigest.getInstance(MESSAGE_DIGEST_ALGORITHM_MD5); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | |
| | | throw new InitializationException(message, e); |
| | | } |
| | | |
| | | digestLock = new Object(); |
| | | messageDigest = ThreadLocal.withInitial(() -> { |
| | | try |
| | | { |
| | | return MessageDigest.getInstance(MESSAGE_DIGEST_ALGORITHM_MD5); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | throw new IllegalStateException(e); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | @Override |
| | |
| | | byte[] digestBytes; |
| | | byte[] plaintextBytes = null; |
| | | |
| | | synchronized (digestLock) |
| | | try |
| | | { |
| | | try |
| | | { |
| | | // TODO: Can we avoid this copy? |
| | | plaintextBytes = plaintext.toByteArray(); |
| | | digestBytes = messageDigest.digest(plaintextBytes); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | // TODO: Can we avoid this copy? |
| | | plaintextBytes = plaintext.toByteArray(); |
| | | digestBytes = messageDigest.get().digest(plaintextBytes); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | |
| | | LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get( |
| | | CLASS_NAME, getExceptionMessage(e)); |
| | | throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(), |
| | | message, e); |
| | | } |
| | | finally |
| | | LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get( |
| | | CLASS_NAME, getExceptionMessage(e)); |
| | | throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(), |
| | | message, e); |
| | | } |
| | | finally |
| | | { |
| | | if (plaintextBytes != null) |
| | | { |
| | | if (plaintextBytes != null) |
| | | { |
| | | Arrays.fill(plaintextBytes, (byte) 0); |
| | | } |
| | | Arrays.fill(plaintextBytes, (byte) 0); |
| | | } |
| | | } |
| | | |
| | |
| | | byte[] plaintextBytes = null; |
| | | byte[] digestBytes; |
| | | |
| | | synchronized (digestLock) |
| | | try |
| | | { |
| | | try |
| | | { |
| | | // TODO: Can we avoid this copy? |
| | | plaintextBytes = plaintext.toByteArray(); |
| | | digestBytes = messageDigest.digest(plaintextBytes); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | // TODO: Can we avoid this copy? |
| | | plaintextBytes = plaintext.toByteArray(); |
| | | digestBytes = messageDigest.get().digest(plaintextBytes); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | |
| | | LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get( |
| | | CLASS_NAME, getExceptionMessage(e)); |
| | | throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(), |
| | | message, e); |
| | | } |
| | | finally |
| | | LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get( |
| | | CLASS_NAME, getExceptionMessage(e)); |
| | | throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(), |
| | | message, e); |
| | | } |
| | | finally |
| | | { |
| | | if (plaintextBytes != null) |
| | | { |
| | | if (plaintextBytes != null) |
| | | { |
| | | Arrays.fill(plaintextBytes, (byte) 0); |
| | | } |
| | | Arrays.fill(plaintextBytes, (byte) 0); |
| | | } |
| | | } |
| | | |
| | |
| | | byte[] plaintextPasswordBytes = null; |
| | | ByteString userPWDigestBytes; |
| | | |
| | | synchronized (digestLock) |
| | | try |
| | | { |
| | | try |
| | | { |
| | | // TODO: Can we avoid this copy? |
| | | plaintextPasswordBytes = plaintextPassword.toByteArray(); |
| | | userPWDigestBytes = |
| | | ByteString.wrap(messageDigest.digest(plaintextPasswordBytes)); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | // TODO: Can we avoid this copy? |
| | | plaintextPasswordBytes = plaintextPassword.toByteArray(); |
| | | userPWDigestBytes = |
| | | ByteString.wrap(messageDigest.get().digest(plaintextPasswordBytes)); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | |
| | | return false; |
| | | } |
| | | finally |
| | | return false; |
| | | } |
| | | finally |
| | | { |
| | | if (plaintextPasswordBytes != null) |
| | | { |
| | | if (plaintextPasswordBytes != null) |
| | | { |
| | | Arrays.fill(plaintextPasswordBytes, (byte) 0); |
| | | } |
| | | Arrays.fill(plaintextPasswordBytes, (byte) 0); |
| | | } |
| | | } |
| | | |
| | |
| | | * |
| | | * Copyright 2006-2008 Sun Microsystems, Inc. |
| | | * Portions Copyright 2013-2016 ForgeRock AS. |
| | | * Portions Copyright 2026 3A Systems, LLC. |
| | | */ |
| | | package org.opends.server.extensions; |
| | | |
| | |
| | | private static final String CLASS_NAME = |
| | | "org.opends.server.extensions.SHA1PasswordStorageScheme"; |
| | | |
| | | /** The message digest that will actually be used to generate the SHA-1 hashes. */ |
| | | private MessageDigest messageDigest; |
| | | |
| | | /** The lock used to provide threadsafe access to the message digest. */ |
| | | private Object digestLock; |
| | | /** |
| | | * The message digests used to generate the SHA-1 hashes. |
| | | * MessageDigest is not thread-safe, so a per-thread instance is used |
| | | * instead of a shared instance guarded by a lock: hashing under a global |
| | | * lock serializes all concurrent bind password verifications. |
| | | */ |
| | | private ThreadLocal<MessageDigest> messageDigest; |
| | | |
| | | /** |
| | | * Creates a new instance of this password storage scheme. Note that no |
| | |
| | | { |
| | | try |
| | | { |
| | | messageDigest = MessageDigest.getInstance(MESSAGE_DIGEST_ALGORITHM_SHA_1); |
| | | // Fail fast at initialization time if the algorithm is unavailable. |
| | | MessageDigest.getInstance(MESSAGE_DIGEST_ALGORITHM_SHA_1); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | |
| | | throw new InitializationException(message, e); |
| | | } |
| | | |
| | | digestLock = new Object(); |
| | | messageDigest = ThreadLocal.withInitial(() -> { |
| | | try |
| | | { |
| | | return MessageDigest.getInstance(MESSAGE_DIGEST_ALGORITHM_SHA_1); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | throw new IllegalStateException(e); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | @Override |
| | |
| | | byte[] digestBytes; |
| | | byte[] plaintextBytes = null; |
| | | |
| | | synchronized (digestLock) |
| | | try |
| | | { |
| | | try |
| | | { |
| | | // TODO: Can we avoid this copy? |
| | | plaintextBytes = plaintext.toByteArray(); |
| | | digestBytes = messageDigest.digest(plaintextBytes); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | // TODO: Can we avoid this copy? |
| | | plaintextBytes = plaintext.toByteArray(); |
| | | digestBytes = messageDigest.get().digest(plaintextBytes); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | |
| | | LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get( |
| | | CLASS_NAME, getExceptionMessage(e)); |
| | | throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(), |
| | | message, e); |
| | | } |
| | | finally |
| | | LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get( |
| | | CLASS_NAME, getExceptionMessage(e)); |
| | | throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(), |
| | | message, e); |
| | | } |
| | | finally |
| | | { |
| | | if (plaintextBytes != null) |
| | | { |
| | | if (plaintextBytes != null) |
| | | { |
| | | Arrays.fill(plaintextBytes, (byte) 0); |
| | | } |
| | | Arrays.fill(plaintextBytes, (byte) 0); |
| | | } |
| | | } |
| | | |
| | |
| | | byte[] plaintextBytes = null; |
| | | byte[] digestBytes; |
| | | |
| | | synchronized (digestLock) |
| | | try |
| | | { |
| | | try |
| | | { |
| | | plaintextBytes = plaintext.toByteArray(); |
| | | digestBytes = messageDigest.digest(plaintextBytes); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | plaintextBytes = plaintext.toByteArray(); |
| | | digestBytes = messageDigest.get().digest(plaintextBytes); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | |
| | | LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get( |
| | | CLASS_NAME, getExceptionMessage(e)); |
| | | throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(), |
| | | message, e); |
| | | } |
| | | finally |
| | | LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get( |
| | | CLASS_NAME, getExceptionMessage(e)); |
| | | throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(), |
| | | message, e); |
| | | } |
| | | finally |
| | | { |
| | | if (plaintextBytes != null) |
| | | { |
| | | if (plaintextBytes != null) |
| | | { |
| | | Arrays.fill(plaintextBytes, (byte) 0); |
| | | } |
| | | Arrays.fill(plaintextBytes, (byte) 0); |
| | | } |
| | | } |
| | | |
| | |
| | | byte[] plaintextPasswordBytes = null; |
| | | ByteString userPWDigestBytes; |
| | | |
| | | synchronized (digestLock) |
| | | try |
| | | { |
| | | try |
| | | { |
| | | plaintextPasswordBytes = plaintextPassword.toByteArray(); |
| | | userPWDigestBytes = |
| | | ByteString.wrap(messageDigest.digest(plaintextPasswordBytes)); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | plaintextPasswordBytes = plaintextPassword.toByteArray(); |
| | | userPWDigestBytes = |
| | | ByteString.wrap(messageDigest.get().digest(plaintextPasswordBytes)); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | |
| | | return false; |
| | | } |
| | | finally |
| | | return false; |
| | | } |
| | | finally |
| | | { |
| | | if (plaintextPasswordBytes != null) |
| | | { |
| | | if (plaintextPasswordBytes != null) |
| | | { |
| | | Arrays.fill(plaintextPasswordBytes, (byte) 0); |
| | | } |
| | | Arrays.fill(plaintextPasswordBytes, (byte) 0); |
| | | } |
| | | } |
| | | |
| | |
| | | * |
| | | * Copyright 2006-2008 Sun Microsystems, Inc. |
| | | * Portions Copyright 2013-2016 ForgeRock AS. |
| | | * Portions Copyright 2026 3A Systems, LLC. |
| | | */ |
| | | package org.opends.server.extensions; |
| | | |
| | |
| | | /** The number of bytes MD5 algorithm produces. */ |
| | | private static final int MD5_LENGTH = 16; |
| | | |
| | | /** The message digest that will actually be used to generate the MD5 hashes. */ |
| | | private MessageDigest messageDigest; |
| | | |
| | | /** The lock used to provide threadsafe access to the message digest. */ |
| | | private Object digestLock; |
| | | /** |
| | | * The message digests used to generate the MD5 hashes. |
| | | * MessageDigest is not thread-safe, so a per-thread instance is used |
| | | * instead of a shared instance guarded by a lock: hashing under a global |
| | | * lock serializes all concurrent bind password verifications. |
| | | */ |
| | | private ThreadLocal<MessageDigest> messageDigest; |
| | | |
| | | /** The secure random number generator to use to generate the salt values. */ |
| | | private Random random; |
| | |
| | | { |
| | | try |
| | | { |
| | | messageDigest = MessageDigest.getInstance(MESSAGE_DIGEST_ALGORITHM_MD5); |
| | | // Fail fast at initialization time if the algorithm is unavailable. |
| | | MessageDigest.getInstance(MESSAGE_DIGEST_ALGORITHM_MD5); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | |
| | | throw new InitializationException(message, e); |
| | | } |
| | | |
| | | digestLock = new Object(); |
| | | random = new Random(); |
| | | messageDigest = ThreadLocal.withInitial(() -> { |
| | | try |
| | | { |
| | | return MessageDigest.getInstance(MESSAGE_DIGEST_ALGORITHM_MD5); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | throw new IllegalStateException(e); |
| | | } |
| | | }); |
| | | random = new Random(); |
| | | } |
| | | |
| | | @Override |
| | |
| | | |
| | | byte[] digestBytes; |
| | | |
| | | synchronized (digestLock) |
| | | try |
| | | { |
| | | try |
| | | { |
| | | // Generate the salt and put in the plain+salt array. |
| | | random.nextBytes(saltBytes); |
| | | System.arraycopy(saltBytes,0, plainPlusSalt, plainBytesLength, |
| | | NUM_SALT_BYTES); |
| | | // Generate the salt and put in the plain+salt array. |
| | | random.nextBytes(saltBytes); |
| | | System.arraycopy(saltBytes,0, plainPlusSalt, plainBytesLength, |
| | | NUM_SALT_BYTES); |
| | | |
| | | // Create the hash from the concatenated value. |
| | | digestBytes = messageDigest.digest(plainPlusSalt); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | // Create the hash from the concatenated value. |
| | | digestBytes = messageDigest.get().digest(plainPlusSalt); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | |
| | | LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get( |
| | | CLASS_NAME, getExceptionMessage(e)); |
| | | throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(), |
| | | message, e); |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSalt, (byte) 0); |
| | | } |
| | | LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get( |
| | | CLASS_NAME, getExceptionMessage(e)); |
| | | throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(), |
| | | message, e); |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSalt, (byte) 0); |
| | | } |
| | | |
| | | // Append the salt to the hashed value and base64-the whole thing. |
| | |
| | | |
| | | byte[] digestBytes; |
| | | |
| | | synchronized (digestLock) |
| | | try |
| | | { |
| | | try |
| | | { |
| | | // Generate the salt and put in the plain+salt array. |
| | | random.nextBytes(saltBytes); |
| | | System.arraycopy(saltBytes,0, plainPlusSalt, plainBytesLength, |
| | | NUM_SALT_BYTES); |
| | | // Generate the salt and put in the plain+salt array. |
| | | random.nextBytes(saltBytes); |
| | | System.arraycopy(saltBytes,0, plainPlusSalt, plainBytesLength, |
| | | NUM_SALT_BYTES); |
| | | |
| | | // Create the hash from the concatenated value. |
| | | digestBytes = messageDigest.digest(plainPlusSalt); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | // Create the hash from the concatenated value. |
| | | digestBytes = messageDigest.get().digest(plainPlusSalt); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | |
| | | LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get( |
| | | CLASS_NAME, getExceptionMessage(e)); |
| | | throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(), |
| | | message, e); |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSalt, (byte) 0); |
| | | } |
| | | LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get( |
| | | CLASS_NAME, getExceptionMessage(e)); |
| | | throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(), |
| | | message, e); |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSalt, (byte) 0); |
| | | } |
| | | |
| | | // Append the salt to the hashed value and base64-the whole thing. |
| | |
| | | |
| | | byte[] userDigestBytes; |
| | | |
| | | synchronized (digestLock) |
| | | try |
| | | { |
| | | try |
| | | { |
| | | userDigestBytes = messageDigest.digest(plainPlusSalt); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | userDigestBytes = messageDigest.get().digest(plainPlusSalt); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | |
| | | return false; |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSalt, (byte) 0); |
| | | } |
| | | return false; |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSalt, (byte) 0); |
| | | } |
| | | |
| | | return Arrays.equals(digestBytes, userDigestBytes); |
| | |
| | | |
| | | byte[] digestBytes; |
| | | |
| | | synchronized (digestLock) |
| | | try |
| | | { |
| | | try |
| | | { |
| | | // Generate the salt and put in the plain+salt array. |
| | | random.nextBytes(saltBytes); |
| | | System.arraycopy(saltBytes,0, plainPlusSalt, plaintextLength, |
| | | NUM_SALT_BYTES); |
| | | // Generate the salt and put in the plain+salt array. |
| | | random.nextBytes(saltBytes); |
| | | System.arraycopy(saltBytes,0, plainPlusSalt, plaintextLength, |
| | | NUM_SALT_BYTES); |
| | | |
| | | // Create the hash from the concatenated value. |
| | | digestBytes = messageDigest.digest(plainPlusSalt); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | // Create the hash from the concatenated value. |
| | | digestBytes = messageDigest.get().digest(plainPlusSalt); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | |
| | | LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get( |
| | | CLASS_NAME, getExceptionMessage(e)); |
| | | throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(), |
| | | message, e); |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSalt, (byte) 0); |
| | | } |
| | | LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get( |
| | | CLASS_NAME, getExceptionMessage(e)); |
| | | throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(), |
| | | message, e); |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSalt, (byte) 0); |
| | | } |
| | | |
| | | // Encode and return the value. |
| | |
| | | System.arraycopy(saltBytes, 0, plainPlusSaltBytes, plainBytesLength, |
| | | saltBytes.length); |
| | | |
| | | synchronized (digestLock) |
| | | try |
| | | { |
| | | try |
| | | { |
| | | return Arrays.equals(digestBytes, |
| | | messageDigest.digest(plainPlusSaltBytes)); |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSaltBytes, (byte) 0); |
| | | } |
| | | return Arrays.equals(digestBytes, |
| | | messageDigest.get().digest(plainPlusSaltBytes)); |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSaltBytes, (byte) 0); |
| | | } |
| | | } |
| | | |
| | |
| | | * |
| | | * Copyright 2006-2010 Sun Microsystems, Inc. |
| | | * Portions Copyright 2010-2016 ForgeRock AS. |
| | | * Portions Copyrighted 2026 3A Systems, LLC. |
| | | * Portions Copyright 2026 3A Systems, LLC. |
| | | */ |
| | | package org.opends.server.extensions; |
| | | |
| | |
| | | /** The number of bytes SHA algorithm produces. */ |
| | | private static final int SHA1_LENGTH = 20; |
| | | |
| | | /** The message digest that will actually be used to generate the SHA-1 hashes. */ |
| | | private MessageDigest messageDigest; |
| | | |
| | | /** The lock used to provide threadsafe access to the message digest. */ |
| | | private Object digestLock; |
| | | /** |
| | | * The message digests used to generate the SHA-1 hashes. |
| | | * MessageDigest is not thread-safe, so a per-thread instance is used |
| | | * instead of a shared instance guarded by a lock: hashing under a global |
| | | * lock serializes all concurrent bind password verifications. |
| | | */ |
| | | private ThreadLocal<MessageDigest> messageDigest; |
| | | |
| | | /** The secure random number generator to use to generate the salt values. */ |
| | | private SecureRandom random; |
| | |
| | | { |
| | | try |
| | | { |
| | | messageDigest = MessageDigest.getInstance(MESSAGE_DIGEST_ALGORITHM_SHA_1); |
| | | // Fail fast at initialization time if the algorithm is unavailable. |
| | | MessageDigest.getInstance(MESSAGE_DIGEST_ALGORITHM_SHA_1); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | |
| | | throw new InitializationException(message, e); |
| | | } |
| | | |
| | | digestLock = new Object(); |
| | | random = new SecureRandom(); |
| | | messageDigest = ThreadLocal.withInitial(() -> { |
| | | try |
| | | { |
| | | return MessageDigest.getInstance(MESSAGE_DIGEST_ALGORITHM_SHA_1); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | throw new IllegalStateException(e); |
| | | } |
| | | }); |
| | | random = new SecureRandom(); |
| | | } |
| | | |
| | | @Override |
| | |
| | | |
| | | byte[] digestBytes; |
| | | |
| | | synchronized (digestLock) |
| | | try |
| | | { |
| | | try |
| | | { |
| | | // Generate the salt and put in the plain+salt array. |
| | | random.nextBytes(saltBytes); |
| | | System.arraycopy(saltBytes,0, plainPlusSalt, plainBytesLength, |
| | | NUM_SALT_BYTES); |
| | | // Generate the salt and put in the plain+salt array. |
| | | random.nextBytes(saltBytes); |
| | | System.arraycopy(saltBytes,0, plainPlusSalt, plainBytesLength, |
| | | NUM_SALT_BYTES); |
| | | |
| | | // Create the hash from the concatenated value. |
| | | digestBytes = messageDigest.digest(plainPlusSalt); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | // Create the hash from the concatenated value. |
| | | digestBytes = messageDigest.get().digest(plainPlusSalt); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | |
| | | LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get( |
| | | CLASS_NAME, getExceptionMessage(e)); |
| | | throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(), |
| | | message, e); |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSalt, (byte) 0); |
| | | } |
| | | LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get( |
| | | CLASS_NAME, getExceptionMessage(e)); |
| | | throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(), |
| | | message, e); |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSalt, (byte) 0); |
| | | } |
| | | |
| | | // Append the salt to the hashed value and base64-the whole thing. |
| | |
| | | |
| | | byte[] digestBytes; |
| | | |
| | | synchronized (digestLock) |
| | | try |
| | | { |
| | | try |
| | | { |
| | | // Generate the salt and put in the plain+salt array. |
| | | random.nextBytes(saltBytes); |
| | | System.arraycopy(saltBytes,0, plainPlusSalt, plainBytesLength, |
| | | NUM_SALT_BYTES); |
| | | // Generate the salt and put in the plain+salt array. |
| | | random.nextBytes(saltBytes); |
| | | System.arraycopy(saltBytes,0, plainPlusSalt, plainBytesLength, |
| | | NUM_SALT_BYTES); |
| | | |
| | | // Create the hash from the concatenated value. |
| | | digestBytes = messageDigest.digest(plainPlusSalt); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | // Create the hash from the concatenated value. |
| | | digestBytes = messageDigest.get().digest(plainPlusSalt); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | |
| | | LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get( |
| | | CLASS_NAME, getExceptionMessage(e)); |
| | | throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(), |
| | | message, e); |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSalt, (byte) 0); |
| | | } |
| | | LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get( |
| | | CLASS_NAME, getExceptionMessage(e)); |
| | | throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(), |
| | | message, e); |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSalt, (byte) 0); |
| | | } |
| | | |
| | | // Append the salt to the hashed value and base64-the whole thing. |
| | |
| | | |
| | | byte[] userDigestBytes; |
| | | |
| | | synchronized (digestLock) |
| | | try |
| | | { |
| | | try |
| | | { |
| | | userDigestBytes = messageDigest.digest(plainPlusSalt); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | userDigestBytes = messageDigest.get().digest(plainPlusSalt); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | |
| | | return false; |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSalt, (byte) 0); |
| | | } |
| | | return false; |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSalt, (byte) 0); |
| | | } |
| | | |
| | | return Arrays.equals(digestBytes, userDigestBytes); |
| | |
| | | |
| | | byte[] digestBytes; |
| | | |
| | | synchronized (digestLock) |
| | | try |
| | | { |
| | | try |
| | | { |
| | | // Generate the salt and put in the plain+salt array. |
| | | random.nextBytes(saltBytes); |
| | | System.arraycopy(saltBytes,0, plainPlusSalt, plaintextLength, |
| | | NUM_SALT_BYTES); |
| | | // Generate the salt and put in the plain+salt array. |
| | | random.nextBytes(saltBytes); |
| | | System.arraycopy(saltBytes,0, plainPlusSalt, plaintextLength, |
| | | NUM_SALT_BYTES); |
| | | |
| | | // Create the hash from the concatenated value. |
| | | digestBytes = messageDigest.digest(plainPlusSalt); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | // Create the hash from the concatenated value. |
| | | digestBytes = messageDigest.get().digest(plainPlusSalt); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | |
| | | LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get( |
| | | CLASS_NAME, getExceptionMessage(e)); |
| | | throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(), |
| | | message, e); |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSalt, (byte) 0); |
| | | } |
| | | LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get( |
| | | CLASS_NAME, getExceptionMessage(e)); |
| | | throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(), |
| | | message, e); |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSalt, (byte) 0); |
| | | } |
| | | |
| | | // Encode and return the value. |
| | |
| | | System.arraycopy(saltBytes, 0, plainPlusSaltBytes, plainBytesLength, |
| | | saltBytes.length); |
| | | |
| | | synchronized (digestLock) |
| | | try |
| | | { |
| | | try |
| | | { |
| | | return Arrays.equals(digestBytes, |
| | | messageDigest.digest(plainPlusSaltBytes)); |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSaltBytes, (byte) 0); |
| | | } |
| | | return Arrays.equals(digestBytes, |
| | | messageDigest.get().digest(plainPlusSaltBytes)); |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSaltBytes, (byte) 0); |
| | | } |
| | | } |
| | | |
| | |
| | | * |
| | | * Copyright 2006-2008 Sun Microsystems, Inc. |
| | | * Portions Copyright 2010-2016 ForgeRock AS. |
| | | * Portions Copyright 2026 3A Systems, LLC. |
| | | */ |
| | | package org.opends.server.extensions; |
| | | |
| | |
| | | /** Size of the dgiest in bytes. */ |
| | | private static final int SHA256_LENGTH = 256 / 8; |
| | | |
| | | /** The message digest that will actually be used to generate the 256-bit SHA-2 hashes. */ |
| | | private MessageDigest messageDigest; |
| | | |
| | | /** The lock used to provide threadsafe access to the message digest. */ |
| | | private Object digestLock; |
| | | /** |
| | | * The message digests used to generate the 256-bit SHA-2 hashes. |
| | | * MessageDigest is not thread-safe, so a per-thread instance is used |
| | | * instead of a shared instance guarded by a lock: hashing under a global |
| | | * lock serializes all concurrent bind password verifications. |
| | | */ |
| | | private ThreadLocal<MessageDigest> messageDigest; |
| | | |
| | | /** The secure random number generator to use to generate the salt values. */ |
| | | private Random random; |
| | |
| | | { |
| | | try |
| | | { |
| | | messageDigest = |
| | | MessageDigest.getInstance(MESSAGE_DIGEST_ALGORITHM_SHA_256); |
| | | // Fail fast at initialization time if the algorithm is unavailable. |
| | | MessageDigest.getInstance(MESSAGE_DIGEST_ALGORITHM_SHA_256); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | |
| | | throw new InitializationException(message, e); |
| | | } |
| | | |
| | | digestLock = new Object(); |
| | | random = new Random(); |
| | | messageDigest = ThreadLocal.withInitial(() -> { |
| | | try |
| | | { |
| | | return MessageDigest.getInstance(MESSAGE_DIGEST_ALGORITHM_SHA_256); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | throw new IllegalStateException(e); |
| | | } |
| | | }); |
| | | random = new Random(); |
| | | } |
| | | |
| | | @Override |
| | |
| | | |
| | | byte[] digestBytes; |
| | | |
| | | synchronized (digestLock) |
| | | try |
| | | { |
| | | try |
| | | { |
| | | // Generate the salt and put in the plain+salt array. |
| | | random.nextBytes(saltBytes); |
| | | System.arraycopy(saltBytes,0, plainPlusSalt, plainBytesLength, |
| | | NUM_SALT_BYTES); |
| | | // Generate the salt and put in the plain+salt array. |
| | | random.nextBytes(saltBytes); |
| | | System.arraycopy(saltBytes,0, plainPlusSalt, plainBytesLength, |
| | | NUM_SALT_BYTES); |
| | | |
| | | // Create the hash from the concatenated value. |
| | | digestBytes = messageDigest.digest(plainPlusSalt); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | // Create the hash from the concatenated value. |
| | | digestBytes = messageDigest.get().digest(plainPlusSalt); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | |
| | | LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get( |
| | | CLASS_NAME, getExceptionMessage(e)); |
| | | throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(), |
| | | message, e); |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSalt, (byte) 0); |
| | | } |
| | | LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get( |
| | | CLASS_NAME, getExceptionMessage(e)); |
| | | throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(), |
| | | message, e); |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSalt, (byte) 0); |
| | | } |
| | | |
| | | // Append the salt to the hashed value and base64-the whole thing. |
| | |
| | | |
| | | byte[] digestBytes; |
| | | |
| | | synchronized (digestLock) |
| | | try |
| | | { |
| | | try |
| | | { |
| | | // Generate the salt and put in the plain+salt array. |
| | | random.nextBytes(saltBytes); |
| | | System.arraycopy(saltBytes,0, plainPlusSalt, plainBytesLength, |
| | | NUM_SALT_BYTES); |
| | | // Generate the salt and put in the plain+salt array. |
| | | random.nextBytes(saltBytes); |
| | | System.arraycopy(saltBytes,0, plainPlusSalt, plainBytesLength, |
| | | NUM_SALT_BYTES); |
| | | |
| | | // Create the hash from the concatenated value. |
| | | digestBytes = messageDigest.digest(plainPlusSalt); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | // Create the hash from the concatenated value. |
| | | digestBytes = messageDigest.get().digest(plainPlusSalt); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | |
| | | LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get( |
| | | CLASS_NAME, getExceptionMessage(e)); |
| | | throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(), |
| | | message, e); |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSalt, (byte) 0); |
| | | } |
| | | LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get( |
| | | CLASS_NAME, getExceptionMessage(e)); |
| | | throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(), |
| | | message, e); |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSalt, (byte) 0); |
| | | } |
| | | |
| | | // Append the salt to the hashed value and base64-the whole thing. |
| | |
| | | |
| | | byte[] userDigestBytes; |
| | | |
| | | synchronized (digestLock) |
| | | try |
| | | { |
| | | try |
| | | { |
| | | userDigestBytes = messageDigest.digest(plainPlusSalt); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | userDigestBytes = messageDigest.get().digest(plainPlusSalt); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | |
| | | return false; |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSalt, (byte) 0); |
| | | } |
| | | return false; |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSalt, (byte) 0); |
| | | } |
| | | |
| | | return Arrays.equals(digestBytes, userDigestBytes); |
| | |
| | | |
| | | byte[] digestBytes; |
| | | |
| | | synchronized (digestLock) |
| | | try |
| | | { |
| | | try |
| | | { |
| | | // Generate the salt and put in the plain+salt array. |
| | | random.nextBytes(saltBytes); |
| | | System.arraycopy(saltBytes,0, plainPlusSalt, plaintextLength, |
| | | NUM_SALT_BYTES); |
| | | // Generate the salt and put in the plain+salt array. |
| | | random.nextBytes(saltBytes); |
| | | System.arraycopy(saltBytes,0, plainPlusSalt, plaintextLength, |
| | | NUM_SALT_BYTES); |
| | | |
| | | // Create the hash from the concatenated value. |
| | | digestBytes = messageDigest.digest(plainPlusSalt); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | // Create the hash from the concatenated value. |
| | | digestBytes = messageDigest.get().digest(plainPlusSalt); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | |
| | | LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get( |
| | | CLASS_NAME, getExceptionMessage(e)); |
| | | throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(), |
| | | message, e); |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSalt, (byte) 0); |
| | | } |
| | | LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get( |
| | | CLASS_NAME, getExceptionMessage(e)); |
| | | throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(), |
| | | message, e); |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSalt, (byte) 0); |
| | | } |
| | | |
| | | // Encode and return the value. |
| | |
| | | System.arraycopy(saltBytes, 0, plainPlusSaltBytes, plainBytesLength, |
| | | saltBytes.length); |
| | | |
| | | synchronized (digestLock) |
| | | try |
| | | { |
| | | try |
| | | { |
| | | return Arrays.equals(digestBytes, |
| | | messageDigest.digest(plainPlusSaltBytes)); |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSaltBytes, (byte) 0); |
| | | } |
| | | return Arrays.equals(digestBytes, |
| | | messageDigest.get().digest(plainPlusSaltBytes)); |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSaltBytes, (byte) 0); |
| | | } |
| | | } |
| | | |
| | |
| | | * |
| | | * Copyright 2006-2008 Sun Microsystems, Inc. |
| | | * Portions Copyright 2010-2016 ForgeRock AS. |
| | | * Portions Copyright 2026 3A Systems, LLC. |
| | | */ |
| | | package org.opends.server.extensions; |
| | | |
| | |
| | | /** The size of the digest in bytes. */ |
| | | private static final int SHA384_LENGTH = 384 / 8; |
| | | |
| | | /** The message digest that will actually be used to generate the 384-bit SHA-2 hashes. */ |
| | | private MessageDigest messageDigest; |
| | | |
| | | /** The lock used to provide threadsafe access to the message digest. */ |
| | | private Object digestLock; |
| | | /** |
| | | * The message digests used to generate the 384-bit SHA-2 hashes. |
| | | * MessageDigest is not thread-safe, so a per-thread instance is used |
| | | * instead of a shared instance guarded by a lock: hashing under a global |
| | | * lock serializes all concurrent bind password verifications. |
| | | */ |
| | | private ThreadLocal<MessageDigest> messageDigest; |
| | | |
| | | /** The secure random number generator to use to generate the salt values. */ |
| | | private Random random; |
| | |
| | | { |
| | | try |
| | | { |
| | | messageDigest = |
| | | MessageDigest.getInstance(MESSAGE_DIGEST_ALGORITHM_SHA_384); |
| | | // Fail fast at initialization time if the algorithm is unavailable. |
| | | MessageDigest.getInstance(MESSAGE_DIGEST_ALGORITHM_SHA_384); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | |
| | | throw new InitializationException(message, e); |
| | | } |
| | | |
| | | digestLock = new Object(); |
| | | random = new Random(); |
| | | messageDigest = ThreadLocal.withInitial(() -> { |
| | | try |
| | | { |
| | | return MessageDigest.getInstance(MESSAGE_DIGEST_ALGORITHM_SHA_384); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | throw new IllegalStateException(e); |
| | | } |
| | | }); |
| | | random = new Random(); |
| | | } |
| | | |
| | | @Override |
| | |
| | | |
| | | byte[] digestBytes; |
| | | |
| | | synchronized (digestLock) |
| | | try |
| | | { |
| | | try |
| | | { |
| | | // Generate the salt and put in the plain+salt array. |
| | | random.nextBytes(saltBytes); |
| | | System.arraycopy(saltBytes,0, plainPlusSalt, plainBytesLength, |
| | | NUM_SALT_BYTES); |
| | | // Generate the salt and put in the plain+salt array. |
| | | random.nextBytes(saltBytes); |
| | | System.arraycopy(saltBytes,0, plainPlusSalt, plainBytesLength, |
| | | NUM_SALT_BYTES); |
| | | |
| | | // Create the hash from the concatenated value. |
| | | digestBytes = messageDigest.digest(plainPlusSalt); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | // Create the hash from the concatenated value. |
| | | digestBytes = messageDigest.get().digest(plainPlusSalt); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | |
| | | LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get( |
| | | CLASS_NAME, getExceptionMessage(e)); |
| | | throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(), |
| | | message, e); |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSalt, (byte) 0); |
| | | } |
| | | LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get( |
| | | CLASS_NAME, getExceptionMessage(e)); |
| | | throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(), |
| | | message, e); |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSalt, (byte) 0); |
| | | } |
| | | |
| | | // Append the salt to the hashed value and base64-the whole thing. |
| | |
| | | |
| | | byte[] digestBytes; |
| | | |
| | | synchronized (digestLock) |
| | | try |
| | | { |
| | | try |
| | | { |
| | | // Generate the salt and put in the plain+salt array. |
| | | random.nextBytes(saltBytes); |
| | | System.arraycopy(saltBytes,0, plainPlusSalt, plainBytesLength, |
| | | NUM_SALT_BYTES); |
| | | // Generate the salt and put in the plain+salt array. |
| | | random.nextBytes(saltBytes); |
| | | System.arraycopy(saltBytes,0, plainPlusSalt, plainBytesLength, |
| | | NUM_SALT_BYTES); |
| | | |
| | | // Create the hash from the concatenated value. |
| | | digestBytes = messageDigest.digest(plainPlusSalt); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | // Create the hash from the concatenated value. |
| | | digestBytes = messageDigest.get().digest(plainPlusSalt); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | |
| | | LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get( |
| | | CLASS_NAME, getExceptionMessage(e)); |
| | | throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(), |
| | | message, e); |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSalt, (byte) 0); |
| | | } |
| | | LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get( |
| | | CLASS_NAME, getExceptionMessage(e)); |
| | | throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(), |
| | | message, e); |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSalt, (byte) 0); |
| | | } |
| | | |
| | | // Append the salt to the hashed value and base64-the whole thing. |
| | |
| | | |
| | | byte[] userDigestBytes; |
| | | |
| | | synchronized (digestLock) |
| | | try |
| | | { |
| | | try |
| | | { |
| | | userDigestBytes = messageDigest.digest(plainPlusSalt); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | userDigestBytes = messageDigest.get().digest(plainPlusSalt); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | |
| | | return false; |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSalt, (byte) 0); |
| | | } |
| | | return false; |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSalt, (byte) 0); |
| | | } |
| | | |
| | | return Arrays.equals(digestBytes, userDigestBytes); |
| | |
| | | |
| | | byte[] digestBytes; |
| | | |
| | | synchronized (digestLock) |
| | | try |
| | | { |
| | | try |
| | | { |
| | | // Generate the salt and put in the plain+salt array. |
| | | random.nextBytes(saltBytes); |
| | | System.arraycopy(saltBytes,0, plainPlusSalt, plaintextLength, |
| | | NUM_SALT_BYTES); |
| | | // Generate the salt and put in the plain+salt array. |
| | | random.nextBytes(saltBytes); |
| | | System.arraycopy(saltBytes,0, plainPlusSalt, plaintextLength, |
| | | NUM_SALT_BYTES); |
| | | |
| | | // Create the hash from the concatenated value. |
| | | digestBytes = messageDigest.digest(plainPlusSalt); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | // Create the hash from the concatenated value. |
| | | digestBytes = messageDigest.get().digest(plainPlusSalt); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | |
| | | LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get( |
| | | CLASS_NAME, getExceptionMessage(e)); |
| | | throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(), |
| | | message, e); |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSalt, (byte) 0); |
| | | } |
| | | LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get( |
| | | CLASS_NAME, getExceptionMessage(e)); |
| | | throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(), |
| | | message, e); |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSalt, (byte) 0); |
| | | } |
| | | |
| | | // Encode and return the value. |
| | |
| | | System.arraycopy(saltBytes, 0, plainPlusSaltBytes, plainBytesLength, |
| | | saltBytes.length); |
| | | |
| | | synchronized (digestLock) |
| | | try |
| | | { |
| | | try |
| | | { |
| | | return Arrays.equals(digestBytes, |
| | | messageDigest.digest(plainPlusSaltBytes)); |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSaltBytes, (byte) 0); |
| | | } |
| | | return Arrays.equals(digestBytes, |
| | | messageDigest.get().digest(plainPlusSaltBytes)); |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSaltBytes, (byte) 0); |
| | | } |
| | | } |
| | | |
| | |
| | | * |
| | | * Copyright 2006-2008 Sun Microsystems, Inc. |
| | | * Portions Copyright 2010-2016 ForgeRock AS. |
| | | * Portions Copyrighted 2026 3A Systems, LLC. |
| | | * Portions Copyright 2026 3A Systems, LLC. |
| | | */ |
| | | package org.opends.server.extensions; |
| | | |
| | |
| | | /** The size of the digest in bytes. */ |
| | | private static final int SHA512_LENGTH = 512 / 8; |
| | | |
| | | /** The message digest that will actually be used to generate the 512-bit SHA-2 hashes. */ |
| | | private MessageDigest messageDigest; |
| | | |
| | | /** The lock used to provide threadsafe access to the message digest. */ |
| | | private Object digestLock; |
| | | /** |
| | | * The message digests used to generate the 512-bit SHA-2 hashes. |
| | | * MessageDigest is not thread-safe, so a per-thread instance is used |
| | | * instead of a shared instance guarded by a lock: hashing under a global |
| | | * lock serializes all concurrent bind password verifications. |
| | | */ |
| | | private ThreadLocal<MessageDigest> messageDigest; |
| | | |
| | | /** The secure random number generator to use to generate the salt values. */ |
| | | private SecureRandom random; |
| | |
| | | { |
| | | try |
| | | { |
| | | messageDigest = |
| | | MessageDigest.getInstance(MESSAGE_DIGEST_ALGORITHM_SHA_512); |
| | | // Fail fast at initialization time if the algorithm is unavailable. |
| | | MessageDigest.getInstance(MESSAGE_DIGEST_ALGORITHM_SHA_512); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | |
| | | throw new InitializationException(message, e); |
| | | } |
| | | |
| | | digestLock = new Object(); |
| | | random = new SecureRandom(); |
| | | messageDigest = ThreadLocal.withInitial(() -> { |
| | | try |
| | | { |
| | | return MessageDigest.getInstance(MESSAGE_DIGEST_ALGORITHM_SHA_512); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | throw new IllegalStateException(e); |
| | | } |
| | | }); |
| | | random = new SecureRandom(); |
| | | } |
| | | |
| | | @Override |
| | |
| | | |
| | | byte[] digestBytes; |
| | | |
| | | synchronized (digestLock) |
| | | try |
| | | { |
| | | try |
| | | { |
| | | // Generate the salt and put in the plain+salt array. |
| | | random.nextBytes(saltBytes); |
| | | System.arraycopy(saltBytes,0, plainPlusSalt, plainBytesLength, |
| | | NUM_SALT_BYTES); |
| | | // Generate the salt and put in the plain+salt array. |
| | | random.nextBytes(saltBytes); |
| | | System.arraycopy(saltBytes,0, plainPlusSalt, plainBytesLength, |
| | | NUM_SALT_BYTES); |
| | | |
| | | // Create the hash from the concatenated value. |
| | | digestBytes = messageDigest.digest(plainPlusSalt); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | // Create the hash from the concatenated value. |
| | | digestBytes = messageDigest.get().digest(plainPlusSalt); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | |
| | | LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get( |
| | | CLASS_NAME, getExceptionMessage(e)); |
| | | throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(), |
| | | message, e); |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSalt, (byte) 0); |
| | | } |
| | | LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get( |
| | | CLASS_NAME, getExceptionMessage(e)); |
| | | throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(), |
| | | message, e); |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSalt, (byte) 0); |
| | | } |
| | | |
| | | // Append the salt to the hashed value and base64-the whole thing. |
| | |
| | | |
| | | byte[] digestBytes; |
| | | |
| | | synchronized (digestLock) |
| | | try |
| | | { |
| | | try |
| | | { |
| | | // Generate the salt and put in the plain+salt array. |
| | | random.nextBytes(saltBytes); |
| | | System.arraycopy(saltBytes,0, plainPlusSalt, plainBytesLength, |
| | | NUM_SALT_BYTES); |
| | | // Generate the salt and put in the plain+salt array. |
| | | random.nextBytes(saltBytes); |
| | | System.arraycopy(saltBytes,0, plainPlusSalt, plainBytesLength, |
| | | NUM_SALT_BYTES); |
| | | |
| | | // Create the hash from the concatenated value. |
| | | digestBytes = messageDigest.digest(plainPlusSalt); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | // Create the hash from the concatenated value. |
| | | digestBytes = messageDigest.get().digest(plainPlusSalt); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | |
| | | LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get( |
| | | CLASS_NAME, getExceptionMessage(e)); |
| | | throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(), |
| | | message, e); |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSalt, (byte) 0); |
| | | } |
| | | LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get( |
| | | CLASS_NAME, getExceptionMessage(e)); |
| | | throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(), |
| | | message, e); |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSalt, (byte) 0); |
| | | } |
| | | |
| | | // Append the salt to the hashed value and base64-the whole thing. |
| | |
| | | |
| | | byte[] userDigestBytes; |
| | | |
| | | synchronized (digestLock) |
| | | try |
| | | { |
| | | try |
| | | { |
| | | userDigestBytes = messageDigest.digest(plainPlusSalt); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | userDigestBytes = messageDigest.get().digest(plainPlusSalt); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | |
| | | return false; |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSalt, (byte) 0); |
| | | } |
| | | return false; |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSalt, (byte) 0); |
| | | } |
| | | |
| | | return Arrays.equals(digestBytes, userDigestBytes); |
| | |
| | | |
| | | byte[] digestBytes; |
| | | |
| | | synchronized (digestLock) |
| | | try |
| | | { |
| | | try |
| | | { |
| | | // Generate the salt and put in the plain+salt array. |
| | | random.nextBytes(saltBytes); |
| | | System.arraycopy(saltBytes,0, plainPlusSalt, plaintextLength, |
| | | NUM_SALT_BYTES); |
| | | // Generate the salt and put in the plain+salt array. |
| | | random.nextBytes(saltBytes); |
| | | System.arraycopy(saltBytes,0, plainPlusSalt, plaintextLength, |
| | | NUM_SALT_BYTES); |
| | | |
| | | // Create the hash from the concatenated value. |
| | | digestBytes = messageDigest.digest(plainPlusSalt); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | // Create the hash from the concatenated value. |
| | | digestBytes = messageDigest.get().digest(plainPlusSalt); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | |
| | | LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get( |
| | | CLASS_NAME, getExceptionMessage(e)); |
| | | throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(), |
| | | message, e); |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSalt, (byte) 0); |
| | | } |
| | | LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get( |
| | | CLASS_NAME, getExceptionMessage(e)); |
| | | throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(), |
| | | message, e); |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSalt, (byte) 0); |
| | | } |
| | | |
| | | // Encode and return the value. |
| | |
| | | System.arraycopy(saltBytes, 0, plainPlusSaltBytes, plainBytesLength, |
| | | saltBytes.length); |
| | | |
| | | synchronized (digestLock) |
| | | try |
| | | { |
| | | try |
| | | { |
| | | return Arrays.equals(digestBytes, |
| | | messageDigest.digest(plainPlusSaltBytes)); |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSaltBytes, (byte) 0); |
| | | } |
| | | return Arrays.equals(digestBytes, |
| | | messageDigest.get().digest(plainPlusSaltBytes)); |
| | | } |
| | | finally |
| | | { |
| | | Arrays.fill(plainPlusSaltBytes, (byte) 0); |
| | | } |
| | | } |
| | | |
| | |
| | | * |
| | | * Copyright 2008 Sun Microsystems, Inc. |
| | | * Portions Copyright 2015 ForgeRock AS. |
| | | * Portions Copyright 2026 3A Systems, LLC |
| | | */ |
| | | /* |
| | | * Copyright 2005 Sun Microsystems, Inc. All rights reserved. |
| | |
| | | int _iobuf[] = new int[16]; |
| | | } |
| | | |
| | | private final SubCrypt _crypt; |
| | | /** |
| | | * The working state of the algorithm. setkey(), encrypt() and _crypt() all |
| | | * scribble on these buffers (and _crypt() returns a reference to _iobuf), |
| | | * so a per-thread instance is used instead of a shared instance guarded by |
| | | * a lock: encrypting under a global lock serializes all concurrent {CRYPT} |
| | | * password operations. |
| | | */ |
| | | private final ThreadLocal<SubCrypt> _crypt = ThreadLocal.withInitial(() -> { |
| | | SubCrypt c = new SubCrypt(); |
| | | copy(e, c._E); |
| | | return c; |
| | | }); |
| | | |
| | | /** |
| | | * Constructor. |
| | | */ |
| | | public Crypt() { |
| | | _crypt = new SubCrypt(); |
| | | |
| | | copy(e, _crypt._E); |
| | | } |
| | | |
| | | private void copy(byte[] src, int[] dest) { |
| | | private static void copy(byte[] src, int[] dest) { |
| | | for (int i = 0; i < dest.length; i++) { |
| | | dest[i] = src[i]; |
| | | } |
| | |
| | | */ |
| | | private void setkey(int[] key) |
| | | { |
| | | SubCrypt _c = _crypt; |
| | | SubCrypt _c = _crypt.get(); |
| | | |
| | | /* |
| | | * if (_c == null) { _cryptinit(); _c = __crypt; } |
| | |
| | | */ |
| | | private final void encrypt(int block[], int edflag) |
| | | { |
| | | SubCrypt _c = _crypt; |
| | | SubCrypt _c = _crypt.get(); |
| | | |
| | | /* |
| | | * First, permute the bits in the input |
| | |
| | | } |
| | | } |
| | | |
| | | private Object digestLock = new Object(); |
| | | |
| | | /** |
| | | * Encode the supplied password in unix crypt form with the provided |
| | | * salt. |
| | |
| | | */ |
| | | public byte[] crypt(byte[] pw, byte[] salt) |
| | | { |
| | | int[] r; |
| | | synchronized (digestLock) |
| | | { |
| | | r = _crypt(pw, salt); |
| | | } |
| | | int[] r = _crypt(pw, salt); |
| | | |
| | | //TODO: crypt always returns same size array? So don't mess |
| | | // around calculating the number of zeros at the end. |
| | |
| | | |
| | | private int[] _crypt(byte[] pw, byte[] salt) |
| | | { |
| | | SubCrypt _c = _crypt; |
| | | SubCrypt _c = _crypt.get(); |
| | | |
| | | Arrays.fill(_c._ablock, 0); |
| | | |