---
title: 'Asymmetric Encryption'
source: 'https://academia.sh/en/courses/identity-and-cryptography/asymmetric-encryption'
course: 'Identity, Access and Cryptography'
language: en
updated: '2026-09-26T03:02:52+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - "Distinguishes what each of the public and private key's four possible operation combinations provides by actually running them"
    - "Compares the asymmetric scheme's secret item count and transported secret item count with the symmetric scheme"
    - "Measures direct asymmetric transport's size limit and calculates hybrid use's operation count"
---

# Asymmetric Encryption

The separation of the public and private key's roles: two of four possible key-operation combinations working, one giving secrecy and the other a source claim, a 190-byte upper bound in a 2048-bit modulus, the secret item count dropping from 7.69×10^7 to 12,401 at 12,401 participants and the transported secret item falling to zero, and hybrid use cutting overhead from 38 percent to 7 percent.

The previous lesson's unresolved item stood in a single sentence: to start an encrypted
conversation, a pre-shared secret is needed, and sharing that secret itself needs an encrypted
channel. The loop cannot be broken from inside symmetric encryption, because a single key
carries both the lock and the key that opens it.

This lesson splits the lock in two. Of the two keys generated together, one locks and the
other unlocks; the two are not interchangeable, and one of them can be given to everyone. The
split itself turns into a countable question: of the four possible key-operation combinations,
which ones work, and what do the ones that work provide?

## Two Keys, One Pair

**CF14:** in asymmetric encryption, two keys are generated together and are mathematically
linked. An operation done with one is only reversed with the other; it is not reversed with the
same key.

**CF15:** encrypting with the public key gives secrecy — only the private key's owner can read
the output. Encrypting with the private key does not give secrecy, because the public key is
with everyone; what it carries is a source claim.

```js
// asymmetric/role.mjs — which operation the public and private key perform in which
// direction is actually run. A key pair is generated for the center in the fictional
// regional measurement network; key material is not printed, only length and outcome are.
import { generateKeyPairSync, publicEncrypt, privateDecrypt,
         privateEncrypt, publicDecrypt, constants } from "node:crypto";

// CF14: in asymmetric encryption, two keys are generated together; an operation done
//       with one is only reversed with the other, not with the same key.
// CF15: encrypting with the public key gives secrecy (only the private key's owner reads
//       it); encrypting with the private key does not give secrecy, it carries a source claim.

const BITS = 2048;
const { publicKey: PUBLIC, privateKey: PRIVATE } = generateKeyPairSync("rsa", { modulusLength: BITS });
const READING = "SAY-4417|2026-03-01T06:00Z|41982";
const OAEP = { oaepHash: "sha256", padding: constants.RSA_PKCS1_OAEP_PADDING };
const attempt = (f) => { try { return f().toString() === READING ? "yes" : "no"; }
                      catch { return "no"; } };
const printRow = (g, ...s) => console.log(s.map((v, i) =>
  (g[i] < 0 ? String(v).padEnd(-g[i]) : String(v).padStart(g[i]))).join(""));

const oaepEncrypted = publicEncrypt({ key: PUBLIC, ...OAEP }, Buffer.from(READING));
const privateEncrypted = privateEncrypt(PRIVATE, Buffer.from(READING));
const R = [-12, -12, 9, -24];
printRow(R, "encryptor", "decryptor", "worked", "  provides");
for (const [a, b, f, provides] of [
  ["public", "private", () => privateDecrypt({ key: PRIVATE, ...OAEP }, oaepEncrypted), "secrecy"],
  ["public", "public", () => publicDecrypt(PUBLIC, oaepEncrypted), "-"],
  ["private", "public", () => publicDecrypt(PUBLIC, privateEncrypted), "source claim"],
  ["private", "private", () => privateDecrypt(PRIVATE, privateEncrypted), "-"],
]) printRow(R, a, b, attempt(f), "  " + provides);

const CAP = BITS / 8 - 2 * 32 - 2;                  // upper bound for OAEP-SHA256
const fits = (n) => { try { publicEncrypt({ key: PUBLIC, ...OAEP }, Buffer.alloc(n));
                             return "yes"; } catch { return "no"; } };
console.log(`\nmodulus ${BITS} bits = ${BITS / 8} bytes; OAEP-SHA256 cap ${CAP} bytes`);
const S = [10, 9, 14];
printRow(S, "plaintext", "fits", "cipher bytes");
for (const n of [32, CAP, CAP + 1])
  printRow(S, n + " bytes", fits(n), fits(n) === "yes"
    ? publicEncrypt({ key: PUBLIC, ...OAEP }, Buffer.alloc(n)).length + " bytes" : "-");
console.log(`reading ${Buffer.byteLength(READING)} bytes; ciphertext form ${oaepEncrypted.length} bytes`);
```

