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}