Skip to content

Commit

Permalink
Add RS256 sign support
Browse files Browse the repository at this point in the history
  • Loading branch information
invisal committed Aug 20, 2020
1 parent 1bbae3e commit dd78423
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 3 deletions.
7 changes: 7 additions & 0 deletions src/binary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ export class RawBinary extends Uint8Array {
return btoa(String.fromCharCode.apply(null, [...this]));
}

base64url(): string {
let a = btoa(String.fromCharCode.apply(null, [...this])).replace(/=/g, "");
a = a.replace(/\+/g, "-");
a = a.replace(/\//g, "_");
return a;
}

base32(): string {
const lookup = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
const trim = [0x0, 0x1, 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f, 0xff];
Expand Down
2 changes: 1 addition & 1 deletion src/rsa/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ export interface RSAOption {
}

export interface RSASignOption {
hash: "sha1" | "sha256";
hash: "sha256";
algorithm: "rsassa-pkcs1-v1_5";
}
17 changes: 17 additions & 0 deletions src/rsa/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,23 @@ export class RSA {
);
}

async sign(
message: Uint8Array | string,
options?: Partial<RSASignOption>,
): Promise<RawBinary> {
const computedOption: RSASignOption = {
...options,
algorithm: "rsassa-pkcs1-v1_5",
hash: "sha256",
};

return await PureRSA.sign(
this.key,
computeMessage(message),
computedOption,
);
}

static parseKey(key: string): RSAKey {
if (key.indexOf("-----BEGIN RSA PRIVATE KEY-----") === 0) {
const trimmedKey = key.substr(31, key.length - 61);
Expand Down
27 changes: 25 additions & 2 deletions src/rsa/rsa_internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { eme_oaep_encode, eme_oaep_decode } from "./eme_oaep.ts";
import { os2ip, i2osp } from "./primitives.ts";
import { concat, random_bytes } from "./../helper.ts";
import { ber_decode, ber_simple } from "./basic_encoding_rule.ts";
import { RawBinary } from "../binary.ts";

/**
* @param n public key modulus
Expand Down Expand Up @@ -120,5 +121,27 @@ export function rsa_pkcs1_verify(
return true;
}

// 3031300d0609608648016503040201050004208041fb8cba9e4f8cc1483790b05262841f27fdcb211bc039ddf8864374db5f53
// 8041fb8cba9e4f8cc1483790b05262841f27fdcb211bc039ddf8864374db5f53
export function rsa_pkcs1_sign(
bytes: number,
n: bigint,
d: bigint,
message: Uint8Array,
): RawBinary {
// deno-fmt-ignore
const oid = [0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00]
const der = [
0x30,
message.length + 2 + oid.length,
...oid,
0x04,
message.length,
...message,
];

const ps = new Array(bytes - 3 - der.length).fill(0xff);
const em = new Uint8Array([0x00, 0x01, ...ps, 0x00, ...der]);

const msg = os2ip(em);
const c = rsaep(n, d, msg);
return new RawBinary(i2osp(c, bytes));
}
12 changes: 12 additions & 0 deletions src/rsa/rsa_js.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
rsa_oaep_decrypt,
rsa_pkcs1_decrypt,
rsa_pkcs1_verify,
rsa_pkcs1_sign,
} from "./rsa_internal.ts";
import { RawBinary } from "./../binary.ts";
import { RSAKey, RSAOption, RSASignOption } from "./common.ts";
Expand Down Expand Up @@ -66,4 +67,15 @@ export class PureRSA {
createHash(options.hash).update(message).digest(),
);
}

static async sign(key: RSAKey, message: Uint8Array, options: RSASignOption) {
if (!key.d) throw "You need private key to sign the message";

return rsa_pkcs1_sign(
key.length,
key.n,
key.d,
createHash(options.hash).update(message).digest(),
);
}
}
45 changes: 45 additions & 0 deletions tests/rsa/rsa.verify.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,48 @@ Deno.test("Verify RSASSA-PKSC1-v1_5", async () => {

assertEquals(verified, true);
});

Deno.test("Sign RSASSA-PKSC1-v1_5", async () => {
const privateKey = `-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAnzyis1ZjfNB0bBgKFMSvvkTtwlvBsaJq7S5wA+kzeVOVpVWw
kWdVha4s38XM/pa/yr47av7+z3VTmvDRyAHcaT92whREFpLv9cj5lTeJSibyr/Mr
m/YtjCZVWgaOYIhwrXwKLqPr/11inWsAkfIytvHWTxZYEcXLgAXFuUuaS3uF9gEi
NQwzGTU1v0FqkqTBr4B8nW3HCN47XUu0t8Y0e+lf4s4OxQawWD79J9/5d3Ry0vbV
3Am1FtGJiJvOwRsIfVChDpYStTcHTCMqtvWbV6L11BWkpzGXSW4Hv43qa+GSYOD2
QU68Mb59oSk2OB+BtOLpJofmbGEGgvmwyCI9MwIDAQABAoIBACiARq2wkltjtcjs
kFvZ7w1JAORHbEufEO1Eu27zOIlqbgyAcAl7q+/1bip4Z/x1IVES84/yTaM8p0go
amMhvgry/mS8vNi1BN2SAZEnb/7xSxbflb70bX9RHLJqKnp5GZe2jexw+wyXlwaM
+bclUCrh9e1ltH7IvUrRrQnFJfh+is1fRon9Co9Li0GwoN0x0byrrngU8Ak3Y6D9
D8GjQA4Elm94ST3izJv8iCOLSDBmzsPsXfcCUZfmTfZ5DbUDMbMxRnSo3nQeoKGC
0Lj9FkWcfmLcpGlSXTO+Ww1L7EGq+PT3NtRae1FZPwjddQ1/4V905kyQFLamAA5Y
lSpE2wkCgYEAy1OPLQcZt4NQnQzPz2SBJqQN2P5u3vXl+zNVKP8w4eBv0vWuJJF+
hkGNnSxXQrTkvDOIUddSKOzHHgSg4nY6K02ecyT0PPm/UZvtRpWrnBjcEVtHEJNp
bU9pLD5iZ0J9sbzPU/LxPmuAP2Bs8JmTn6aFRspFrP7W0s1Nmk2jsm0CgYEAyH0X
+jpoqxj4efZfkUrg5GbSEhf+dZglf0tTOA5bVg8IYwtmNk/pniLG/zI7c+GlTc9B
BwfMr59EzBq/eFMI7+LgXaVUsM/sS4Ry+yeK6SJx/otIMWtDfqxsLD8CPMCRvecC
2Pip4uSgrl0MOebl9XKp57GoaUWRWRHqwV4Y6h8CgYAZhI4mh4qZtnhKjY4TKDjx
QYufXSdLAi9v3FxmvchDwOgn4L+PRVdMwDNms2bsL0m5uPn104EzM6w1vzz1zwKz
5pTpPI0OjgWN13Tq8+PKvm/4Ga2MjgOgPWQkslulO/oMcXbPwWC3hcRdr9tcQtn9
Imf9n2spL/6EDFId+Hp/7QKBgAqlWdiXsWckdE1Fn91/NGHsc8syKvjjk1onDcw0
NvVi5vcba9oGdElJX3e9mxqUKMrw7msJJv1MX8LWyMQC5L6YNYHDfbPF1q5L4i8j
8mRex97UVokJQRRA452V2vCO6S5ETgpnad36de3MUxHgCOX3qL382Qx9/THVmbma
3YfRAoGAUxL/Eu5yvMK8SAt/dJK6FedngcM3JEFNplmtLYVLWhkIlNRGDwkg3I5K
y18Ae9n7dHVueyslrb6weq7dTkYDi3iOYRW8HRkIQh06wEdbxt0shTzAJvvCQfrB
jg/3747WSsf/zBTcHihTRBdAv6OmdhV4/dD5YBfLAkLrd+mX7iE=
-----END RSA PRIVATE KEY-----`;

const key = RSA.parseKey(privateKey);
const rsa = new RSA(key);
let expectedSignature =
"POstGetfAytaZS82wHcjoTyoqhMyxXiWdR7Nn7A29DNSl0EiXLdwJ6xC6AfgZWF1bOsS_TuYI3OG85AmiExREkrS6" +
"tDfTQ2B3WXlrr-wp5AokiRbz3_oB4OxG-W9KcEEbDRcZc0nH3L7LzYptiy1PtAylQGxHTWZXtGz4ht0bAecBgmpdg" +
"XMguEIcoqPJ1n3pIWk_dUZegpqx0Lka21H6XxUTxiy8OcaarA8zdnPUnV6AmNP3ecFawIFYdvJB_cm-GvpCSbr8G8" +
"y_Mllj8f4x9nBH8pQux89_6gUY618iYv7tuPWBFfEbLxtF2pZS6YC1aSfLQxeNe8djT9YjpvRZA";

const signature = await rsa.sign(
"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0",
{ algorithm: "rsassa-pkcs1-v1_5", hash: "sha256" },
);

assertEquals(signature.base64url(), expectedSignature);
});

0 comments on commit dd78423

Please sign in to comment.