0

Trying to create a custom integration from Salesforce to Snowflake and they provide a python snippet, which can be seen here, that is able to generate a jwt to gain authorization. I'm trying to recreate that in Apex and running into one of two problems: either the JWT is considered invalid or I'm getting 'Invalid Crypto Key'. The uncommented createJWTAuth is my latest attempt but as you can see by the two commented out versions of the method, I have been doing as much experimenting as I can to figure out a workable method.

public class SnowflakeServices {

    
    static String queryURL = 'redacted';
    private String getResultsURL = 'redacted';
    
    private static Final Map<String,String> HEADER_MAP = new Map<String,String>
    {
        'User-Agent'=>'testapp/1.0',
        'X-Snowflake-Authorization-Token-Type'=>'KEYPAIR_JWT',
        'Content-Type'=>'application/json',
        'Accept'=>'application/json'
    };
                
    public static HttpResponse submitQuery(String query){
        
        Blob authKey = createJWTAuth();
        HEADER_MAP.put('Authorization','Bearer '+authKey);
        
        PostRequest reqObj = new PostRequest(query);
        String reqBody = json.serialize(reqObj);
        system.debug('Request Body Sent: '+reqBody);
        
        return CalloutUtilities.makePostCall(queryURL, reqBody, HEADER_MAP);
    }    
    
    
    /*****************************************************************************************
     * 
     *      Generate JWT
     * 
     * 
     * ***************************************************************************************/
    
    /*private static String createJWTAuth(){
        
        String key = 'MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDFneJNXi0f5WUiww5LWPwXJ+GttV86Hk+SqnHYlUKMwib4KncwgQXnFdt94lyhB6XBG4jCMjDNjKRbCY34AFyCXI14p0Aav3AQTQi25OqvpHrDlVTm309eNmxOHdn4ejPvrEN2LF1QtygL/AHQ+19FIlmqeHbVhYPYTFpmDHanZKLUiWCLocKXvjgDdBIRQcxdZY1MDeIy/udtmExq64KyGLIMzQIkhVsIm/NZtqyXLCnr5i79vajWJKPDoQ4/sPjdbzTkLK055URSANUttNNB7g4SCmsspSHvpI1joKnMX2doqgtkDhM3+ngi55mGec6BVwJjheFnZHSZBxNx8ovNAgMBAAECggEACx+lAmffsGDwPXGvV0xt/Duxd16zOolEP2Q6vk9AjL3NBcEWLnPEykovrblVL3dHvEP5gbzjhF57TeiOxjv2+RuOlxELCbZm73P2npzqHPLsOiAoxEojpJD5rbf+cf8m90HCFl/O7OEybuJDk+jiZvjVBUR8o+49CqrZl87xyuBEyY9ue8MrbuMOrG8SULAJdeIyMZfR+jVXgIhTsdLrmDlMDsrDGzoy2NgCZlGO4dxyosTsymhoRZoatl9XoKMlm1k/8pjaQKx9vT0feFMqUMGTWa761MTZNT5K/4YwIe8JgvgXM88OzMOVkvbbSHUd7oZOeKycO+ZGQUcCOgbkQQKBgQD+un4N7atjSRTz7wkSgDzX87+Jj6NFmYA6xL7iOPBZo9jLxKnJ+TPHEVwBJGXMMLKNuDru2S6G5V2YDui2G/sl0d94vtZYebCrBRlilj3Eeox5isyrDwDVKD5Goh5AzdnuZE8ygvXC9dRoWKWv3T9CSMbBZ5ZYu2C1qkJTGrhynQKBgQDGmmkkdKBydN00SvsC6geDGGkCta8ih+pld+/z3Y2x0v5NHqbM4m6mSIWaMSe2Cjm7WGjennkoC/RW1AN/549cwsaJbalE5GDYMQREV9rN1IEl0xFNNtrG6JruShEccUotlWtIMlbL7vx4ZoGZw9lMMZt/BmiiqO0K861mQ01e8QKBgQC9diaV44XLdisQeGVODnWyxchb36s39EEs9PNpPaJ/ed+hR+hlomYBlYbSDqlso3fa8wFab80sZmMj20r5UHyPl7dYjtDMw85ElalY65iLj/710+gA/0rGQIV5cdGAr9t9dbx2PHPhC4luBylNUglgGtNCPPLVkqEO3dExELnJ/QKBgDrXWtlzOGpQ14z7vhzh67HalJo57BamADtp6oztEmMiKTf+IdtIJxJbIEJL2AEmTG6GRiPW/fxwdkcXb6YAnEgWaS4kcGc55ruG4kXBZdFf6ZekTlb7LpyKJAT/qswrgJe60ltEltS7DNA7RqChFwttEy3yXyP6OqjpA3Ht9MlxAoGAXei8OVPblMCw+6k3Palbv8DbUklm6+opVdnDVRLE/++ZCRU7AAGBa/ceTys2mazmSLo97h1kDghSuuSCyRK8A/S5fVi3mgXDhwjtetIJb1RzdPo5quOUNc4cpEC9DKUMs0FlahcnaRJcNLBARQQc0sUWUFsJnLXHC76hAq4p05E=';       
        Blob blobKey = EncodingUtil.base64Decode(key);
        String alg = 'SHA256';
        String iss = 'redacted';
        String sub = 'redacted';
        Long iat = DateTime.now().getTime();
        Long exp = DateTime.now().addMinutes(5).getTime();
        
        String jwtHeader = '{"typ":"JWT","alg":'+alg+'}';
        String jwtPayload = json.serialize(new Jwt());//String.format('{"iss":"{0}","sub":"{1}","iat":"{2}","exp":{3}}', new List<Object>{iss, sub, iat, exp});
        
        String jwtClearPart = String.format('{0}.{1}', new List<Object>{
            // Salesforce doesn't give us a method for base64url encoding, so we need to do that ourselves
            System.EncodingUtil.base64Encode(Blob.valueOf(jwtHeader)).replace('+', '-').replace('/', '_'),
            System.EncodingUtil.base64Encode(Blob.valueOf(jwtPayload)).replace('+', '-').replace('/', '_')
        });
        
        // Time to use the Crypto class to generate the signature
        // I assume that you'll be able to get the key to use for the signature fairly easily using
        // something like JSON.deserializeUntyped()
        Blob jwtSig = Crypto.generateMac('hmacSHA256', Blob.valueOf(jwtClearPart), blobKey);
        String jwt = String.format('{0}.{1}', new List<Object>{jwtClearPart, jwtSig});
        
        return jwt;
    }*/
    
