001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019package org.apache.hadoop.hbase.io.crypto.aes; 020 021import java.io.IOException; 022import java.security.InvalidAlgorithmParameterException; 023import java.security.InvalidKeyException; 024import java.security.NoSuchAlgorithmException; 025import java.util.Arrays; 026import java.util.Properties; 027import javax.crypto.Cipher; 028import javax.crypto.Mac; 029import javax.crypto.SecretKey; 030import javax.crypto.ShortBufferException; 031import javax.crypto.spec.IvParameterSpec; 032import javax.crypto.spec.SecretKeySpec; 033import javax.security.sasl.SaslException; 034 035import org.apache.commons.crypto.cipher.CryptoCipher; 036import org.apache.commons.crypto.utils.Utils; 037import org.apache.yetus.audience.InterfaceAudience; 038import org.apache.yetus.audience.InterfaceStability; 039 040/** 041 * AES encryption and decryption. 042 */ 043@InterfaceAudience.Private 044@InterfaceStability.Evolving 045public class CryptoAES { 046 047 private final CryptoCipher encryptor; 048 private final CryptoCipher decryptor; 049 050 private final Integrity integrity; 051 052 public CryptoAES(String transformation, Properties properties, 053 byte[] inKey, byte[] outKey, byte[] inIv, byte[] outIv) throws IOException { 054 checkTransformation(transformation); 055 // encryptor 056 encryptor = Utils.getCipherInstance(transformation, properties); 057 try { 058 SecretKeySpec outKEYSpec = new SecretKeySpec(outKey, "AES"); 059 IvParameterSpec outIVSpec = new IvParameterSpec(outIv); 060 encryptor.init(Cipher.ENCRYPT_MODE, outKEYSpec, outIVSpec); 061 } catch (InvalidKeyException | InvalidAlgorithmParameterException e) { 062 throw new IOException("Failed to initialize encryptor", e); 063 } 064 065 // decryptor 066 decryptor = Utils.getCipherInstance(transformation, properties); 067 try { 068 SecretKeySpec inKEYSpec = new SecretKeySpec(inKey, "AES"); 069 IvParameterSpec inIVSpec = new IvParameterSpec(inIv); 070 decryptor.init(Cipher.DECRYPT_MODE, inKEYSpec, inIVSpec); 071 } catch (InvalidKeyException | InvalidAlgorithmParameterException e) { 072 throw new IOException("Failed to initialize decryptor", e); 073 } 074 075 integrity = new Integrity(outKey, inKey); 076 } 077 078 /** 079 * Encrypts input data. The result composes of (msg, padding if needed, mac) and sequence num. 080 * @param data the input byte array 081 * @param offset the offset in input where the input starts 082 * @param len the input length 083 * @return the new encrypted byte array. 084 * @throws SaslException if error happens 085 */ 086 public byte[] wrap(byte[] data, int offset, int len) throws SaslException { 087 // mac 088 byte[] mac = integrity.getHMAC(data, offset, len); 089 integrity.incMySeqNum(); 090 091 // encrypt 092 byte[] encrypted = new byte[len + 10]; 093 try { 094 int n = encryptor.update(data, offset, len, encrypted, 0); 095 encryptor.update(mac, 0, 10, encrypted, n); 096 } catch (ShortBufferException sbe) { 097 // this should not happen 098 throw new SaslException("Error happens during encrypt data", sbe); 099 } 100 101 // append seqNum used for mac 102 byte[] wrapped = new byte[encrypted.length + 4]; 103 System.arraycopy(encrypted, 0, wrapped, 0, encrypted.length); 104 System.arraycopy(integrity.getSeqNum(), 0, wrapped, encrypted.length, 4); 105 106 return wrapped; 107 } 108 109 /** 110 * Decrypts input data. The input composes of (msg, padding if needed, mac) and sequence num. 111 * The result is msg. 112 * @param data the input byte array 113 * @param offset the offset in input where the input starts 114 * @param len the input length 115 * @return the new decrypted byte array. 116 * @throws SaslException if error happens 117 */ 118 public byte[] unwrap(byte[] data, int offset, int len) throws SaslException { 119 // get plaintext and seqNum 120 byte[] decrypted = new byte[len - 4]; 121 byte[] peerSeqNum = new byte[4]; 122 try { 123 decryptor.update(data, offset, len - 4, decrypted, 0); 124 } catch (ShortBufferException sbe) { 125 // this should not happen 126 throw new SaslException("Error happens during decrypt data", sbe); 127 } 128 System.arraycopy(data, offset + decrypted.length, peerSeqNum, 0, 4); 129 130 // get msg and mac 131 byte[] msg = new byte[decrypted.length - 10]; 132 byte[] mac = new byte[10]; 133 System.arraycopy(decrypted, 0, msg, 0, msg.length); 134 System.arraycopy(decrypted, msg.length, mac, 0, 10); 135 136 // check mac integrity and msg sequence 137 if (!integrity.compareHMAC(mac, peerSeqNum, msg, 0, msg.length)) { 138 throw new SaslException("Unmatched MAC"); 139 } 140 if (!integrity.comparePeerSeqNum(peerSeqNum)) { 141 throw new SaslException("Out of order sequencing of messages. Got: " + integrity.byteToInt 142 (peerSeqNum) + " Expected: " + integrity.peerSeqNum); 143 } 144 integrity.incPeerSeqNum(); 145 146 return msg; 147 } 148 149 private void checkTransformation(String transformation) throws IOException { 150 if ("AES/CTR/NoPadding".equalsIgnoreCase(transformation)) { 151 return; 152 } 153 throw new IOException("AES cipher transformation is not supported: " + transformation); 154 } 155 156 /** 157 * Helper class for providing integrity protection. 158 */ 159 private static class Integrity { 160 161 private int mySeqNum = 0; 162 private int peerSeqNum = 0; 163 private byte[] seqNum = new byte[4]; 164 165 private byte[] myKey; 166 private byte[] peerKey; 167 168 Integrity(byte[] outKey, byte[] inKey) throws IOException { 169 myKey = outKey; 170 peerKey = inKey; 171 } 172 173 byte[] getHMAC(byte[] msg, int start, int len) throws SaslException { 174 intToByte(mySeqNum); 175 return calculateHMAC(myKey, seqNum, msg, start, len); 176 } 177 178 boolean compareHMAC(byte[] expectedHMAC, byte[] peerSeqNum, byte[] msg, int start, 179 int len) throws SaslException { 180 byte[] mac = calculateHMAC(peerKey, peerSeqNum, msg, start, len); 181 return Arrays.equals(mac, expectedHMAC); 182 } 183 184 boolean comparePeerSeqNum(byte[] peerSeqNum) { 185 return this.peerSeqNum == byteToInt(peerSeqNum); 186 } 187 188 byte[] getSeqNum() { 189 return seqNum; 190 } 191 192 void incMySeqNum() { 193 mySeqNum ++; 194 } 195 196 void incPeerSeqNum() { 197 peerSeqNum ++; 198 } 199 200 private byte[] calculateHMAC(byte[] key, byte[] seqNum, byte[] msg, int start, 201 int len) throws SaslException { 202 byte[] seqAndMsg = new byte[4+len]; 203 System.arraycopy(seqNum, 0, seqAndMsg, 0, 4); 204 System.arraycopy(msg, start, seqAndMsg, 4, len); 205 206 try { 207 SecretKey keyKi = new SecretKeySpec(key, "HmacMD5"); 208 Mac m = Mac.getInstance("HmacMD5"); 209 m.init(keyKi); 210 m.update(seqAndMsg); 211 byte[] hMAC_MD5 = m.doFinal(); 212 213 /* First 10 bytes of HMAC_MD5 digest */ 214 byte macBuffer[] = new byte[10]; 215 System.arraycopy(hMAC_MD5, 0, macBuffer, 0, 10); 216 217 return macBuffer; 218 } catch (InvalidKeyException e) { 219 throw new SaslException("Invalid bytes used for key of HMAC-MD5 hash.", e); 220 } catch (NoSuchAlgorithmException e) { 221 throw new SaslException("Error creating instance of MD5 MAC algorithm", e); 222 } 223 } 224 225 private void intToByte(int num) { 226 for(int i = 3; i >= 0; i --) { 227 seqNum[i] = (byte)(num & 0xff); 228 num >>>= 8; 229 } 230 } 231 232 private int byteToInt(byte[] seqNum) { 233 int answer = 0; 234 for (int i = 0; i < 4; i ++) { 235 answer <<= 8; 236 answer |= ((int)seqNum[i] & 0xff); 237 } 238 return answer; 239 } 240 } 241}