Finally after multiple communications with SF and AWS (Cloudfront & JDK) support teams, we managed to successfully generate the cloudfront signed URL to access the private resource in S3..
here's the code snippet that worked for us..
//endpoint parameter - resource path inside the S3 folderpublic static string getCFsignedURL(String endpoint) { //key pair id from S3 String keyPairId = Key_Pair_Id__c; //the private key from pkcs8 file Blob privateKey = EncodingUtil.base64Decode(Private_Key__c); //cloudfront full path to sign String urlToSign = Cloudfront_Domain__c + endpoint; //Expiry time Datetime dt = Datetime.now(); Long longTime = dt.getTime(); Long expiryLong = (longTime / 1000) + 3600; String Expiry = String.valueOf(expiryLong); //Policy Statement for the cloudfront full path with the expiry time String policyStatementText = '{"Statement":[{"Resource":"'+ urlToSign +'","Condition":{"DateLessThan":{"AWS:EpochTime":'+ Expiry +'}}}]}'; String policyStatement = EncodingUtil.base64Encode(Blob.valueOf(policyStatementText )); policyStatement = policyStatement.replace('+','-'); policyStatement = policyStatement.replace('=','_'); policyStatement = policyStatement.replace('/','~'); //genarate the signature Blob mac = Crypto.sign('RSA-SHA1', blob.valueof(policyStatementText),privateKey ); String signed = EncodingUtil.base64Encode(mac); signed = signed.replace('+','-'); signed = signed.replace('=','_'); signed = signed.replace('/','~'); String signedURL = urlToSign +'?'+'Policy='+ policyStatement +'&Signature='+ signed +'&Key-Pair-Id='+ keyPairId ; return signedURL;}