node-opcua/node-opcua-crypto

View on GitHub
packages/node-opcua-crypto/source/crypto_utils2.ts

Summary

Maintainability
C
1 day
Test Coverage
// ---------------------------------------------------------------------------------------------------------------------
// node-opcua-crypto
// ---------------------------------------------------------------------------------------------------------------------
// Copyright (c) 2014-2022 - Etienne Rossignon - etienne.rossignon (at) gadz.org
// Copyright (c) 2022-2024 - Sterfive.com
// ---------------------------------------------------------------------------------------------------------------------
//
// This  project is licensed under the terms of the MIT license.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
// documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so,  subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
// Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ---------------------------------------------------------------------------------------------------------------------

// tslint:disabled:no-var-requires
/**
 * @module node_opcua_crypto
 */
import assert from "assert";

import { KeyObject, isKeyObject } from "./common.js";

import { PublicKey, PublicKeyPEM, PrivateKeyPEM, PrivateKey } from "./common.js";
import { removeTrailingLF, toPem } from "./crypto_utils.js";

import  jsrsasign from "jsrsasign";




/***
 * @method rsaLengthPrivateKey
 * A very expensive way to determine the rsa key length ( i.e 2048bits or 1024bits)
 * @param key  a PEM public key or a PEM rsa private key
 * @return the key length in bytes.
 */
export function rsaLengthPrivateKey(key: PrivateKey): number {
    const keyPem = typeof key.hidden === "string" ? key.hidden : key.hidden.export({ type: "pkcs1", format: "pem" }).toString();
    // in node 16 and above :
    // return o.asymmetricKeyDetails.modulusLength/8
    // in node <16 :
    const a = jsrsasign.KEYUTIL.getKey(keyPem) as any;
    return a.n.toString(16).length / 2;
}

/**
 * @method toPem2
 * @param raw_key
 * @param pem
 *
 *
 * @return a PEM string containing the Private Key
 *
 * Note:  a Pem key can be converted back to a private key object using coercePrivateKey
 *
 */
export function toPem2(raw_key: Buffer | string | KeyObject | PrivateKey, pem: string): string {
    if ((raw_key as PrivateKey).hidden) {
        return toPem2((raw_key as PrivateKey).hidden, pem);
    }
    assert(raw_key, "expecting a key");
    assert(typeof pem === "string");

    if (isKeyObject(raw_key)) {
        const _raw_key = raw_key as KeyObject;
        if (pem === "RSA PRIVATE KEY") {
            return removeTrailingLF(_raw_key.export({ format: "pem", type: "pkcs1" }).toString());
        } else if (pem === "PRIVATE KEY") {
            return removeTrailingLF(_raw_key.export({ format: "pem", type: "pkcs8" }).toString());
        } else {
            throw new Error("Unsupported case!");
        }
    }
    return toPem(raw_key as (Buffer | string), pem);
}

export function coercePrivateKeyPem(privateKey: PrivateKey): PrivateKeyPEM {
    return toPem2(privateKey, "PRIVATE KEY");
    /*
    if (privateKey.hidden instanceof Buffer) {
        const o = createPrivateKeyFromNodeJSCrypto({ key: privateKey.hidden, format: "der", type: "pkcs1" });
        const e = o.export({ format: "der", type: "pkcs1" });
        return toPem(e, "RSA PRIVATE KEY");
    } else if (privateKey.hidden instanceof KeyObject) {
        const o = privateKey.hidden.export({ format: "pem", type: "pkcs1" });
        return o.toString();
    } else if (typeof privateKey.hidden === "string") {
        return privateKey.hidden;
    }
    throw new Error("Invalid privateKey");
    */
}

export function coercePublicKeyPem(publicKey: PublicKey | PublicKeyPEM): PublicKeyPEM {
    if (isKeyObject(publicKey)) {
        return (publicKey as KeyObject).export({ format: "pem", type: "spki" }).toString();
    }
    assert(typeof publicKey === "string");
    return publicKey;
}
export function coerceRsaPublicKeyPem(publicKey: PublicKey | KeyObject | PublicKeyPEM): PublicKeyPEM {
    if (isKeyObject(publicKey)) {
       return (publicKey as KeyObject).export({ format: "pem", type: "spki" }).toString();
    }
    assert(typeof publicKey === "string");
    return publicKey;
}

export function rsaLengthPublicKey(key: PublicKeyPEM | PublicKey): number {
    key = coercePublicKeyPem(key);
    assert(typeof key === "string");
    const a = jsrsasign.KEYUTIL.getKey(key) as any;
    return a.n.toString(16).length / 2;
}
export function rsaLengthRsaPublicKey(key: PublicKeyPEM | PublicKey): number {
    key = coerceRsaPublicKeyPem(key);
    assert(typeof key === "string");
    const a = jsrsasign.KEYUTIL.getKey(key) as any;
    return a.n.toString(16).length / 2;
}