001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2016 ForgeRock AS.
015 */
016package org.opends.server.crypto;
017
018import net.jcip.annotations.Immutable;
019import org.forgerock.opendj.ldap.ByteSequence;
020import org.forgerock.opendj.ldap.ByteString;
021import org.forgerock.opendj.ldap.DecodeException;
022import org.opends.server.types.CryptoManager;
023import org.opends.server.types.CryptoManagerException;
024
025import javax.crypto.CipherInputStream;
026import javax.crypto.CipherOutputStream;
027import java.io.InputStream;
028import java.io.OutputStream;
029import java.security.GeneralSecurityException;
030import java.security.NoSuchAlgorithmException;
031
032import static org.opends.messages.CoreMessages.*;
033
034/** Defines cipher transformation and hash algorithm for cryptographic related operations. */
035public class CryptoSuite
036{
037  /** Cipher specific settings that can change at runtime. */
038  @Immutable
039  private static final class CipherInfo
040  {
041    private final String cipherTransformation;
042    private final int cipherKeyLength;
043    private final boolean encrypt;
044
045    CipherInfo(String cipherTransformation, int cipherKeyLength, boolean encrypt)
046    {
047      this.cipherTransformation = cipherTransformation;
048      this.cipherKeyLength = cipherKeyLength;
049      this.encrypt = encrypt;
050    }
051  }
052
053  private volatile CipherInfo cipherInfo;
054  private final CryptoManager cryptoManager;
055
056  /**
057   * Declares a new CryptoSuite with provided parameters.
058   * @param cryptoManager the CryptoManager to use for cryptographic operations
059   * @param cipherTransformation the initial cipher transformation
060   * @param cipherKeyLength the initial key length for the cipher
061   * @param encrypt if the user of the crypto suite needs encryption
062   */
063  public CryptoSuite(CryptoManager cryptoManager, String cipherTransformation, int cipherKeyLength, boolean encrypt)
064  {
065    this.cryptoManager = cryptoManager;
066    this.cipherInfo = new CipherInfo(cipherTransformation, cipherKeyLength, encrypt);
067  }
068
069  /**
070   * Set new cipher and enable parameters for the crypto suite.
071   *
072   * @param cipherTransformation the new cipher transformation
073   * @param cipherKeyLength the new key length
074   * @param enabled true if the user of the crypto suite needs encryption
075   */
076  public void newParameters(String cipherTransformation, int cipherKeyLength, boolean enabled)
077  {
078    cipherInfo = new CipherInfo(cipherTransformation, cipherKeyLength, enabled);
079  }
080
081  /**
082   * Decrypts data using the key specified in the prologue.
083   *
084   * @param data the cipher-text to be decrypted (contains prologue)
085   * @return a byte array with the clear-text
086   * @throws GeneralSecurityException if a problem occurs while decrypting the data
087   * @throws CryptoManagerException if a problem occurs during cipher initialization
088   */
089  public byte[] decrypt(byte[] data) throws GeneralSecurityException, CryptoManagerException
090  {
091    return cryptoManager.decrypt(data);
092  }
093
094  /**
095   * Encrypts data with the configured cipher transformation and key length.
096   *
097   * @param data the clear-text data to encrypt
098   * @return a byte array with a prologue containing the key identifier followed by cipher-text
099   * @throws GeneralSecurityException if a problem occurs while encrypting the data
100   * @throws CryptoManagerException if a problem occurs during cipher initialization
101   */
102  public byte[] encrypt(byte[] data) throws GeneralSecurityException, CryptoManagerException
103  {
104    CipherInfo currentCipher = cipherInfo;
105    return cryptoManager.encrypt(currentCipher.cipherTransformation, currentCipher.cipherKeyLength, data);
106  }
107
108  /**
109   * Returns a {@link CipherOutputStream} for encrypting through a sequence of
110   * OutputStreams.
111   *
112   * @param os the up-link OutputStream
113   * @return a {@link CipherOutputStream} for encrypting through a sequence of
114   * OutputStreams
115   * @throws CryptoManagerException if a problem occurs during cipher initialization
116   */
117  public CipherOutputStream getCipherOutputStream(OutputStream os) throws CryptoManagerException
118  {
119    CipherInfo currentCipher = cipherInfo;
120    return cryptoManager.getCipherOutputStream(currentCipher.cipherTransformation, currentCipher.cipherKeyLength, os);
121  }
122
123  /**
124   * Returns a {@link CipherInputStream} for decrypting through a sequence of InputStreams.
125   *
126   * @param is the up-link InputStream
127   * @return a {@link CipherInputStream} for decrypting through a sequence of InputStreams.
128   * @throws CryptoManagerException if a problem occurs during cipher initialization
129   */
130  public CipherInputStream getCipherInputStream(InputStream is) throws CryptoManagerException
131  {
132    return cryptoManager.getCipherInputStream(is);
133  }
134
135  /**
136   * Returns a ByteString of 6 bytes hash of the data.
137   *
138   * @param data a ByteSequence containing the input data to be hashed
139   * @return a ByteString of 6 bytes hash of the data.
140   * @throws DecodeException if digest of the data cannot be computed
141   */
142  public ByteString hash48(ByteSequence data) throws DecodeException
143  {
144    try
145    {
146      byte[] hash = cryptoManager.digest("SHA-1", data.toByteArray());
147      return ByteString.valueOfBytes(hash, 0, 6);
148    }
149    catch (NoSuchAlgorithmException e)
150    {
151      throw DecodeException.error(ERR_CANNOT_HASH_DATA.get());
152    }
153  }
154
155  /**
156   * Returns whether the user of the crypto suite needs encryption.
157   *
158   * @return true if the user of the crypto suite needs encryption
159   */
160  public boolean isEncrypted()
161  {
162    return cipherInfo.encrypt;
163  }
164
165  @Override
166  public String toString()
167  {
168    StringBuilder builder = new StringBuilder();
169    CipherInfo currentCipher = cipherInfo;
170    builder.append("CryptoSuite(cipherTransformation=");
171    builder.append(currentCipher.cipherTransformation);
172    builder.append(", keyLength=");
173    builder.append(currentCipher.cipherKeyLength);
174    builder.append(", encrypt=");
175    builder.append(currentCipher.encrypt);
176    builder.append(")");
177    return builder.toString();
178  }
179}