I'm trying to generate a signed URL for a CloudFront resource in my Nest.js application using the AWS SDK, but I'm encountering an error that I can't seem to resolve. The error message I'm receiving is:
Error: error:1E08010C:DECODER routines::unsupported
at Sign.sign (node:internal/crypto/sig:131:29)
at signPolicy (C:\Full Stack JS Apps\testCloudfront\api\node_modules\aws-sdk\lib\cloudfront\signer.js:21:29)
at signWithCannedPolicy (C:\Full Stack JS Apps\testCloudfront\api\node_modules\aws-sdk\lib\cloudfront\signer.js:37:20)
at Signer.getSignedUrl (C:\Full Stack JS Apps\testCloudfront\api\node_modules\aws-sdk\lib\cloudfront\signer.js:186:19)
at S3Service.generateSignedUrl (C:\Full Stack JS Apps\testCloudfront\api\src\common\services\aws\s3\s3.service.ts:57:36)
at processTicksAndRejections (node:internal/process/task_queues:95:5)
at AppController.getFile (C:\Full Stack JS Apps\testCloudfront\api\src\app.controller.ts:24:17) {
library: 'DECODER routines',
reason: 'unsupported',
code: 'ERR_OSSL_UNSUPPORTED'
}
Here's the relevant code that I'm using to generate the signed URL:
async generateSignedUrl(objectKey: string): Promise<string> {
const cloudFrontDomain = 'my-alternate-domain.com';
const keyPairId = 'MyAWSCFPublicKeyId';
try {
const privateKey = await this.getCloudFrontPrivateKey();
const cloudFront = new CloudFront.Signer(keyPairId, privateKey);
console.log('Signer:', cloudFront);
const signedUrl = cloudFront.getSignedUrl({
url: `https://${cloudFrontDomain}/${objectKey}`,
expires: Math.floor(Date.now() / 1000) + 3600, // URL expiration time (1 hour)
});
return signedUrl;
} catch (error) {
console.log('Error generating signed URL:', error);
throw error;
}
}
My private key is stored in AWS Secret Manager. Here's the code I'm using to retrieve the private key:
private async getCloudFrontPrivateKey(): Promise<string> {
try {
const secretsManager = new SecretsManager({
accessKeyId: 'MY_ACCESS_KEY',
secretAccessKey: 'MY_SECRET_ACCESS_KEY',
region: 'us-west-1',
});
const response = await secretsManager
.getSecretValue({ SecretId: 'test/key' })
.promise();
if (response.SecretString) {
const secretData = JSON.parse(response.SecretString);
return secretData.privateKey;
} else {
throw new Error('SecretString is empty');
}
} catch (error) {
console.log('Error fetching CloudFront private key:', error);
throw error;
}
}
Sample Signer Output when logging: When I log the cloudFront object (Signer) using console.log('Signer:', cloudFront);, the output looks like this:
Signer: Signer {
keyPairId: 'MyAWSCFPublicKeyId',
privateKey: '-----BEGIN RSA PRIVATE KEY----- contents here -----END RSA PRIVATE KEY-----'
}
Additional Information:
- I already tried this
SET NODE_OPTIONS=--openssl-legacy-provider - I generated the public-private key pair locally using the ff command:
openssl genrsa -out private_key.pem 2048
openssl rsa -pubout -in private_key.pem -out public_key.pem
- I'm using node v18.15.0 and nest v9.5.0
The issue lies with the private key. The line breaks are removed if you store it in AWS Secrets Manager. Therefore, I did base64 encode the private key and decode it in my code. The code will appear as follows: