The Ease of Public Key and Power of Symmetric Key: Meet Hybrid Encryption with Google Tink (ECIES)

Using public key and secret key

The Ease of Public Key and Power of Symmetric Key: Meet Hybrid Encryption with Google Tink (ECIES)

Using public key and secret key

A demo of the method is here.

Google has released Tink and which is a multi-language, cross-platform cryptographic library. Hybrid Encryption is a combination of symmetric key encryption and public-key encryption. We encrypt with a newly created symmetric key, and then encrypt this with the public key of the recipient. The most popular public method is elliptic curve with Curve 25591, and the most popular symmetric method is AES.

Outline of method

With Elliptic curve integrated encryption scheme (ECIES), Alice generates a random private key (dA) and the takes a point on an elliptic curve (G) and then determines her public key (QA):

QA=dA×G

G and QA are thus points on an elliptic curve. Alice then sends QA to Bob. Next Bob will generate:

R=r×G

S=r×QA

and where r is a random number generated by Bob. The symmetric key (S) is then used to encrypt a message.

Alice will then receive the encrypted message along with . She is then able to determine the same encryption key with:

S=dA×R

which is:

S=dA×(r×G)

S=r×(dA×G)

S=r×QA

and which is the same as the key that Bob generated.

The method is illustrated here:

Bob thus uses Alice’s public key to encrypt the newly created symmetric key.

Implementation with Google Tink

Google Tink is an open source repository for the integration of cryptography methods. It uses best practice in order to reduce risks, and also to simplify code integration. Currently it supports Java, C++ and Objective-C. As Java is well supported on Android devices, the code has already been integrated into a wide range of applications, including Google Pay.

We create the symmetric key set with:

KeysetHandle privateKeysetHandle = KeysetHandle.generateNew(
HybridKeyTemplates.ECIES_P256_HKDF_HMAC_SHA256_AES128_GCM);

and the public key pair with:

KeysetHandle publicKeysetHandle =
privateKeysetHandle.getPublicKeysetHandle();
    HybridEncrypt hybridEncrypt = HybridEncryptFactory.getPrimitive(publicKeysetHandle);

The encryption and decryption are then (for a message of “plaintext” and a passphrase of “aad”:

byte[] ciphertext = hybridEncrypt.encrypt(plaintext.getBytes(), aad.getBytes());

    HybridDecrypt hybridDecrypt = HybridDecryptFactory.getPrimitive(
privateKeysetHandle);

    byte[] decrypted = hybridDecrypt.decrypt(ciphertext, aad.getBytes());

A sample run is:

Text:		hello
Password: qwerty
Sig (Base64):	ATJrfYwE7x3tQ6d4Zu5vGZ8/2hW3433Dk1cfw8ca900YdH1vJM9c+h2YPIFcXwk1duuOKXHPZ3qXx7QVikuHL4+D9tF38L7tHHnddsQlLnm4I9Oy+BV3ai4vEzmpts4LSeFwmfvqfw==
Sig (Hex):41544A726659774537783374513664345A753576475A382F32685733343333446B3163667738636139303059644831764A4D39632B6832595049466358776B316475754F4B5848505A33715878375156696B75484C342B4439744633384C377448486E646473516C4C6E6D3449394F792B42563361693476457A6D707473344C536546776D66767166773D3D
Decrypted:	hello

In terms of the key pair we can view the key pair here:

Printing out key:
{
"primaryKeyId": 845905292,
"key": [{
"keyData": {
"typeUrl": "type.googleapis.com/google.crypto.tink.EciesAeadHkdfPublicKey",
"keyMaterialType": "ASYMMETRIC_PUBLIC",
"value": "EkQKBAgCEAMSOhI4CjB0eXBlLmdvb2dsZWFwaXMuY29tL2dvb2dsZS5jcnlwdG8udGluay5BZXNHY21LZXkSAhAQGAEYARogOyIb/MongoqTLNlQS9iRbqqhIrzE7w8w1SwpnVjsDpwiIQCrCIUxvLyqXUD9a3nDb3Cbs0YvNQB5wlGMxCLDFTmeDA=="
},
"outputPrefixType": "TINK",
"keyId": 845905292,
"status": "ENABLED"
}]
}

The code is:

package com.helloworld;
import java.util.Base64;
import com.google.crypto.tink.aead.AeadConfig;
import java.security.GeneralSecurityException;
import java.io.ByteArrayOutputStream;
import com.google.crypto.tink.HybridDecrypt;
import com.google.crypto.tink.HybridEncrypt;
import com.google.crypto.tink.hybrid.HybridDecryptFactory;
import com.google.crypto.tink.hybrid.HybridEncryptFactory;
import com.google.crypto.tink.hybrid.HybridKeyTemplates;
import com.google.crypto.tink.KeysetHandle;
import org.apache.commons.codec.binary.Hex;
import com.google.crypto.tink.CleartextKeysetHandle;
import com.google.crypto.tink.JsonKeysetWriter;
import com.google.crypto.tink.config.TinkConfig;
public final class HelloWorld {
  public static void main(String[] args) throws Exception {

    TinkConfig.register();
    try {
    String plaintext="napier";
String aad="qwerty123";
    System.out.println("Text:\t\t"+plaintext);
System.out.println("Password:\t"+aad);

    KeysetHandle privateKeysetHandle = KeysetHandle.generateNew(
HybridKeyTemplates.ECIES_P256_HKDF_HMAC_SHA256_AES128_GCM);

    KeysetHandle publicKeysetHandle =
privateKeysetHandle.getPublicKeysetHandle();
    HybridEncrypt hybridEncrypt = HybridEncryptFactory.getPrimitive(publicKeysetHandle);
    byte[] ciphertext = hybridEncrypt.encrypt(plaintext.getBytes(), aad.getBytes());

    byte[] encoded = Base64.getEncoder().encode(ciphertext);
System.out.println("\nSig (Base64):\t"+ new String(encoded));
System.out.println("Sig (Hex):"+ toHexString(encoded));
    HybridDecrypt hybridDecrypt = HybridDecryptFactory.getPrimitive(
privateKeysetHandle);

    byte[] decrypted = hybridDecrypt.decrypt(ciphertext, aad.getBytes());
    String s = new String(decrypted);
System.out.println("\nDecrypted:\t"+s);

    System.out.println("\nPrinting out key:");
    
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    CleartextKeysetHandle.write( publicKeysetHandle, JsonKeysetWriter.withOutputStream(outputStream));
    System.out.println(new String(outputStream.toByteArray()));
    System.out.println("\nPrinting out public key:");
    outputStream = new ByteArrayOutputStream();

    } catch (GeneralSecurityException e) {
System.out.println(e);
System.exit(1);
}
  }
  public static String toHexString( byte[] bytes )
{
StringBuffer sb = new StringBuffer( bytes.length*2 );
for( int i = 0; i < bytes.length; i++ )
{
sb.append( toHex(bytes[i] >> 4) );
sb.append( toHex(bytes[i]) );
}
      return sb.toString();
}
private static char toHex(int nibble)
{
final char[] hexDigit =
{
'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
};
return hexDigit[nibble & 0xF];
}
}

A demo of the method is here.

Conclusions

ECIES provides the ease of use of public key with the performance of symmetric key.