```
encryptor   decryptor      worked  provides
public      private           yes  secrecy
public      public             no  -
private     public            yes  source claim
private     private            no  -

modulus 2048 bits = 256 bytes; OAEP-SHA256 cap 190 bytes
 plaintext     fits  cipher bytes
  32 bytes      yes     256 bytes
 190 bytes      yes     256 bytes
 191 bytes       no             -
reading 32 bytes; ciphertext form 256 bytes
```

Two of the four combinations work and two do not. The two rows that do not work say the same
thing: an operation done with one key is not reversed with that same key. This is the one
structural difference from the symmetric scheme, and the rest of the lesson counts this
difference's consequences.

The two rows that work are each other's opposite. The **encrypt with the public key, decrypt
with the private key** direction gives secrecy: since the public key is with everyone, anyone
can send an encrypted reading to the center, but the only side that can read it is the private
key's owner. In the fictional measurement network, this means none of the 12,400 meter endpoints
can read what another one sends.

The **encrypt with the private key, decrypt with the public key** direction, on the other hand,
does not give secrecy — since the public key is with everyone, anyone can decrypt the output.
What it gives is a source claim: if the decryption succeeds, the side that performed the
operation is the side holding the private key. This direction is the foundation of the digital
signature and is taken up in its own form in the seventh lesson.

The lower table gives asymmetric encryption's second constraint. The modulus is 256 bytes, and
the padding scheme consumes 66 bytes of it; the largest plaintext that can be transported is
190 bytes. 190 bytes works, 191 bytes does not. The output, though, is 256 bytes no matter what
the input is — even a 32-byte reading grows to 256 bytes, an eightfold overhead.

## The Secret Item Count and Hybrid Use

The size limit looks like an obstacle, but the real measurement is elsewhere: how many secret
items does this scheme require, and how many of them are transported?

```js
// asymmetric/item.mjs — the asymmetric scheme's effect on the secret item count and hybrid
// use for large data (model + running measurement). Participant counts are fictional.
import { generateKeyPairSync, publicEncrypt, privateDecrypt, randomBytes,
         createCipheriv, createDecipheriv, constants } from "node:crypto";

// CF16: the asymmetric scheme reduces the secret item count from n(n-1)/2 to n, and none
//       of the secret items are transported; the only thing transported is the public key, and it is not secret.

const printRow = (g, ...s) => console.log(s.map((v, i) =>
  (g[i] < 0 ? String(v).padEnd(-g[i]) : String(v).padStart(g[i]))).join(""));
const format = (x) => x >= 1e7 ? x.toExponential(2) : Math.round(x).toLocaleString("en-US");
const A = [12, 12, 11, 11, 20, 20];
printRow(A, "participant", "symmetric", "asymmetric", "asymmetric", "transported secret", "transported secret");
printRow(A, "", "secret item", "secret", "public", "item (sym)", "item (asym)");
for (const n of [2, 7, 40, 12_401]) {
  const c = (n * (n - 1)) / 2;
  printRow(A, format(n), format(c), format(n), format(n), format(c), 0);
}

const { publicKey: PUBLIC, privateKey: PRIVATE } = generateKeyPairSync("rsa", { modulusLength: 2048 });
const OAEP = { oaepHash: "sha256", padding: constants.RSA_PKCS1_OAEP_PADDING };
const BATCH = Buffer.alloc(4096, 65);                // fictional daily reading batch
const CAP = 190;                                     // OAEP-SHA256 upper bound

const key = randomBytes(32), nonce = randomBytes(12);
const cipher = createCipheriv("aes-256-gcm", key, nonce);
const body = Buffer.concat([cipher.update(BATCH), cipher.final()]), tag = cipher.getAuthTag();
const envelope = publicEncrypt({ key: PUBLIC, ...OAEP }, key);
const decipher = createDecipheriv("aes-256-gcm", privateDecrypt({ key: PRIVATE, ...OAEP }, envelope), nonce);
decipher.setAuthTag(tag);
const recovered = Buffer.concat([decipher.update(body), decipher.final()]);

console.log(`\nif a ${BATCH.length}-byte batch were transported two ways:`);
const B = [-16, 12, 12, 14, 10];
printRow(B, "path", "asymmetric", "symmetric", "output bytes", "overhead");
const direct = Math.ceil(BATCH.length / CAP);
printRow(B, "direct RSA", direct, 0, format(direct * 256),
    Math.round((direct * 256 / BATCH.length - 1) * 100) + "%");
const hybrid = envelope.length + nonce.length + body.length + tag.length;
printRow(B, "hybrid", 1, 1, format(hybrid), Math.round((hybrid / BATCH.length - 1) * 100) + "%");
console.log("did the hybrid path return the batch unchanged:", recovered.equals(BATCH) ? "yes" : "no");
```

