diff --git a/packages/grpc-js/src/channel-credentials.ts b/packages/grpc-js/src/channel-credentials.ts index 02c9c309..94b66c1b 100644 --- a/packages/grpc-js/src/channel-credentials.ts +++ b/packages/grpc-js/src/channel-credentials.ts @@ -15,7 +15,7 @@ * */ -import { ConnectionOptions, createSecureContext, PeerCertificate } from 'tls'; +import { ConnectionOptions, createSecureContext, PeerCertificate, SecureContext } from 'tls'; import { CallCredentials } from './call-credentials'; import { CIPHER_SUITES, getDefaultRootsData } from './tls-helpers'; @@ -110,6 +110,7 @@ export abstract class ChannelCredentials { * @param rootCerts The root certificate data. * @param privateKey The client certificate private key, if available. * @param certChain The client certificate key chain, if available. + * @param verifyOptions Additional options to modify certificate verification */ static createSsl( rootCerts?: Buffer | null, @@ -130,14 +131,35 @@ export abstract class ChannelCredentials { 'Certificate chain must be given with accompanying private key' ); } + const secureContext = createSecureContext({ + ca: rootCerts ?? getDefaultRootsData() ?? undefined, + key: privateKey ?? undefined, + cert: certChain ?? undefined, + ciphers: CIPHER_SUITES, + }); return new SecureChannelCredentialsImpl( - rootCerts || getDefaultRootsData(), - privateKey || null, - certChain || null, - verifyOptions || {} + secureContext, + verifyOptions ?? {} ); } + /** + * Return a new ChannelCredentials instance with credentials created using + * the provided secureContext. The resulting instances can be used to + * construct a Channel that communicates over TLS. gRPC will not override + * anything in the provided secureContext, so the environment variables + * GRPC_SSL_CIPHER_SUITES and GRPC_DEFAULT_SSL_ROOTS_FILE_PATH will + * not be applied. + * @param secureContext The return value of tls.createSecureContext() + * @param verifyOptions Additional options to modify certificate verification + */ + static createFromSecureContext(secureContext: SecureContext, verifyOptions?: VerifyOptions): ChannelCredentials { + return new SecureChannelCredentialsImpl( + secureContext, + verifyOptions ?? {} + ) + } + /** * Return a new ChannelCredentials instance with no credentials. */ @@ -170,18 +192,10 @@ class SecureChannelCredentialsImpl extends ChannelCredentials { connectionOptions: ConnectionOptions; constructor( - private rootCerts: Buffer | null, - private privateKey: Buffer | null, - private certChain: Buffer | null, + private secureContext: SecureContext, private verifyOptions: VerifyOptions ) { super(); - const secureContext = createSecureContext({ - ca: rootCerts || undefined, - key: privateKey || undefined, - cert: certChain || undefined, - ciphers: CIPHER_SUITES, - }); this.connectionOptions = { secureContext }; @@ -210,19 +224,10 @@ class SecureChannelCredentialsImpl extends ChannelCredentials { return true; } if (other instanceof SecureChannelCredentialsImpl) { - if (!bufferOrNullEqual(this.rootCerts, other.rootCerts)) { - return false; - } - if (!bufferOrNullEqual(this.privateKey, other.privateKey)) { - return false; - } - if (!bufferOrNullEqual(this.certChain, other.certChain)) { - return false; - } return ( - this.verifyOptions.checkServerIdentity === - other.verifyOptions.checkServerIdentity - ); + this.secureContext === other.secureContext && + this.verifyOptions.checkServerIdentity === other.verifyOptions.checkServerIdentity + ); } else { return false; } diff --git a/packages/grpc-js/src/index.ts b/packages/grpc-js/src/index.ts index c4c774d9..4bf1d936 100644 --- a/packages/grpc-js/src/index.ts +++ b/packages/grpc-js/src/index.ts @@ -109,6 +109,7 @@ export const credentials = { // from channel-credentials.ts createInsecure: ChannelCredentials.createInsecure, createSsl: ChannelCredentials.createSsl, + createFromSecureContext: ChannelCredentials.createFromSecureContext, // from call-credentials.ts createFromMetadataGenerator: CallCredentials.createFromMetadataGenerator,