Skip to content
Open
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
2111a4a
initial commit
jjhoughton Jul 6, 2019
2a6660a
returns all the things about an rsa key
jjhoughton Jul 6, 2019
97776de
package.json: change package name
jjhoughton Jul 6, 2019
e8f5235
add lgpl license
jjhoughton Jul 6, 2019
3d60bb9
add some x509 stuff
jjhoughton Jul 6, 2019
c30fa20
get public key from x509 cert
jjhoughton Jul 6, 2019
f9970b9
change the name of some files
jjhoughton Jul 6, 2019
3a66b85
add some missing files
jjhoughton Jul 6, 2019
8f44825
fix typo in error message
jjhoughton Jul 6, 2019
ef764c3
add readme
jjhoughton Jul 6, 2019
243fecd
sign copyright over to ripjar
jjhoughton Jul 7, 2019
9ed5dc5
consider the size of the cert/key before allocating memory
jjhoughton Jul 7, 2019
ec99d71
do a bit more error checking
jjhoughton Jul 7, 2019
7e4eddd
add the ability to specify a passphase for the private key (#2)
jjhoughton Nov 26, 2019
a9e37ba
fix rsa_st not being defined in node12
jjhoughton May 16, 2020
0e6d9ef
Get working on openssl 3.0
jjhoughton Jun 28, 2023
6c152fe
Updated minor version
adamjjeffery Mar 4, 2024
e91b97c
Added release workflow (#9)
adamjjeffery Mar 5, 2024
cc8b710
Automatically synchronising release.yml from develop to master
Mar 10, 2024
17fae10
Updates .github/workflows/release.yml with new #li-releases channel id
adamjjeffery Apr 30, 2024
c11f236
Updates .github/workflows/release.yml with new #li-releases channel id
adamjjeffery Apr 30, 2024
8f0f5c8
Merge pull request #11 from ripjar/master
jammie1903 Sep 12, 2024
ef1c03a
use middleman workflows
Jan 14, 2025
efb9017
Merge pull request #12 from ripjar/chore/use-middleman-workflows
adammartin-ripjar Jan 15, 2025
b089c34
Botjar: Merged branch develop into master
rj-bot-1 Jul 30, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
build/
node_modules
*~
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Node openssl

This was created so that we could verify the modulus of a public and private key
match. At the moment it has two functions RSAPrivateKey and X509PublicKey. The
first reads an rsa private key and the second reads from an x509 cert.

### The output of RSAPrivateKey is as follows:
```
{
n: (hex string) // public modulus
e: (hex string) // public exponent
d: (hex string) // private exponent
p: (hex string) // secret prime factor
q: (hex string) // secret prime factor
dmp1: (hex string) // d mod (p-1)
dmq1: (hex string) // d mod (q-1)
iqmp: (hex string) // q^-1 mod p
}
```

### The output of X509PublicKey is as follows:
```
{
n: (hex string) // public modulus
e: (hex string) // public exponent
}
```

## Licence

This is licenced under the GNU Lesser General Public License version 2. See
lgpl-2.1.txt for more details.
8 changes: 8 additions & 0 deletions binding.gyp
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"targets": [
{
"target_name": "node_openssl",
"sources": [ "./main.c" ]
}
]
}
502 changes: 502 additions & 0 deletions lgpl-2.1.txt

Large diffs are not rendered by default.

294 changes: 294 additions & 0 deletions main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,294 @@
/**
* Copyright (C) 2019 Ripjar Limited
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.

* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
* or see see <https://www.gnu.org/licenses/>.
*/

#include <node_api.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/bio.h>
#include <openssl/x509.h>

#include <string.h>

static napi_value
x509_cert_pub_key (napi_env env, napi_callback_info info)
{
size_t argc = 1, size;
napi_value argv[argc];
char *buf;
napi_status status = napi_ok;

BIO *bio;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think you should link to the openssl docs were this came from

X509 *x509;
EVP_PKEY *evp_pubkey;
RSA *public_key;
char *n_hex, *e_hex;
napi_value n_val, e_val;
napi_value obj;

if (napi_get_cb_info (env, info, &argc, argv, NULL, NULL) != napi_ok)
{
napi_throw_error (env, NULL, "Failed to parse arguments");
return NULL;
}
if (argc < 1)
{
napi_throw_error (env, NULL, "This function requires one argument");
Copy link
Copy Markdown

@moxonj moxonj Jul 9, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't this say what the argument is supposed to be

return NULL;
}
if (napi_get_value_string_utf8 (env, argv[0], NULL, 0, &size) != napi_ok)
{
napi_throw_error (env, NULL, "Failed to parse string");
Copy link
Copy Markdown

@moxonj moxonj Jul 9, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you add more detail in all these errors?

return NULL;
}
if ((buf = malloc (++size)) == NULL)
{
napi_throw_error (env, NULL, "Failed to allocate memory");
return NULL;
}
if (napi_get_value_string_utf8 (env, argv[0], buf,
size, &size) != napi_ok)
{
napi_throw_error (env, NULL, "Failed to parse string");
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is the same error message as above, id change it so you know where it wrong

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's because it's the same function as above. The one above just checks the size of the string and the second copies the string to a c string

free (buf);
return NULL;
}

if ((bio = BIO_new_mem_buf (buf, size)) == NULL)
{
napi_throw_error (env, NULL, "Failed to copy cert into buffer");
free (buf);
return NULL;
}
if ((x509 = PEM_read_bio_X509 (bio, 0, 0, 0)) == NULL)
{
napi_throw_error (env, NULL, "Failed to read x509 from bio");
BIO_free (bio);
free (buf);
return NULL;
}
if ((evp_pubkey = X509_get_pubkey (x509)) == NULL)
{
napi_throw_error (env, NULL, "Failed to extract public key");
X509_free (x509);
BIO_free (bio);
free (buf);
return NULL;
}
if ((public_key = EVP_PKEY_get1_RSA (evp_pubkey)) == NULL)
{
napi_throw_error (env, NULL, "Faild to extract rsa key");
EVP_PKEY_free (evp_pubkey);
X509_free (x509);
BIO_free (bio);
free (buf);
return NULL;
}

if (public_key->n == NULL || public_key->e == NULL)
{
napi_throw_error (env, NULL, "One or more required values in the "
"public key is null");
RSA_free (public_key);
EVP_PKEY_free (evp_pubkey);
X509_free (x509);
BIO_free (bio);
free (buf);
return NULL;
}

n_hex = BN_bn2hex (public_key->n);
e_hex = BN_bn2hex (public_key->e);

RSA_free (public_key);
EVP_PKEY_free (evp_pubkey);
X509_free (x509);
BIO_free (bio);
free (buf);

status |= napi_create_string_utf8 (env, n_hex, strlen (n_hex), &n_val);
status |= napi_create_string_utf8 (env, e_hex, strlen (e_hex), &e_val);
status |= napi_create_object (env, &obj);

free (n_hex);
free (e_hex);

if (status != napi_ok)
{
napi_throw_error (env, NULL, "Failed to create return object");
return NULL;
}

status |= napi_set_named_property (env, obj, "n", n_val);
status |= napi_set_named_property (env, obj, "e", e_val);

if (status != napi_ok)
{
napi_throw_error (env, NULL, "Failed to assign properties to object");
return NULL;
}

return obj;
}

static napi_value
rsa_priv_key (napi_env env, napi_callback_info info)
{
size_t argc = 1, size;
napi_value argv[argc];
char *buf;
napi_status status = napi_ok;

RSA* private_key;
BIO *bio;
char *n_hex, *e_hex, *d_hex, *p_hex, *q_hex, *dmp1_hex, *dmq1_hex, *iqmp_hex;
napi_value n_val, e_val, d_val, p_val, q_val, dmp1_val, dmq1_val, iqmp_val;
napi_value obj;

if (napi_get_cb_info (env, info, &argc, argv, NULL, NULL) != napi_ok)
{
napi_throw_error (env, NULL, "Failed to parse arguments");
return NULL;
}
if (argc < 1)
{
napi_throw_error (env, NULL, "This function requires one argument");
return NULL;
}
if (napi_get_value_string_utf8 (env, argv[0], NULL, 0, &size) != napi_ok)
{
napi_throw_error (env, NULL, "Failed to parse string");
return NULL;
}
if ((buf = malloc (++size)) == NULL)
{
napi_throw_error (env, NULL, "Failed to allocate memory");
return NULL;
}
if (napi_get_value_string_utf8 (env, argv[0], buf,
size, &size) != napi_ok)
{
napi_throw_error (env, NULL, "Failed to parse string");
free (buf);
return NULL;
}
if ((bio = BIO_new_mem_buf (buf, size)) == NULL)
{
napi_throw_error (env, NULL, "Failed to copy key into buffer");
free (buf);
return NULL;
}
if ((private_key = PEM_read_bio_RSAPrivateKey (bio, 0, 0, 0)) == NULL)
{
napi_throw_error (env, NULL, "Failed to read key from bio");
BIO_free (bio);
free (buf);
return NULL;
}

if (private_key->n == NULL || private_key->e == NULL ||
private_key->d == NULL || private_key->p == NULL ||
private_key->q == NULL || private_key->dmp1 == NULL ||
private_key->dmq1 == NULL || private_key->iqmp == NULL)
{
napi_throw_error (env, NULL, "One or more required values in the "
"private key is null");
RSA_free (private_key);
BIO_free (bio);
free (buf);
return NULL;
}

n_hex = BN_bn2hex (private_key->n);
e_hex = BN_bn2hex (private_key->e);
d_hex = BN_bn2hex (private_key->d);
p_hex = BN_bn2hex (private_key->p);
q_hex = BN_bn2hex (private_key->q);
dmp1_hex = BN_bn2hex (private_key->dmp1);
dmq1_hex = BN_bn2hex (private_key->dmq1);
iqmp_hex = BN_bn2hex (private_key->iqmp);

BIO_free (bio);
RSA_free (private_key);
free (buf);

status |= napi_create_string_utf8 (env, n_hex, strlen (n_hex), &n_val);
status |= napi_create_string_utf8 (env, e_hex, strlen (e_hex), &e_val);
status |= napi_create_string_utf8 (env, d_hex, strlen (d_hex), &d_val);
status |= napi_create_string_utf8 (env, p_hex, strlen (p_hex), &p_val);
status |= napi_create_string_utf8 (env, q_hex, strlen (q_hex), &q_val);
status |= napi_create_string_utf8 (env, dmp1_hex, strlen (dmp1_hex), &dmp1_val);
status |= napi_create_string_utf8 (env, dmq1_hex, strlen (dmq1_hex), &dmq1_val);
status |= napi_create_string_utf8 (env, iqmp_hex, strlen (iqmp_hex), &iqmp_val);
status |= napi_create_object (env, &obj);

free (n_hex);
free (e_hex);
free (d_hex);
free (p_hex);
free (q_hex);
free (dmp1_hex);
free (dmq1_hex);
free (iqmp_hex);

if (status != napi_ok)
{
napi_throw_error (env, NULL, "Failed to create return object");
return NULL;
}

status |= napi_set_named_property (env, obj, "n", n_val);
status |= napi_set_named_property (env, obj, "e", e_val);
status |= napi_set_named_property (env, obj, "d", d_val);
status |= napi_set_named_property (env, obj, "p", p_val);
status |= napi_set_named_property (env, obj, "q", q_val);
status |= napi_set_named_property (env, obj, "dmp1", dmp1_val);
status |= napi_set_named_property (env, obj, "dmq1", dmq1_val);
status |= napi_set_named_property (env, obj, "iqmp", iqmp_val);

if (status != napi_ok)
{
napi_throw_error (env, NULL, "Failed to assign properties to object");
return NULL;
}

return obj;
}

static napi_value
init (napi_env env, napi_value exports) {
napi_value rsa_fn, x509_fn;

if (napi_create_function (env, NULL, 0, rsa_priv_key,
NULL, &rsa_fn) != napi_ok)
napi_throw_error (env, NULL, "Unable to wrap native rsa function");
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These should have return statements all of them. I don't think it stop when it hits an error

if (napi_create_function (env, NULL, 0, x509_cert_pub_key,
NULL, &x509_fn) != napi_ok)
napi_throw_error (env, NULL, "Unable to wrap native x509 function");

if (napi_set_named_property (env, exports, "RSAPrivateKey",
rsa_fn) != napi_ok)
napi_throw_error (env, NULL, "Unable to populate exports with rsa");
if (napi_set_named_property (env, exports, "X509PublicKey",
x509_fn) != napi_ok)
napi_throw_error (env, NULL, "Unable to populate exports with x509");

return exports;
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, init)
1 change: 1 addition & 0 deletions main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require("./build/Release/node_openssl");
16 changes: 16 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "node-openssl",
"version": "0.1.0",
"main": "main.js",
"private": true,
"gypfile": true,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does this do

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes it compile when you run yarn

"scripts": {
"start": "node-gyp configure build && node module.js",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

its weird to have a start script for a lib like this

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't work it needs deleting

"test": "node test.js"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

id have thought you'd use a testing lib to make sure your code meets some expectations, otherwise its jut ascript you run

},
"engines": {
"node" : ">=8.4.0"
},
"author": "Joshua Houghton <joshua.houghton@ripjar.com",
"license": "LGPL"
}
Loading