End-to-end encryption

Octet API provides RSA-based end-to-end encryption for additional security during a transaction. Clients use a public key for encryption, and Octet uses a private key to decrypt.

Figure 1: Roles of each participant

Figure 1: Roles of each participant



RSA Key

The RSA key pair for encryption is automatically generated when creating the owner account. The RSA public key can be found on the developer tool page of the Octet console.



Encryption Target

The user key of the multi-sig key must be encrypted. Call the API with the encrypted result in the encryptedUserKey field of the Request Body, and the Octet server will use the decrypted result of the value when signing.



Encrypt

Figure 2: Encrypt

Figure 2: Encrypt

Encryption modes and padding

  • Padding : OAEPWithSHA1AndMGF1Padding


Sample encryption code

Below are sample encryption codes for different programming languages that the client can use to encrypt data using the RSA public key.

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 or higher

/*
 * 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'))