Code Samples


See for code snippets in this post.

Asymmetric encryption is like a 1970's style cash lock box that can be locked by anyone using a publicly available store key, but that can only unlocked by the manager with the master key.

Asymmetric authentication is like a watermark or a signature. It's also like a lock that only the author can secure, but anyone can unlock.

These metaphorical lock boxes are very small - only big enough to store another key (meaning a symmetric key) or a fingerprint (meaning hashsum).

A use case for Public / Private crypto

Bob wants to send Alice a message, but he doesn't want Eve, the postwoman, to know what it is or to be able to tamper with it.

  1. Bob signs the message with his private key
  2. Bob uses Alice's public key to encrypt his message
  3. Bob gives the message to Eve to send to Alice
  4. Eve cannot verify if the message originated from Bob or someone else
  5. Eve cannot read the contents of the message (because it's encrypted)
  6. Eve cannot tamper with the contents of the message (because it's signed)

In reality this would be done in conjunction with message digests for signatures and symmetric keys for encryption, but the oversimplification above does the job for the basic premise.

In Summary

About Keys

  • Public keys Encrypt & Verify
  • Private keys Sign & Decrypt

About Signatures

  • Created by Private key on a digest (i.e. a sha256 hashsum)
  • Authentic
  • A tamper-proof seal
  • Completely visible
  • Verifiable by anyone via Public key

About Crypto

  • Encryptable by anyone with Public key
  • Private
  • Decryptable by recipient only
  • You can start a two-way encrypted conversation with only one set of keys


Asymmetric encryption is very slow and the size is very limited.

Protocols like HTTPS are very fast and can be used to encrypt very large streams of data because they only use RSA to exchange an symmetric AES key (a shared secret) and then continues the session with that shared secret.


To "encrypt" and to "sign" are almost the same thing (as are "decrypt" and "verify" also very similar), but they are subtly different.

If you were to encrypt the string "hello", you would encrypt it with the recipient's public key and the string could be retrieved with the recipient's private key alone.

If you were to sign the string "hello" with your own private key, it can only be "verified" if the recipient has both the public key and the original plaintext.

You might intuitively think that you could simply "encrypt" data with your own private key to "sign" it, as the author. This would validate the authorship of the message, however, it if you intended to encrypt the message and send it with such a "signature", your "signature" would contain the message and it would now be publicly visible.

Securing Keys

Typically you may encrypt your private key with a passphrase-derived symmetric key.

Let's get (RSA) keys!

RSA is a standard for Public / Private cryptography. It's completely open source, patent and royalty free. That's why it works everywhere.

Here we're gonna create two 2048-bit RSA keypairs.

The reason we need two keypairs is that Bob and Alice each need to have their own keypairs.

Using OpenSSL.

# Make keys for Bob
mkdir -p ./bob
openssl genrsa -out ./bob/privkey.pem 2048
openssl rsa -in ./bob/privkey.pem -pubout -out ./bob/pubkey.pem

# Make keys for Alice
mkdir -p ./alice
openssl genrsa -out ./alice/privkey.pem 2048
openssl rsa -in ./alice/privkey.pem -pubout -out ./alice/pubkey.pem

Using node.js

npm install --save bluebird
npm install --save ursa

In short

var ursa = require('ursa');
var key = ursa.generatePrivateKey(1024, 65537);
var privkeypem = key.toPrivatePem();
var pubkeypem = key.toPublicPem();


In full


'use strict';

var PromiseA = require('bluebird').Promise;
var fs = PromiseA.promisifyAll(require('fs'));
var path = require('path');
var ursa = require('ursa');
var mkdirpAsync = PromiseA.promisify(require('mkdirp'));

function keypair(pathname) {
  var key = ursa.generatePrivateKey(1024, 65537);
  var privpem = key.toPrivatePem();
  var pubpem = key.toPublicPem();
  var privkey = path.join(pathname, 'privkey.pem');
  var pubkey = path.join(pathname, 'pubkey.pem');

  return mkdirpAsync(pathname).then(function () {
    return PromiseA.all([
      fs.writeFileAsync(privkey, privpem, 'ascii')
    , fs.writeFileAsync(pubkey, pubpem, 'ascii')
  }).then(function () {
    return key;

, keypair('alice')
]).then(function (keys) {
  console.log('generated %d keypairs', keys.length);

Let's sign and encrypt some data

As stated before, you don't directly sign or encrypt data using RSA.

Instead you create a SHA-256 hashsum of the data and sign it with the sender's Private key.

Likewise you create a random 256-bit AES key and encrypt it with the recipient's Public key.

See for a very simple explanation and for a very effective demonstration.

Since the particulars of how to combine the RSA and AES operations and metadata into a particular flow and container format are defined in higher-layer standards - such as HTTPS, GPG, PKI, X.509 etc - we'll only encrypt a short message for this example.

You can think of this in a similar way to how mp4, MKV, avi, mov, flv, and others can all contain h.264 video and various types of audio.

With OpenSSL

echo "Hello, Alice!" > ./msg.txt

# Sign as Bob, so Alice knows who sent the message
openssl rsautl -sign -in ./msg.txt \
  -inkey ./bob/privkey.pem -out ./msg.sig

# Encrypt the message and the signature with Alice's key
openssl rsautl -encrypt -inkey alice/pubkey.pem \
  -pubin -in ./msg.txt -out ./msg.enc

With node.js

'use strict';

var fs = require('fs');
var ursa = require('ursa');
var msg;
var sig;
var enc;
var rcv;

// Bob has his private and Alice's public key
var privkeyBob = ursa.createPrivateKey(fs.readFileSync('./bob/privkey.pem'));
var pubkeyAlice = ursa.createPublicKey(fs.readFileSync('./alice/pubkey.pem'));

// Alice has her private and Bob's public key
var privkeyAlice = ursa.createPrivateKey(fs.readFileSync('./alice/privkey.pem'));
var pubkeyBob = ursa.createPublicKey(fs.readFileSync('./bob/pubkey.pem'));


console.log('Encrypt with Alice Public; Sign with Bob Private');
enc = pubkeyAlice.encrypt(msg, 'utf8', 'base64');
sig = privkeyBob.hashAndSign('sha256', msg, 'utf8', 'base64');
console.log('encrypted', enc, '\n');
console.log('signed', sig, '\n');

console.log('Decrypt with Alice Private; Verify with Bob Public');
rcv = privkeyAlice.decrypt(enc, 'base64', 'utf8');
if (msg !== rcv) {
  throw new Error("invalid decrypt");
rcv = new Buffer(rsv).toString('base64');
if (!pubkeyBob.hashAndVerify('sha256', rcv, sig, 'base64')) {
  throw new Error("invalid signature");
console.log('decrypted', msg, '\n');

Encrypting with your Private Key

Encrypting with your private key and decrypting with your public key is nonsense. It's something you would only try to do if you're just learning about Public / Private key encryption and you still don't know what you're doing yet.

I'm including this section only because plenty of people, including myself, search for it.

WARNING: You don't understand what you're doing. Repeat: You can't get there from here. It don't do like that.

Let's say you're sending very small, twitter-sized messages. You might think that if you could encrypt them with your private key have others decrypt them with your public key that you save on bytes by only sending one encrypted message rather than the plaintext plus the 1024- or 2048-bit signature, effectively halving your size.

In that instance, you would be wrong. This cannot be done.

See also

By AJ ONeal

If you loved this and want more like it, sign up!

Did I make your day?
Buy me a coffeeBuy me a coffee  

(you can learn about the bigger picture I'm working towards on my patreon page )