    private static Blob createJWTAuth(){
        String key = 'MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDFneJNXi0f5WUiww5LWPwXJ+GttV86Hk+SqnHYlUKMwib4KncwgQXnFdt94lyhB6XBG4jCMjDNjKRbCY34AFyCXI14p0Aav3AQTQi25OqvpHrDlVTm309eNmxOHdn4ejPvrEN2LF1QtygL/AHQ+19FIlmqeHbVhYPYTFpmDHanZKLUiWCLocKXvjgDdBIRQcxdZY1MDeIy/udtmExq64KyGLIMzQIkhVsIm/NZtqyXLCnr5i79vajWJKPDoQ4/sPjdbzTkLK055URSANUttNNB7g4SCmsspSHvpI1joKnMX2doqgtkDhM3+ngi55mGec6BVwJjheFnZHSZBxNx8ovNAgMBAAECggEACx+lAmffsGDwPXGvV0xt/Duxd16zOolEP2Q6vk9AjL3NBcEWLnPEykovrblVL3dHvEP5gbzjhF57TeiOxjv2+RuOlxELCbZm73P2npzqHPLsOiAoxEojpJD5rbf+cf8m90HCFl/O7OEybuJDk+jiZvjVBUR8o+49CqrZl87xyuBEyY9ue8MrbuMOrG8SULAJdeIyMZfR+jVXgIhTsdLrmDlMDsrDGzoy2NgCZlGO4dxyosTsymhoRZoatl9XoKMlm1k/8pjaQKx9vT0feFMqUMGTWa761MTZNT5K/4YwIe8JgvgXM88OzMOVkvbbSHUd7oZOeKycO+ZGQUcCOgbkQQKBgQD+un4N7atjSRTz7wkSgDzX87+Jj6NFmYA6xL7iOPBZo9jLxKnJ+TPHEVwBJGXMMLKNuDru2S6G5V2YDui2G/sl0d94vtZYebCrBRlilj3Eeox5isyrDwDVKD5Goh5AzdnuZE8ygvXC9dRoWKWv3T9CSMbBZ5ZYu2C1qkJTGrhynQKBgQDGmmkkdKBydN00SvsC6geDGGkCta8ih+pld+/z3Y2x0v5NHqbM4m6mSIWaMSe2Cjm7WGjennkoC/RW1AN/549cwsaJbalE5GDYMQREV9rN1IEl0xFNNtrG6JruShEccUotlWtIMlbL7vx4ZoGZw9lMMZt/BmiiqO0K861mQ01e8QKBgQC9diaV44XLdisQeGVODnWyxchb36s39EEs9PNpPaJ/ed+hR+hlomYBlYbSDqlso3fa8wFab80sZmMj20r5UHyPl7dYjtDMw85ElalY65iLj/710+gA/0rGQIV5cdGAr9t9dbx2PHPhC4luBylNUglgGtNCPPLVkqEO3dExELnJ/QKBgDrXWtlzOGpQ14z7vhzh67HalJo57BamADtp6oztEmMiKTf+IdtIJxJbIEJL2AEmTG6GRiPW/fxwdkcXb6YAnEgWaS4kcGc55ruG4kXBZdFf6ZekTlb7LpyKJAT/qswrgJe60ltEltS7DNA7RqChFwttEy3yXyP6OqjpA3Ht9MlxAoGAXei8OVPblMCw+6k3Palbv8DbUklm6+opVdnDVRLE/++ZCRU7AAGBa/ceTys2mazmSLo97h1kDghSuuSCyRK8A/S5fVi3mgXDhwjtetIJb1RzdPo5quOUNc4cpEC9DKUMs0FlahcnaRJcNLBARQQc0sUWUFsJnLXHC76hAq4p05E=';       
        //String key = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxZ3iTV4tH+VlIsMOS1j8FyfhrbVfOh5Pkqpx2JVCjMIm+Cp3MIEF5xXbfeJcoQelwRuIwjIwzYykWwmN+ABcglyNeKdAGr9wEE0ItuTqr6R6w5VU5t9PXjZsTh3Z+Hoz76xDdixdULcoC/wB0PtfRSJZqnh21YWD2ExaZgx2p2Si1Ilgi6HCl744A3QSEUHMXWWNTA3iMv7nbZhMauuCshiyDM0CJIVbCJvzWbaslywp6+Yu/b2o1iSjw6EOP7D43W805CytOeVEUgDVLbTTQe4OEgprLKUh76SNY6CpzF9naKoLZA4TN/p4IueZhnnOgVcCY4XhZ2R0mQcTcfKLzQIDAQAB';
        
        key = base64URLencode(blob.valueOf(key));
        String payload = json.serialize(new Jwt());
        payload = payload.replace('object1','1');
        system.debug('Payload: '+payload);
        Blob blobKey = blob.valueof(key);
        String jwtPayload = base64UrlEncode(blob.valueOf(payload));
        Blob signature = crypto.sign('RSA-SHA256', Blob.valueOf(jwtPayload), blobkey);
        
        system.debug('Signature: '+String.valueOf(signature));
        return signature;
    }
    
