AWS CloudHSM  鍵の登録方法　ラッピング方法

# CloudHSMとは
- AWSで鍵を管理する場合、通常はKMS(Key Management Service)などを使うのが一般的。
その方が断然安いし、取り回しが簡単。

- しかし、KMSはメモリ上で鍵が平文となる。
　また、AWS自体がハッキング受けた場合には弱いと言わざるを得ない。
　（そもそもハッキングを受けて侵入が成功した場合の話）

-  法令や（VISA,MasterCardなどの）レギュレーションなどで
　  Fips140-2 level 3の要件が受けた場合に、KMSでは対応できなくなる。

# 安全な鍵の輸送方法
- 単純に登録、取り出しをするのであれば、exSymKey,imSymKeyというコマンドがある。
　このコマンドは -wというオプションでラッピングキーを指定するが、実際には平文で出力される。
  （なぜラッピングキーを指定されるのかは不明）
- しかし、登録時に鍵に平文で取り扱うので、安全ではない、CloudHSMに登録するまで、
　暗号化して取り扱い、平文にはしないで取り扱う方法が必要。
- 以下の方式を考える。
  - ① CloudHSMからラッピングキーを取り出す。（AES 192bit）
  - ②　相手先システムでラッピングしてもらう(javaで実装　サンプル)
  - ③ CloudHSMにunWrapKeyコマンドで鍵を登録する。
![flowpng](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22057/06b046e0-335e-2139-581d-d242af954edd.png)


# ① CloudHSMからラッピングキーを取り出す。
ラッピング用鍵のキーハンドルが 17

```bash:ログイン
[test_user@xxxxx ]/opt/cloudhsm/bin/key_mgmt_util 
Command: loginHSM -u CU -s example_user -p hoge12345678
Command:exSymKey -k 17 -w 17 -out gyoumu_plain.dat
Command:exit
[test_user@xxxxx ]od -tx1  gyoumu_plain.dat
0000000 39 c3 bf 03 c7 1e 0d 49 bd 96 8f 26 39 7b 38 55
0000020 e5 e8 9e aa fd 25 6e db c2 f1 d0 3f 32 66 f3 f4
```

#　② 相手先システムでラッピングしてもらう(javaで実装　サンプル)
http://www5d.biglobe.ne.jp/stssk/rfc/rfc3394j.html
- IV:A6A6A6A6A6A6A6A6 で固定
- パディングはなし

```java:AesWrap.java
package aes_wrap_for_cloudhsm;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;

/**
 * AES Wrap　&  UNWrap/ NoPadding 
 *   Wrap Key: AES 128,192,256bit
 *   Wrap Data:   AES,DESEde(T-DES),DES
 *   Padding: NOPadding
 */
public class AesWrap  {

    /** WRAP
     * @param key_bytes
     * @param keyspec: "AES","DESEde","DES"
     * @param wrap_key_bytes
     * @return
     * @throws Exception
     */
    public byte[] wrap(byte[] key_bytes, String keyspec ,byte[] wrap_key_bytes) throws Exception {
        Key wrap_key = new SecretKeySpec(wrap_key_bytes, "AES");
        
        Cipher cipher = Cipher.getInstance("AESWrap");
        // IV:A6A6A6A6A6A6A6A6 で固定
        cipher.init(Cipher.WRAP_MODE, wrap_key);
    	Key key = new SecretKeySpec(key_bytes, keyspec);
        byte[] reseult = cipher.wrap(key); // WRAPする
        return reseult;
    }

    /** UNWRAP
     * @param wrappde_bytes
     * @param keyspec: "AES","DESEde","DES"
     * @param wrap_key_bytes
     * @return
     * @throws Exception
     */
    public byte[] unwrap(byte[] wrappde_bytes,String keyspec ,byte[] wrap_key_bytes) throws Exception {
        Key wrap_key = new SecretKeySpec(wrap_key_bytes, "AES");
        Cipher cipher = Cipher.getInstance("AESWrap");
        // IV:A6A6A6A6A6A6A6A6 で固定
        cipher.init(Cipher.UNWRAP_MODE, wrap_key);
    	Key key  = cipher.unwrap(wrappde_bytes,keyspec, Cipher.SECRET_KEY);
        return key.getEncoded();
    }


}

```

```java:Sample01.java
package aes_wrap_for_cloudhsm;

public class Sample01{
	private static void print_bytes(byte[] data,String label) {
	  System.out.println(label + "="); 
      for (byte b : data) {
      	  System.out.print(String.format("%02X",b));
      }
      System.out.println("");  
	}
	private static byte[] strint_bytes(String hex) {
	    byte[] bytes = new byte[hex.length() / 2];
	    for (int index = 0; index < bytes.length; index++) {
	        bytes[index] = (byte) Integer.parseInt(hex.substring(index * 2, (index + 1) * 2), 16);
	    }
	    return bytes;
	}
    public static void main(String args[]) {
        /*******************************************************************************
         * https://sandbox.ietf.org/doc/html/rfc3394
         * http://www5d.biglobe.ne.jp/stssk/rfc/rfc3394j.html
         *******************************************************************************/
        try {
           	byte[] wrap_key = strint_bytes("000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F");
        	byte[] data_key = strint_bytes("00112233445566778899AABBCCDDEEFF");

        	AesWrap wrap = new AesWrap();
        	byte[] result = wrap.wrap(data_key,"DESEde",wrap_key);
        	print_bytes(result, "WRAP_DATA    ");

        } catch (Exception e) {
        	e.printStackTrace();
        }

    }
}
```


# ③ CloudHSMにunWrapKeyコマンドで鍵を登録する。
## 上記で出力されたラッピングの文字列を hoge.txtに記載

```text:hoge.txt
00 11 22 33 44 55 66 77 88 99 AA BB CC DD EEFF
```

## バイナリデータに変換 hoge.txt->hoge.dat

```bash:
cat hoge.txt | awk -F# '{print $1}' | tr -dc [0-9a-fA-F] | xxd -r -p > hoge.dat
```

## ③　unWrapKeyコマンドで鍵を登録する
ラッピング用鍵のキーハンドルが 17
-m 5 のパディングはなし
-kt 21: T-DES鍵
-kc 4: 対象鍵

```bash:unWrapKey
[test_user@xxxxx ]/opt/cloudhsm/bin/key_mgmt_util 
Command: loginHSM -u CU -s example_user -p hoge12345678
Command:unWrapKey -f hoge.dat -w 17 -noheader -l unwrapped3DES -kc 4 -kt 21 -m 5
        Cfm3GenerateSymmetricKey returned: 0x00 : HSM Return: SUCCESS
        Symmetric Key Created.  Key Handle: 10<-重要
        Cluster Status:
        Node id 0 status: 0x00000000 : HSM Return: SUCCESS
```


##　おまけ　Key CheckValueの確認方法

```bash:unWrapKey
[test_user@xxxxx ]/opt/cloudhsm/bin/key_mgmt_util 
Command: loginHSM -u CU -s example_user -p hoge12345678
Command: getAttribute -o 10 -a 371 -out attr.txt
Command: exit
[test_user@xxxxx ]cat  attr.txt
OBJ_ATTR_KCV
0xe0a44c

```
e0 a4 4cがKey Check Valueの先頭３BYTE

# TODO:RSAを使った輸送方法はより安全

今度、時間をとって調べてみます。