```
 participant   symmetric asymmetric asymmetric  transported secret  transported secret
             secret item     secret     public          item (sym)         item (asym)
           2           1          2          2                   1                   0
           7          21          7          7                  21                   0
          40         780         40         40                 780                   0
      12,401     7.69e+7     12,401     12,401             7.69e+7                   0

if a 4096-byte batch were transported two ways:
path              asymmetric   symmetric  output bytes  overhead
direct RSA                22           0         5,632       38%
hybrid                     1           1         4,380        7%
did the hybrid path return the batch unchanged: yes
```

The first table's second and third columns close the previous lesson's question. For 12,401
participants, the symmetric scheme needs 7.69×10^7 secret items while the asymmetric scheme
needs 12,401 — dropping from quadratic to linear. The public key count is also 12,401, but
those items are not secret, and the management cost of a non-secret item is an entirely
different matter.

The real number is in the last column, and it is zero. In the symmetric scheme, every secret
item has to be **transported** to both sides; in a 12,401-endpoint network, that means
7.69×10^7 transports, and that was the source of the manual items counted in the previous
lesson. In the asymmetric scheme, no secret item is transported: each side generates its own
private key itself and sends it nowhere. The only thing transported is the public key, and it
can travel over an open channel.

The second table shows how the size limit is worked around. If a 4096-byte batch is
transported directly with asymmetric encryption, 22 separate operations are needed and the
output rises to 5,632 bytes, a 38 percent overhead. On the hybrid path, asymmetric encryption
transports only a 32-byte symmetric key; the batch itself is encrypted with a symmetric mode.
The operation count drops to one, the output becomes 4,380 bytes, and the overhead falls to 7
percent. The last line confirms that this path actually works.

The difference in operation count is not just a byte calculation. Asymmetric operations are
many times more expensive than symmetric operations, and the direct path repeats this
expensive operation 22 times for a 4-kilobyte batch. On the hybrid path, the expensive
operation is done once and the whole batch is carried by the cheap operation. If the fictional
network processes 12,400 batches a day, the difference between the two paths is 260,400 extra
asymmetric operations a day.

The hybrid scheme's split is clear: asymmetric encryption **carries the key**, symmetric
encryption **carries the data**. Each one's chain stays as it was in its own lesson; on the
hybrid path, the chain gets longer, because now both the private key's secrecy and the
symmetric chain's four items have to be correct at the same time.

## Why Key Lengths Cannot Be Compared

The symmetric key here was 256 bits, the asymmetric key 2048 bits. The numbers cannot be read on
the same scale, because the two schemes' security comes from different places. With a
symmetric key, the only path is trying the key one by one, and the cost is 2^256. With an
asymmetric key, the public key is mathematically linked to the private key, and there are
cheaper ways to break that link than trying one by one; the key length is made larger to
counter those cheaper paths.

| Scheme | Key bits | Equivalent symmetric security | Key/output size |
|---|---|---|---|
| symmetric | 128 | 128 bit | 16 bytes |
| symmetric | 256 | 256 bit | 32 bytes |
| factoring-based | 2048 | approximately 112 bit | 256 bytes |
| factoring-based | 3072 | approximately 128 bit | 384 bytes |
| elliptic-curve-based | 256 | approximately 128 bit | 32 bytes |