    /*private static Blob createJWTAuth(){
        
        String iss = 'redacted';
        String sub = 'redacted';
        Long iat = DateTime.now().getTime();
        Long exp = DateTime.now().addMinutes(5).getTime();
        
        String key = 'MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDFneJNXi0f5WUiww5LWPwXJ+GttV86Hk+SqnHYlUKMwib4KncwgQXnFdt94lyhB6XBG4jCMjDNjKRbCY34AFyCXI14p0Aav3AQTQi25OqvpHrDlVTm309eNmxOHdn4ejPvrEN2LF1QtygL/AHQ+19FIlmqeHbVhYPYTFpmDHanZKLUiWCLocKXvjgDdBIRQcxdZY1MDeIy/udtmExq64KyGLIMzQIkhVsIm/NZtqyXLCnr5i79vajWJKPDoQ4/sPjdbzTkLK055URSANUttNNB7g4SCmsspSHvpI1joKnMX2doqgtkDhM3+ngi55mGec6BVwJjheFnZHSZBxNx8ovNAgMBAAECggEACx+lAmffsGDwPXGvV0xt/Duxd16zOolEP2Q6vk9AjL3NBcEWLnPEykovrblVL3dHvEP5gbzjhF57TeiOxjv2+RuOlxELCbZm73P2npzqHPLsOiAoxEojpJD5rbf+cf8m90HCFl/O7OEybuJDk+jiZvjVBUR8o+49CqrZl87xyuBEyY9ue8MrbuMOrG8SULAJdeIyMZfR+jVXgIhTsdLrmDlMDsrDGzoy2NgCZlGO4dxyosTsymhoRZoatl9XoKMlm1k/8pjaQKx9vT0feFMqUMGTWa761MTZNT5K/4YwIe8JgvgXM88OzMOVkvbbSHUd7oZOeKycO+ZGQUcCOgbkQQKBgQD+un4N7atjSRTz7wkSgDzX87+Jj6NFmYA6xL7iOPBZo9jLxKnJ+TPHEVwBJGXMMLKNuDru2S6G5V2YDui2G/sl0d94vtZYebCrBRlilj3Eeox5isyrDwDVKD5Goh5AzdnuZE8ygvXC9dRoWKWv3T9CSMbBZ5ZYu2C1qkJTGrhynQKBgQDGmmkkdKBydN00SvsC6geDGGkCta8ih+pld+/z3Y2x0v5NHqbM4m6mSIWaMSe2Cjm7WGjennkoC/RW1AN/549cwsaJbalE5GDYMQREV9rN1IEl0xFNNtrG6JruShEccUotlWtIMlbL7vx4ZoGZw9lMMZt/BmiiqO0K861mQ01e8QKBgQC9diaV44XLdisQeGVODnWyxchb36s39EEs9PNpPaJ/ed+hR+hlomYBlYbSDqlso3fa8wFab80sZmMj20r5UHyPl7dYjtDMw85ElalY65iLj/710+gA/0rGQIV5cdGAr9t9dbx2PHPhC4luBylNUglgGtNCPPLVkqEO3dExELnJ/QKBgDrXWtlzOGpQ14z7vhzh67HalJo57BamADtp6oztEmMiKTf+IdtIJxJbIEJL2AEmTG6GRiPW/fxwdkcXb6YAnEgWaS4kcGc55ruG4kXBZdFf6ZekTlb7LpyKJAT/qswrgJe60ltEltS7DNA7RqChFwttEy3yXyP6OqjpA3Ht9MlxAoGAXei8OVPblMCw+6k3Palbv8DbUklm6+opVdnDVRLE/++ZCRU7AAGBa/ceTys2mazmSLo97h1kDghSuuSCyRK8A/S5fVi3mgXDhwjtetIJb1RzdPo5quOUNc4cpEC9DKUMs0FlahcnaRJcNLBARQQc0sUWUFsJnLXHC76hAq4p05E=';       
        key = base64URLencode(blob.valueOf(key));
        //String payload = json.serialize(new Jwt());
        JSONGenerator body = JSOn.createGenerator(false);
        body.writeStartObject();
        body.writeStringField('iss',iss);
        body.writeStringField('sub',sub);
        body.writeNumberField('iat',iat);
        body.writeNumberField('exp',exp);
        body.writeEndObject();
        
        String payload = body.getAsString();
        
        
        system.debug('Payload: '+payload);
        Blob blobKey = blob.valueof(key);
        String jwtPayload = base64UrlEncode(blob.valueOf(payload));
        Blob signature = crypto.sign('RSA-SHA256', Blob.valueOf(jwtPayload), blobKey);
        
        
        system.debug('Signature: '+String.valueOf(signature));
        return signature;
    }*/
    
    
    public static String base64URLencode(Blob input){ 
        String output = encodingUtil.base64Encode(input);
        output = output.replace('+', '-');
        output = output.replace('/', '_');
        while ( output.endsWith('=')){
            output = output.subString(0,output.length()-1);
        }
        //output = output.replaceAll('=','');
        return output;
    }
    
    
    /*****************************************************************************************
     * 
     * Private helper classes for forming our fairly complicated request body
     * 
     * 
     ******************************************************************************************/    
    
