Proving Bob is “Bob”: Using Digital Signatures 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…

Proving Bob is “Bob”: Using Digital Signatures 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.

One of the standard methods that we use in cryptography is to sign a message with a private key, and to prove the signing with the public. Thus if Bob has a key pair, he uses his private key to sign the message, and then Alice will prove that it was Bob who signed it, using Bob’s public key. It will also prove that the message has not been changed by Eve.

We use this method is many applications. An example is in Bitcoin transfers and where Bob signs a transaction to pay Alice a given number of bitcoins. He signs this transaction with his private key (which is in his wallet), and then adds it, with his public key onto the blockchain. Anyone who wants to check the transfer will check the signature with Bob’s public key.

Now let’s look at how Google Tink generates digital signatures codes. The core method is to use ECDSA (Elliptic Curve Digital Signature Algorithm). For this we generate a new key pair with getPrimitive() method for PublicKeySignFactory. We can then use the private key of the key pair to sign a message with the sign() method:

KeysetHandle privateKeysetHandle = KeysetHandle.generateNew(SignatureKeyTemplates.ECDSA_P256);

    PublicKeySign signer = PublicKeySignFactory.getPrimitive(privateKeysetHandle);
    byte[] signature = signer.sign(plaintext.getBytes());

A sample run which shows the signature for a message of “hello” for Base-64 and Hex formats:

Text:	hello
MAC (Base64):	ATHO2ZIwRAIgd+JV6SOM08i01AFsrGR8JLenLDWtPKzoWUDRh4tBqC8CIHEtnVf8OR9QxLQitKVvwxm7FlrkfLnVMC0G0nql3j5d
MAC (Hex):4154484F325A497752414967642B4A5636534F4D3038693031414673724752384A4C656E4C445774504B7A6F575544526834744271433843494845746E5666384F523951784C5169744B567677786D37466C726B664C6E564D433047306E716C336A3564
Valid Signature

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

Printing out key:
{
"primaryKeyId": 835639698,
"key": [{
"keyData": {
"typeUrl": "type.googleapis.com/google.crypto.tink.EcdsaPrivateKey",
"keyMaterialType": "ASYMMETRIC_PRIVATE",
"value": "EkwSBggDEAIYAhogafr30IV05SpchJZvJfQ6ChyWxhNIVSjpmztkJ+wYRusiIHCl4fYPSpuISdC18N9OhWQ9jZvky6B0ytnL97HqafbrGiEAxJzrrM7H3ONlzTRMdoPwWJ/PwkaQ2rCsUK5i3wGZMZQ="
},
"outputPrefixType": "TINK",
"keyId": 835639698,
"status": "ENABLED"
}]
}

We can see that the encryption key used is “EkwSBggDEAIYAho…GZMZQ=” and that the format of the EcdsPrivateKey is defined. 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.KeysetHandle;
import com.google.crypto.tink.PublicKeySign;
import com.google.crypto.tink.PublicKeyVerify;
import com.google.crypto.tink.signature.PublicKeySignFactory;
import com.google.crypto.tink.signature.PublicKeyVerifyFactory;
import com.google.crypto.tink.signature.SignatureKeyTemplates;
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";

    if (args.length>0) plaintext=args[0];

    System.out.println("Text:\t"+plaintext);

KeysetHandle privateKeysetHandle = KeysetHandle.generateNew(SignatureKeyTemplates.ECDSA_P256);

    PublicKeySign signer = PublicKeySignFactory.getPrimitive(privateKeysetHandle);
    byte[] signature = signer.sign(plaintext.getBytes());

    byte[] encoded = Base64.getEncoder().encode(signature);
System.out.println("\nMAC (Base64):\t"+ new String(encoded));
System.out.println("MAC (Hex):"+ toHexString(encoded));
    KeysetHandle publicKeysetHandle =        privateKeysetHandle.getPublicKeysetHandle();

    PublicKeyVerify verifier = PublicKeyVerifyFactory.getPrimitive( publicKeysetHandle);

try {
verifier.verify(signature, plaintext.getBytes());
System.out.println("\nValid Signature");
} catch (GeneralSecurityException e) {
System.out.println("In Valid Signature");
}
    System.out.println("\nPrinting out key:");
    
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    CleartextKeysetHandle.write( privateKeysetHandle, JsonKeysetWriter.withOutputStream(outputStream));
    System.out.println(new String(outputStream.toByteArray()));

    } 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 this method is here.

Conclusions

With Google Tink we finally get a code based which has now been hacked together over the years (such as with OpenSSL) and which is consistent in its usage and focused on API integration. It is a code based for a modern world. A signing with the private key is one of the most fundamental actions that we have in our modern world, and it is basically our identity. So we need to protect our private keys, otherwise we will have our digital identity stolen.