API 전문 암호화

추가적인 보안을 위해 출금 API 호출 시 전문을 암호화할 수 있습니다. 기존 출금 API의 동일한 endpoint를 사용합니다. Header에 Octet-Access-Key가 포함되어 있다면 API 전문 암호화를 사용하는 것으로 판단합니다. 따라서 해당 기능 사용 시 반드시 AccessKey를 발급 받아 사용해야 합니다. API Payload를 암호화하여 출금 API를 호출하는 방법은 아래 가이드를 참고해 주세요.

TIP
해당 기능은 모든 키 관리 유형을 지원합니다.
싱글시그 지갑과 멀티시그 지갑 모두 API 전문을 암호화하여 출금할 수 있습니다.


# 지원 API

* 출금 신청
* NFT 컨트랙트 배포 신청
* NFT 아이템 생성 신청
* NFT 출금 신청
* NFT contract method execution 신청

<br/>

    <br/>

        <br/>

            ***

            # API 전문 암호화하기

            1. **AES 키 생성 API**를 호출해 양방향 암호화를 위한 키 쌍을 생성합니다.

            `SecretKey`와 `hashKey`는 발급할 때만 확인이 가능하며, `AccessKey`는 언제든지 확인할 수 있습니다. 만약 `SecretKey`와 `hashKey`를 분실했다면, 키를 재발급 받아야 합니다.

            <br/>

                2. API 호출을 위한 Body를 만듭니다.\
                전문 암호화 기능을 이용하게 되면 유저키를 RSA 암호화 하여 전송할 필요가 없습니다. 평문으로 전송해도 되고, 기존처럼 RSA 암호화하여 전송해도 됩니다. 단, 평문으로 전송할 때에는 `userKey` 로 전송해야 합니다.

                ```typescript
                // 기존 멀티시그 지갑 출금 신청 API Payload
                {
                    ...
                        "encryptedUserKey": "rsa encrypted userkey",
                    ...
                }

                // 암호화 시 멀티시그 지갑 출금 신청 API Payload
                {
                    ...
                        "userKey": "802...",
                    ...
                }
                ```

                <br/>

                    3. 암호화 한 결과 값을 Body에 data로 넣습니다.

                    ```typescript
                    // API 호출 시 Body
                    {
                        "data": "Owl9/MDqndW0jNpz2waUSO3Ed50ZGwTNEAjXTkhlLYVPi9e05cMzKeugYFKywPDNx/FCeUQOKzHeyHKKsKFLVC+7Eo5mrefC30t8E4tfjDMY+PRQJPLl4LsQICCiTDUr6n/s/y/6hchNlea1beobmSHY9IFDc39aQJmr1T3gYh0fhQOTHCv9GvyAmRwv18L7wOLvTT/jYySLq2uYa24hR4IcCtUe4yYLUmPMYqPi4Rh4aXOkooOWKJP0ARdtk09WQwKNAh1NbHpWkIa3CRpHUg=="
                    }
                    ```

                    <br/>

                        4. API 호출하는 Header에는 AccessKey를 넣습니다.

                        ```typescript
                        HEADER
                        Authorization: 'Bearer {apiToken}'
                        Octet-Access-Key: '{Access Key}'

                        BODY
                        {
                            "data": "Owl9/MDqndW0jNpz2waUSO3Ed50ZGwTNEAjXTkhlLYVPi9e05cMzKeugYFKywPDNx/FCeUQOKzHeyHKKsKFLVC+7Eo5mrefC30t8E4tfjDMY+PRQJPLl4LsQICCiTDUr6n/s/y/6hchNlea1beobmSHY9IFDc39aQJmr1T3gYh0fhQOTHCv9GvyAmRwv18L7wOLvTT/jYySLq2uYa24hR4IcCtUe4yYLUmPMYqPi4Rh4aXOkooOWKJP0ARdtk09WQwKNAh1NbHpWkIa3CRpHUg=="
                        }
                        ```

                        <br/>

                            5. Payload의 무결성을 확인하기 위해 HMAC을 헤더에 포함하여 전송하여야 합니다.\
                            'SecretKey로 암호화 하기 전의 Body값'을 HashKey를 이용하여 SHA256으로 해싱합니다.\
                            해당 값을 헤더에 담아 전송합니다.

                            ```typescript
                            // HMAC 대상이 되는 Body
                            {
                                "symbol": "ETH",
                                "requestId": "user1-amount0.001-symbolETH-202302281830",
                                "amount": "0.001",
                                "senderAddress": "0x55Bc4ba7A86A00C8A1A09C59DcC32eA2E7FAbCd4",
                                "receiverAddress": "0xee3c0f0AF227dA8DaccbF3Bc33bCf20f0a222aEF"
                            }
                            ```

                            ```typescript
                            HEADER
                            Authorization: 'Bearer {apiToken}'
                            Octet-Access-Key: '{Access Key}'
                            Octet-Hmac: '5EJZMSFuZrtTle+O5FzuPvHZFnFWRFe5WZJ/VKWfaBY=' <-- HMAC-SHA256
                            BODY
                            {
                                "data": "Owl9/MDqndW0jNpz2waUSO3Ed50ZGwTNEAjXTkhlLYVPi9e05cMzKeugYFKywPDNx/FCeUQOKzHeyHKKsKFLVC+7Eo5mrefC30t8E4tfjDMY+PRQJPLl4LsQICCiTDUr6n/s/y/6hchNlea1beobmSHY9IFDc39aQJmr1T3gYh0fhQOTHCv9GvyAmRwv18L7wOLvTT/jYySLq2uYa24hR4IcCtUe4yYLUmPMYqPi4Rh4aXOkooOWKJP0ARdtk09WQwKNAh1NbHpWkIa3CRpHUg=="
                            }
                            ```

                            <br/>

                                <br/>

                                    <br/>

                                        ***

                                        # 예제 코드

                                        고객사에서 수행해야 하는 암호화의 언어 별 예제 코드입니다.

                                        ## Node.js

                                        ```typescript
                                        import { createHash, randomBytes, createCipheriv, createDecipheriv, generateKey, generateKeySync, createHmac } from 'crypto';

                                        hashMAC(data: Record<string, any>, hashKey: string): string {
                                        const message = JSON.stringify(data);
                                        return createHmac('sha256', hashKey).update(message).digest('base64');
                                    }

                                        encryptMessage(data: Record<string, any>, secretKey: string): string {
                                        const AES_ALGORITHM = 'aes-256-cbc';
                                        const AES_IV_SIZE = 16;
                                        const AES_OUTPUT_ENCODING = 'base64';

                                        const message = JSON.stringify(data);
                                        const keyHash = createHash('sha256').update(secretKey);
                                        const iv = randomBytes(AES_IV_SIZE);
                                        const cipher = createCipheriv(AES_ALGORITHM, keyHash.digest(), iv);
                                        const cipherText = cipher.update(Buffer.from(message));
                                        const cipherFinal = cipher.final();
                                        const encrypted = Buffer.concat([iv, cipherText, cipherFinal]);

                                        return encrypted.toString(AES_OUTPUT_ENCODING);
                                    }
                                        ```

                                        <br/>

                                            ## Java

                                            ```java
                                            // gson
                                            class JsonUtil {
                                            public static String stringify(Object obj) {
                                            Gson gson = new Gson();
                                            return gson.toJson(obj);
                                        }
                                        }

                                            // jackson
                                            class JsonUtil {
                                            public static String stringify(Object obj) throws JsonProcessingException {
                                            ObjectMapper objectMapper = new ObjectMapper();
                                            return objectMapper.writeValueAsString(obj);
                                        }
                                        }

                                            class AesEncryptionUtil {
                                            private static final String AES_ALGORITHM = "AES/CBC/PKCS5Padding";
                                            private static final int AES_IV_SIZE = 16;

                                            public static String aesEncrypt(Object data, String password) throws Exception {
                                            String message = JsonUtil.stringify(data);
                                            MessageDigest md = MessageDigest.getInstance("SHA-256");
                                            byte[] keyBytes = md.digest(password.getBytes(StandardCharsets.UTF_8));
                                            SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
                                            byte[] iv = new byte[AES_IV_SIZE];
                                            SecureRandom random = new SecureRandom();
                                            random.nextBytes(iv);
                                            IvParameterSpec ivParams = new IvParameterSpec(iv);
                                            Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
                                            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParams);
                                            byte[] cipherText = cipher.update(message.getBytes(StandardCharsets.UTF_8));
                                            byte[] cipherFinal = cipher.doFinal();
                                            byte[] encryptedBytes = new byte[iv.length + cipherText.length + cipherFinal.length];
                                            System.arraycopy(iv, 0, encryptedBytes, 0, iv.length);
                                            System.arraycopy(cipherText, 0, encryptedBytes, iv.length, cipherText.length);
                                            System.arraycopy(cipherFinal, 0, encryptedBytes, iv.length + cipherText.length, cipherFinal.length);
                                            return Base64.getEncoder().encodeToString(encryptedBytes);
                                        }
                                        }

                                            class HmacUtil {
                                            public static String createHmac(Object data, String hashKey) throws Exception {
                                            String message = JsonUtil.stringify(data);
                                            Mac mac = Mac.getInstance("HmacSHA256");
                                            SecretKeySpec keySpec = new SecretKeySpec(hashKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
                                            mac.init(keySpec);
                                            byte[] hmacBytes = mac.doFinal(message.getBytes(StandardCharsets.UTF_8));
                                            return Base64.getEncoder().encodeToString(hmacBytes);
                                        }
                                        }


                                            // lombok
                                            @Builder
                                            @Getter
                                            class Withdrawal {
                                            private String symbol;
                                            private String requestId;
                                            private String amount;
                                            private String senderAddress;
                                            private String receiverAddress;
                                        }
                                            ```

                                            <br/>

                                                ## 그 외 언어 사용 시

                                                위 언어 외 다른 언어로 암호화를 진행할 경우, 아래 값을 이용하여 테스트할 수 있습니다.

                                                ### 1. 테스트 데이터

                                                ```
                                                secretKey : 5ba425e8473f74e246f393f1950f0509772c35d2cfc0c3dae8fdbe5db33daa51
                                                hashKey   : 218471b0f4b1e4f8a01a8bd783462ef7a988569ecb1518263b129a10a910945d
                                                iv        : HEXLANTOCTETV2.0
                                                ```

                                                ```
                                                {
                                                    "symbol": "ETH",
                                                    "requestId": "1ETH_20230511_1738_USER_000",
                                                    "amount": "1",
                                                    "senderAddress": "0x0CDAf02E737bF187c40bdd44f6f5230a4007A4d6",
                                                    "receiverAddress": "0x1317e2E91eb1f3161fad47D5a70A3C52E8E84f4D",
                                                    "userKey": "rawUserKeyrawUserKeyrawUserKeyrawUserKeyrawUserKeyrawUserKeyrawUserKeyrawUserKeyrawUserKeyrawUserKeyrawUserKeyrawUserKeyrawUserKeyrawUserKey"
                                                }
                                                ```

                                                <br/>

                                                    ### 2. 검증

                                                    위 객체를 암호화 하였을 때 다음과 같은 값으로 출력되어야 합니다.

                                                    ```
                                                    HMAC           : KQTd+eynbbyeDA1Hc+N75taYqCNc5Ln04HlXUOvg7qg=
                                                    Encrypted Body : SEVYTEFOVE9DVEVUVjIuMH4ftbMr9z+fYILoCWSOnUeRwPb2E8orqtKDEM3eSZ7WxrYIUH76Yp0FkA5i9sdBTUj48mdtlxQ1Hc2oPpQkAf5SZql3rdnaT5B4fC1csnkSopCg3cqFbknlVOThpUpF+d7Lrb708IEkWmmyOADAn67GSO9XP7lKkHBdzi4ueSAPg8JovNoVq27tjcINLhNMln+HS+gQp0t/HgfP5AC8sxgwMxuNoJ2i7qU3BFt8pPov8nBpY/4989kY1bE1r31GeEkHr30iiG5S3HsRoZRXeEMetVt7/4Vwk/FmoIBbO4tujIabsunNo5CRxMpoAHYFoGtGI+AqG2HdoZL70csNDdMAen0jjBaF4Q/W+PMgrPimmUjYTxpVDgVrKXFa1H5PeK1lncpE0CUnRA7v6kXptMyNVyaAR4xFYELRjSHt3aSFy4Do3Q8rERmEhfeAOJdIpD7iOC5wx3hr/XNEfn0mctw=
                                                    ```

                                                    <br/>

                                                        위 객체를 직렬화 하여 문자열로 출력했을 때 다음과 같은 값으로 출력되어야 합니다.

                                                        ```
                                                        {"symbol":"ETH","requestId":"1ETH_20230511_1738_USER_000","amount":"1","senderAddress":"0x0CDAf02E737bF187c40bdd44f6f5230a4007A4d6","receiverAddress":"0x1317e2E91eb1f3161fad47D5a70A3C52E8E84f4D","userKey":"rawUserKeyrawUserKeyrawUserKeyrawUserKeyrawUserKeyrawUserKeyrawUserKeyrawUserKeyrawUserKeyrawUserKeyrawUserKeyrawUserKeyrawUserKeyrawUserKey"}
                                                        ```

                                                        <br/>

                                                            <br/>