    private class PostRequest{
        private String statement;
        private Integer timeout = 60;
        private ResultMetaData resultSetMetaData = new ResultMetaData();
        private String database = 'redacted';
        private String schema = 'PUBLIC';
        private String warehouse = 'redacted';
        private String role = 'SYSADMIN';
        private Binding bindings = new Binding();
        
        public postRequest(String query){
            this.statement = query;
        }
    }
    
    private class ResultMetaData{
        private String format = 'jsonv2';
    }
    
    private class Binding{
        private one object1 = new one();
    }
    
    private class One{
        private String type = 'FIXED';
        private String value = '123';
    }
    
    private class Jwt{
        private String iss = 'redacted';
        private string sub = 'redacted';
        private Double iat = Math.floor(DateTime.now().getTime() / 1000);
        private Double exp = Math.floor(DateTime.now().addMinutes(10).getTime() / 1000);
    }
}
2
  • This might be a Snowflake-side issue. To generate the JWT, import the key into Salesforce and use classes in Auth namespace. See linked answer for an example
    – identigral
    Jan 11, 2022 at 21:43
  • I don't think it's from the Snowflake side. Everything works correctly when generating the jwt via python and calling from postman but I get the 'Invalid Crypto Key' error prior to the callout. Lookin at the linked answer and working through some of that now. Appreciate the insight!
    – SFDC Noob
    Jan 11, 2022 at 22:24

0

Browse other questions tagged .