종단 간 암호화

옥텟 API 사용 시 전송 구간 사이의 추가적인 보안을 위해 RSA 퍼블릭 키 기반의 종단 간 암호화 기능을 제공합니다. 고객은 퍼블릭 키를 이용해 암호화를 수행하고 옥텟 서버는 프라이빗 키를 이용해 복호화를 수행합니다.

938

그림 1 : 양측 수행 역할



RSA 키

암호화에 필요한 RSA 키 쌍은 오너 계정 생성 시 자동으로 생성됩니다. 고객사 측에서 사용할 RSA 퍼블릭 키는 옥텟 콘솔의 개발자 도구 페이지에서 확인할 수 있습니다.



암호화 대상

멀티시그 키 중 유저키를 암호화해야 합니다. 암호화 결과를 Request Body의 encryptedUserKey 필드에 담아 API를 호출하면, 옥텟 서버는 해당 값의 복호화 결과를 서명 시 사용합니다.



암호화하기

938

그림 2 : 암호화하기

암호화 패딩 - OAEPWithSHA1AndMGF1Padding



암호화 예제 코드

고객사에서 수행해야 하는 RSA 퍼블릭 키를 활용한 암호화의 언어 별 예제 코드입니다.

node.js

const { readFileSync } = require('fs');
const { publicEncrypt } = require('crypto');

const publicKeyFilePath = '/Users/sam/Downloads/rsa-public-key.pem';
const multisigKey = '801... or 802...';

const publicKey = readFileSync(publicKeyFilePath);
const bodyBuffer = Buffer.from(multisigKey);

const encryptedMessage = publicEncrypt(publicKey, bodyBuffer).toString('base64');
console.log(encryptedMessage);

java 8 이상

/*
 * This Java source file was generated by the Gradle 'init' task.
 */
package EncryptBody;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Scanner;

import javax.crypto.Cipher;

public class App {
    private static String readPemFile(String filePath) {
        StringBuffer sb = null;

        try (
            FileInputStream is = new FileInputStream(filePath);
            Scanner sc = new Scanner(is, "UTF-8");
        ) {
            sb = new StringBuffer();

            while (sc.hasNextLine()) {
                sb.append(sc.nextLine());
            }
            if (sc.ioException() != null) {
                throw sc.ioException();
            }
        } catch(FileNotFoundException e) {
            System.err.println("File not found");
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        String result = sb.toString()
            .replace("-----BEGIN PUBLIC KEY-----", "")
            .replace("-----END PUBLIC KEY-----", "");
        sb.setLength(0);
        return result;
    }

    private static RSAPublicKey getPublicKey(String pem) {
        byte[] decoded = null;
        KeyFactory kf = null;
        RSAPublicKey publicKey = null;

        try {
            decoded = Base64.getDecoder().decode(pem);
            kf = KeyFactory.getInstance("RSA");
            publicKey = (RSAPublicKey) kf.generatePublic(new X509EncodedKeySpec(decoded));
        } catch (IllegalArgumentException e) {
            System.err.println("Invalid pem file");
            e.printStackTrace();
        } catch (GeneralSecurityException e) {
            System.err.println("PublicKey generation failed");
            e.printStackTrace();
        }

        return publicKey;
    }

    private static String encrypt(String plainText, RSAPublicKey publicKey) {
        String result = null;

        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding");
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            result = Base64.getEncoder().encodeToString(cipher.doFinal(plainText.getBytes()));
        } catch (GeneralSecurityException e) {
            System.err.println("PublicKey encryption failed");
            e.printStackTrace();
        }

        return result;
    }

    public static void main(String[] args) {
        String publicKeyFilePath = "/Users/sam/Downloads/rsa-public-key.pem";
        String multisigKey = "801... or 802...";

        String pem = readPemFile(publicKeyFilePath);
        RSAPublicKey publicKey = getPublicKey(pem);

        String encrypted = encrypt(multisigKey, publicKey);
        System.out.println(encrypted);
    }
}

C#

using System.Security.Cryptography;
using System.Text;

string publicKeyFilePath = "/Users/sam/Downloads/rsa-public-key.pem";
string multisigKey = "801... or 802...";

RSACryptoServiceProvider rsa = new();
rsa.ImportFromPem(File.ReadAllText(publicKeyFilePath));

byte[] encrypted = rsa.Encrypt(Encoding.UTF8.GetBytes(multisigKey), true);
Console.WriteLine(Convert.ToBase64String(encrypted));

Python 3

#
# pip3 install pycryptodome
#

from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
import base64

def readPemFile(file_path) :
  file = open(file_path)
  key = RSA.importKey(file.read())
  file.close()
  return key

public_key_file_path = '/Users/sam/Downloads/rsa-public-key.pem'
multisig_key = '801... or 802...'

cipher = PKCS1_OAEP.new(readPemFile(public_key_file_path))
encrypted = cipher.encrypt(multisig_key.encode('utf-8'))

print(base64.b64encode(encrypted).decode('ascii'))