2727import org .apache .commons .crypto .cipher .CryptoCipherFactory ;
2828import org .apache .hadoop .conf .Configuration ;
2929import org .apache .hadoop .hbase .HConstants ;
30- import org .apache .hadoop .hbase .client .ColumnFamilyDescriptor ;
3130import org .apache .hadoop .hbase .io .crypto .Cipher ;
3231import org .apache .hadoop .hbase .io .crypto .Encryption ;
3332import org .apache .hadoop .hbase .io .crypto .aes .CryptoAES ;
@@ -80,6 +79,21 @@ public static byte[] wrapKey(Configuration conf, byte[] key, String algorithm)
8079 * @return the encrypted key bytes
8180 */
8281 public static byte [] wrapKey (Configuration conf , String subject , Key key ) throws IOException {
82+ return wrapKey (conf , subject , key , null );
83+ }
84+
85+ /**
86+ * Protect a key by encrypting it with the secret key of the given subject or kek. The
87+ * configuration must be set up correctly for key alias resolution. Only one of the
88+ * {@code subject} or {@code kek} needs to be specified and the other one can be {@code null}.
89+ * @param conf configuration
90+ * @param subject subject key alias
91+ * @param key the key
92+ * @param kek the key encryption key
93+ * @return the encrypted key bytes
94+ */
95+ public static byte [] wrapKey (Configuration conf , String subject , Key key , Key kek )
96+ throws IOException {
8397 // Wrap the key with the configured encryption algorithm.
8498 String algorithm = conf .get (HConstants .CRYPTO_KEY_ALGORITHM_CONF_KEY , HConstants .CIPHER_AES );
8599 Cipher cipher = Encryption .getCipher (conf , algorithm );
@@ -100,8 +114,12 @@ public static byte[] wrapKey(Configuration conf, String subject, Key key) throws
100114 builder
101115 .setHash (UnsafeByteOperations .unsafeWrap (Encryption .computeCryptoKeyHash (conf , keyBytes )));
102116 ByteArrayOutputStream out = new ByteArrayOutputStream ();
103- Encryption .encryptWithSubjectKey (out , new ByteArrayInputStream (keyBytes ), subject , conf , cipher ,
104- iv );
117+ if (kek != null ) {
118+ Encryption .encryptWithGivenKey (kek , out , new ByteArrayInputStream (keyBytes ), cipher , iv );
119+ } else {
120+ Encryption .encryptWithSubjectKey (out , new ByteArrayInputStream (keyBytes ), subject , conf ,
121+ cipher , iv );
122+ }
105123 builder .setData (UnsafeByteOperations .unsafeWrap (out .toByteArray ()));
106124 // Build and return the protobuf message
107125 out .reset ();
@@ -118,6 +136,21 @@ public static byte[] wrapKey(Configuration conf, String subject, Key key) throws
118136 * @return the raw key bytes
119137 */
120138 public static Key unwrapKey (Configuration conf , String subject , byte [] value )
139+ throws IOException , KeyException {
140+ return unwrapKey (conf , subject , value , null );
141+ }
142+
143+ /**
144+ * Unwrap a key by decrypting it with the secret key of the given subject. The configuration must
145+ * be set up correctly for key alias resolution. Only one of the {@code subject} or {@code kek}
146+ * needs to be specified and the other one can be {@code null}.
147+ * @param conf configuration
148+ * @param subject subject key alias
149+ * @param value the encrypted key bytes
150+ * @param kek the key encryption key
151+ * @return the raw key bytes
152+ */
153+ public static Key unwrapKey (Configuration conf , String subject , byte [] value , Key kek )
121154 throws IOException , KeyException {
122155 EncryptionProtos .WrappedKey wrappedKey =
123156 EncryptionProtos .WrappedKey .parser ().parseDelimitedFrom (new ByteArrayInputStream (value ));
@@ -126,11 +159,12 @@ public static Key unwrapKey(Configuration conf, String subject, byte[] value)
126159 if (cipher == null ) {
127160 throw new RuntimeException ("Cipher '" + algorithm + "' not available" );
128161 }
129- return getUnwrapKey (conf , subject , wrappedKey , cipher );
162+ return getUnwrapKey (conf , subject , wrappedKey , cipher , kek );
130163 }
131164
132165 private static Key getUnwrapKey (Configuration conf , String subject ,
133- EncryptionProtos .WrappedKey wrappedKey , Cipher cipher ) throws IOException , KeyException {
166+ EncryptionProtos .WrappedKey wrappedKey , Cipher cipher , Key kek )
167+ throws IOException , KeyException {
134168 String configuredHashAlgorithm = Encryption .getConfiguredHashAlgorithm (conf );
135169 String wrappedHashAlgorithm = wrappedKey .getHashAlgorithm ().trim ();
136170 if (!configuredHashAlgorithm .equalsIgnoreCase (wrappedHashAlgorithm )) {
@@ -143,8 +177,13 @@ private static Key getUnwrapKey(Configuration conf, String subject,
143177 }
144178 ByteArrayOutputStream out = new ByteArrayOutputStream ();
145179 byte [] iv = wrappedKey .hasIv () ? wrappedKey .getIv ().toByteArray () : null ;
146- Encryption .decryptWithSubjectKey (out , wrappedKey .getData ().newInput (), wrappedKey .getLength (),
147- subject , conf , cipher , iv );
180+ if (kek != null ) {
181+ Encryption .decryptWithGivenKey (kek , out , wrappedKey .getData ().newInput (),
182+ wrappedKey .getLength (), cipher , iv );
183+ } else {
184+ Encryption .decryptWithSubjectKey (out , wrappedKey .getData ().newInput (), wrappedKey .getLength (),
185+ subject , conf , cipher , iv );
186+ }
148187 byte [] keyBytes = out .toByteArray ();
149188 if (wrappedKey .hasHash ()) {
150189 if (
@@ -176,58 +215,7 @@ public static Key unwrapWALKey(Configuration conf, String subject, byte[] value)
176215 if (cipher == null ) {
177216 throw new RuntimeException ("Cipher '" + algorithm + "' not available" );
178217 }
179- return getUnwrapKey (conf , subject , wrappedKey , cipher );
180- }
181-
182- /**
183- * Helper to create an encyption context.
184- * @param conf The current configuration.
185- * @param family The current column descriptor.
186- * @return The created encryption context.
187- * @throws IOException if an encryption key for the column cannot be unwrapped
188- * @throws IllegalStateException in case of encryption related configuration errors
189- */
190- public static Encryption .Context createEncryptionContext (Configuration conf ,
191- ColumnFamilyDescriptor family ) throws IOException {
192- Encryption .Context cryptoContext = Encryption .Context .NONE ;
193- String cipherName = family .getEncryptionType ();
194- if (cipherName != null ) {
195- if (!Encryption .isEncryptionEnabled (conf )) {
196- throw new IllegalStateException ("Encryption for family '" + family .getNameAsString ()
197- + "' configured with type '" + cipherName + "' but the encryption feature is disabled" );
198- }
199- Cipher cipher ;
200- Key key ;
201- byte [] keyBytes = family .getEncryptionKey ();
202- if (keyBytes != null ) {
203- // Family provides specific key material
204- key = unwrapKey (conf , keyBytes );
205- // Use the algorithm the key wants
206- cipher = Encryption .getCipher (conf , key .getAlgorithm ());
207- if (cipher == null ) {
208- throw new IllegalStateException ("Cipher '" + key .getAlgorithm () + "' is not available" );
209- }
210- // Fail if misconfigured
211- // We use the encryption type specified in the column schema as a sanity check on
212- // what the wrapped key is telling us
213- if (!cipher .getName ().equalsIgnoreCase (cipherName )) {
214- throw new IllegalStateException (
215- "Encryption for family '" + family .getNameAsString () + "' configured with type '"
216- + cipherName + "' but key specifies algorithm '" + cipher .getName () + "'" );
217- }
218- } else {
219- // Family does not provide key material, create a random key
220- cipher = Encryption .getCipher (conf , cipherName );
221- if (cipher == null ) {
222- throw new IllegalStateException ("Cipher '" + cipherName + "' is not available" );
223- }
224- key = cipher .getRandomKey ();
225- }
226- cryptoContext = Encryption .newContext (conf );
227- cryptoContext .setCipher (cipher );
228- cryptoContext .setKey (key );
229- }
230- return cryptoContext ;
218+ return getUnwrapKey (conf , subject , wrappedKey , cipher , null );
231219 }
232220
233221 /**
0 commit comments