I create a js function which manual create asn.1 pkcs1 der format of RSA public key to achieve this.
/**
* create a public key object from n and e
* @param {*} n RSA modulus
* @param {*} e RSA public exponent
* @returns Crypto.keyObject, see https://nodejs.org/api/crypto.html
*/
function create_crypto_public_key_object(n, e) {
// The n hex value should always prefixed 00, why?
if (!n.startsWith('00')) {
n = `00${n}`
}
const hex_length = (hex_str) => {
// fix the data if the length is even
if (hex_str.length % 2 == 1) {
hex_str = `0${hex_str}`
}
hex_str_length = (hex_str.length / 2).toString(16)
// if the length is even like '101', then the expected returns should be '0101'
if (hex_str_length.length % 2 == 1) {
hex_str_length = `0${hex_str_length}`
}
// extra length byte is needed if the length is larger than 127
// see http://luca.ntop.org/Teaching/Appunti/asn1.html
if (hex_str_length.length / 2 > 1) {
hex_str_length = `${(0x80 | hex_str_length.length / 2).toString(16)}${hex_str_length}`
}
return hex_str_length
}
// https://polarssl.org/kb/cryptography/asn1-key-structures-in-der-and-pem/
// The ASN.1 structure for a public key is:
// RSAPublicKey ::= SEQUENCE {
// modulus INTEGER, -- n
// publicExponent INTEGER -- e
// }
asn1_part_n_hex_string = `02${hex_length(n)}${n}`
asn1_part_e_hex_string = `02${hex_length(e)}${e}`
asn1_hex_string = `30${hex_length(asn1_part_n_hex_string + asn1_part_e_hex_string)}${asn1_part_n_hex_string}${asn1_part_e_hex_string}`;
return crypto.createPublicKey({ key: Buffer.from(asn1_hex_string, 'hex'), format: 'der', type: 'pkcs1' });
}
/**
*
* @param {*} n
* @param {*} e
* @param {*} type, can be 'pkcs1' (RSA only), 'pkcs8' or 'sec1' (EC only)
* @param {*} format can be 'pem', 'der', or 'jwk',
* @returns
*/
function create_public_key(n, e, type = 'spki', format = 'pem') {
key_object = create_crypto_public_key_object(n, e)
// see https://nodejs.org/api/crypto.html#keyobjectexportoptions
publicKey = key_object.export({ type, format })
return publicKey;
}
// Test for short n, which the length is one byte
n1 = '0086fa9ba066685845fc03833a9699c8baefb53cfbf19052a7f10f1eaa30488cec1ceb752bdff2df9fad6c64b3498956e7dbab4035b4823c99a44cc57088a23783'
// Test for long n, which the length is multi bytes, the first byte is 0x80 bitwise and with the bytes count of the n
n2 = 'B347A3185DE515D6E123A94CDA2DB2884892A7F27D40B536A8E258F4DF8531029A2997F37994940E1CBAE09E96975482CCB99C37E71E6B83E86EEE1AC82F73CA084D3354765EE9B671DAB0E9DE5F2EDB798DF88CFA4C6586F84440A66AEEAD352901BE9CE8F49872E9DA53A9329F2197128F097CD3ECA99C91B93032F3D30F655C1C540BC71BD53DA7BE933433367FFE247BC0D51CF5905395589079B6B98AC5826741BF08762937F4B56C30669778E2EFE58565D029040E96579488468693A81B85FBC29641DD55A39254FBB7E1DF9F4F1540125C233758DB3C0BCFADEFF7A9FC3CAE2366B419776B35BE60CE8BBE1460F84C74AC068951FD26AD5EE6EB6BE1'
e = '010001'
publicKey1 = create_public_key(n1, e);
publicKey2 = create_public_key(n2, e);
console.info(`publicKey1: ${publicKey1}`)
console.info(`publicKey2: ${publicKey2}`)
encryptedText1 = crypto.publicEncrypt(publicKey1, Buffer.from(data))
console.info(`Encrypted text 1: ${encryptedText1.toString('base64')}`);
encryptedText2 = crypto.publicEncrypt(publicKey2, Buffer.from(data))
console.info(`Encrypted text 2: ${encryptedText2.toString('base64')}`);