The table reads as follows: a 2048-bit asymmetric key does not come close to the security a
256-bit symmetric key provides; it is equivalent to roughly a 112-bit symmetric key. To raise
the equivalence, the asymmetric key has to be pushed up to 3072 bits, and that carries every
operation's output to 384 bytes.

The last line is a separate option. In the elliptic-curve-based scheme, the same equivalence is
reached with a 256-bit key, that is, 32 bytes. In the fictional measurement network, this
difference translates directly into an item: if 12,400 endpoints each carry a 256-byte envelope
per message, the daily load is eight times what it would be with a 32-byte envelope. On
constrained endpoints, what decides the choice is not the algorithm's name, it is this size
arithmetic.

## The Root's Name and the Unproven Item

The trust root here is a single item: the private key existing only with its owner. This item
is born the moment the key is generated, and where that generation happens is the root's first
item. If the key pair is generated at the center and distributed to the endpoints, the private
key has been transported at least once, and the previous lesson's transport problem comes back;
if each endpoint generates its own pair itself, the transported secret item count stays at
zero. Generating it yourself has its own condition: the endpoint's random number generator has
to be unpredictable. In a pair generated with a predictable generator, the private key can be
regenerated even if it never leaks at all. The item count that falls when the root breaks is
different from the symmetric scheme. If a private key leaks, every past and future message sent
to that key's owner drops; in a network with 12,401 participants, that is all the traffic
reaching the center. Renewal, though, is cheap — a new pair is generated and the **public** key
is distributed; no secret item is transported.

Where the root sits is a separate item count. If the private key sits in a file at the center,
the items that can reach it can be counted one by one: the file's permission record, the memory
of the process reading the file, its backup copy, the medium the backup travels on, and the
copy the distribution automation accesses — five items. All five of the five have to be kept
correct at the same time, and a flaw in any one of them opens the root.

Generating and keeping the key inside a **hardware security module** it never leaves changes
this count. Since the key never leaves the module, there is no file to read, no backup to take,
and no copy to transport; a single item remains — authorization to use the module. The item
count drops from five to one. The cost is countable too: since the key cannot be exported, it
cannot be backed up either, and if the module is lost, the key is lost permanently —
renewal means public key distribution at 12,401 endpoints.

But there is one more item in the chain that has not been named, and this lesson does not close
it. A meter endpoint uses the center's public key to send an encrypted reading to the center.
Nothing proves that public key really belongs to the center. Since the public key is not secret,
it comes over an open channel, and anything that comes over an open channel can be altered. A
reading encrypted with the wrong public key is read by whoever holds that key's private
counterpart.

This item is the entire subject of the course's second topic. In this lesson it can only be
counted: two of four combinations work, the secret item count drops to linear, the transported
secret item is zero — and **the public key's owner is still unverified**.

## Summary

- The public and private key are generated together; two of four key-operation combinations
  work, and an operation done with one key is not reversed with the same key.
- Encrypting with the public key gives secrecy; encrypting with the private key does not give
  secrecy, it carries a source claim.
- In a 2048-bit modulus, the largest plaintext that can be transported is 190 bytes, and the
  output is 256 bytes no matter what the input is.
- The asymmetric scheme drops the secret item count from 7.69×10^7 to 12,401 at 12,401
  participants; the transported secret item count is zero.
- In hybrid use, asymmetric encryption carries the key, symmetric encryption carries the data:
  for a 4096-byte batch, the operation count drops from 22 to 1, and the overhead drops from 38
  percent to 7 percent.
- The trust root is the private key's secrecy; who the public key belongs to is not proven in
  this scheme.

## Next Step

The hybrid path transported the key, but the side that sent the transported key **chose** it.
This has two consequences. First, the session key's quality depends entirely on that side's
generator. Second, and heavier: recorded encrypted traffic is stored, and if the center's
private key leaks years later, every session key up to that point is extracted back out of its
envelope, and all past traffic is opened. The next lesson actually runs, with small parameters,
a method that removes both of these consequences at once: a shared secret that both sides
contribute to, that never crosses the channel, and that neither side determines on its own. The
items left in the eavesdropper's hands will be counted one by one.
