Skip to content

Commit

Permalink
feat: Support PEM certificates for (#4473)
Browse files Browse the repository at this point in the history
  • Loading branch information
marikaner authored Feb 7, 2024
1 parent e4d04ea commit 36be489
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 24 deletions.
5 changes: 5 additions & 0 deletions .changeset/tasty-shoes-joke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@sap-cloud-sdk/connectivity": minor
---

[New Functionality] Support certificates in PEM format for `ClientCertificateAuthentication`.
67 changes: 45 additions & 22 deletions packages/connectivity/src/http-agent/http-agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,24 +114,29 @@ function getTrustStoreOptions(destination: HttpDestination): {

/**
* @internal
* The http agents (proxy and default) use node tls for the certificate handling. This method creates the options with the pfx and passphrase.
* The http agent uses node tls for the certificate handling. This method creates the options with the pfx and passphrase or key, cert and passphrase, depending on the format of the certificate.
* https://nodejs.org/api/tls.html#tls_tls_createsecurecontext_options
* @param destination - Destination object
* @param destination - Destination object.
* @returns Options, which can be used later by tls.createSecureContext() e.g. pfx and passphrase or an empty object, if the protocol is not 'https:' or no client information are in the definition.
*/
function getKeyStoreOptions(destination: Destination): {
pfx?: Buffer;
passphrase?: string;
} {
function getKeyStoreOptions(destination: Destination):
| {
pfx?: Buffer;
passphrase?: string;
}
| {
cert?: Buffer;
key?: Buffer;
passphrase?: string;
} {
if (
// Only add certificates, when using MTLS (https://github.com/SAP/cloud-sdk-js/issues/3544)
// Only add certificates, when using ClientCertificateAuthentication (https://github.com/SAP/cloud-sdk-js/issues/3544)
destination.authentication === 'ClientCertificateAuthentication' &&
// pfx is an alternative to providing key and cert individually
// For mTLS we provide key and cert, in non-mTLS cases we provide pfx
!mtlsIsEnabled(destination) &&
destination.keyStoreName
) {
const certificate = selectCertificate(destination);
validateFormat(certificate);

logger.debug(`Certificate with name "${certificate.name}" selected.`);

Expand All @@ -140,8 +145,21 @@ function getKeyStoreOptions(destination: Destination): {
`Destination '${destination.name}' does not have a keystore password.`
);
}

const certBuffer = Buffer.from(certificate.content, 'base64');

// if the format is pem, the key and certificate needs to be passed separately
// it could be required to separate the string into two parts, but this seems to work as well
if (getFormat(certificate) === 'pem') {
return {
cert: certBuffer,
key: certBuffer,
passphrase: destination.keyStorePassword
};
}
// pfx is a format that combines key and cert
return {
pfx: Buffer.from(certificate.content, 'base64'),
pfx: certBuffer,
passphrase: destination.keyStorePassword
};
}
Expand Down Expand Up @@ -169,7 +187,10 @@ export interface MtlsOptions {
async function getMtlsOptions(
destination: Destination
): Promise<MtlsOptions | Record<string, never>> {
if (!mtlsIsEnabled(destination) && destination.mtls) {
if (
destination.mtls &&
!(process.env.CF_INSTANCE_CERT && process.env.CF_INSTANCE_KEY)
) {
logger.warn(
`Destination ${
destination.name ? destination.name : ''
Expand Down Expand Up @@ -202,14 +223,10 @@ function mtlsIsEnabled(destination: Destination) {
/*
The node client supports only these store formats https://nodejs.org/api/tls.html#tlscreatesecurecontextoptions.
*/
const supportedCertificateFormats = ['p12', 'pfx'];
const supportedCertificateFormats = ['p12', 'pfx', 'pem'];

function hasSupportedFormat(certificate: DestinationCertificate): boolean {
const certificateFormat = last(certificate.name.split('.'));
if (certificateFormat) {
return supportedCertificateFormats.includes(certificateFormat);
}
return false;
function isSupportedFormat(format: string | undefined): boolean {
return !!format && supportedCertificateFormats.includes(format);
}

function selectCertificate(destination): DestinationCertificate {
Expand All @@ -223,8 +240,16 @@ function selectCertificate(destination): DestinationCertificate {
);
}

if (!hasSupportedFormat(certificate)) {
const format: string | undefined = last(certificate.name.split('.'));
return certificate;
}

function getFormat(certificate: DestinationCertificate): string | undefined {
return last(certificate.name.split('.'));
}

function validateFormat(certificate: DestinationCertificate) {
const format = getFormat(certificate);
if (!isSupportedFormat(format)) {
throw Error(
`The format of the provided certificate '${
certificate.name
Expand All @@ -237,8 +262,6 @@ function selectCertificate(destination): DestinationCertificate {
}`
);
}

return certificate;
}

/**
Expand Down
4 changes: 2 additions & 2 deletions packages/http-client/src/http-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,8 @@ export function buildHttpRequestConfigWithOrigin(
}

/**
* This method does nothing and is only there to indicated that the call was made by Odata or OpenApi client and encoding is already done on filter and key parameters.
* @param params - Parameters which are returned
* This method does nothing and is only there to indicate that the call was made by an OData or OpenApi client and encoding is already done on filter and key parameters.
* @param params - Parameters which are returned.
* @returns The parameters as they are without encoding.
* @internal
*/
Expand Down

0 comments on commit 36be489

Please sign in to comment.