function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
j4jamshaidj4jamshaid 

Restful Webservice Authetication

HI,
   i am acessing an external webservice using apex class. My external webserices is restful webservice and it uses Digest access authentication. I am acessing it in java and its working fine. I need to access it using apex code. Here is my apex code...


HttpRequest req = new HttpRequest();
req.setEndpoint(serviceEndPoint);
req.setMethod('POST');

req.setBody(xmlFeed);

String myDeveloperAPIKey = 'xxxx';
String username = myDeveloperAPIKey + '%xxxxxuserName';
String password = 'xxxPassword';


Blob headerValue = Crypto.generateDigest('MD5', Blob.valueOf('username:password'));

String authorizationHeader = EncodingUtil.convertToHex(headerValue );

req.setHeader('Authorization',authorizationHeader );
req.setHeader('charset', 'UTF-8');     
req.setHeader('Content-Type', 'application/atom+xml');

Http http = new Http();
HTTPResponse res = http.send(req);

return 'status=' + res.getStatus();



It is returning Status="unAuthorized", where as userName/Pasword/key are all correct. can anybody figure out whats wrong here? or what is missing?...

SuperfellSuperfell
you don't appear to use your apikey, username or password strings, as your generateDigest uses a string literal of 'username: password' instead.
j4jamshaidj4jamshaid

1- UserName in generateDigest is somthing like this (APIkey % Username). This is the requirement of the my target web service. This is like this....

                    String myDeveloperAPIKey = 'xxxx';
                    String username = myDeveloperAPIKey + '%' + 'xxxxxName';
                    String password = 'xxxPassword';
                    Blob headerValue = Crypto.generateDigest('MD5', Blob.valueOf('username:password'));
2- There is too little about generateDigest in api docs, Can any one share my a detail article of using this?
3- Can anyone share the sample code that uses generateDigest in its implimentation?

Thanks for replying my friends.

--Jamshaid.
SuperfellSuperfell
Blob.valueOf('username:password')

is the string literal username:password, and nothing to do with your variables called username and password.
j4jamshaidj4jamshaid
Hi SimonF,
       
Thanks for sharing...

   1- i have tried it with the real values and making a string literal of them as following, but it is sending me the same error,  I guess i am not using the generateDigest correctly....
   
Blob headerValue = Crypto.generateDigest('MD5', Blob.valueOf('xxxkey%xxxUsername:xxxPassword'));

2- Can you share any code sample that uses this generateDigest?

Thanks again..

--Jamshaid..


SuperfellSuperfell
There's a good sample in the docs
http://www.salesforce.com/us/developer/docs/apexcode/index_CSH.htm#apex_classes_restful_crypto.htm
j4jamshaidj4jamshaid
HI SimonF ,
         i have seen that example but it is not helping me out. this example uses generteMac and i need to use generateDigest. here is my code.. Lets review it again.

String atom = '<entry> XML FEED </entry>';
String uri = 'http://api./samplexxxx/lists';

String myDeveloperAPIKey = '1232-12321-12312-123-12123213-xxxx';
String username = 'xxxUserName';
String password = 'xxxABC';
String realm='api.realmxxx.com';

HttpRequest req = new HttpRequest();
req.setEndpoint(uri);
req.setMethod('POST');
req.setBody(atom);

Blob headerValue = Crypto.generateDigest('MD5', Blob.valueOf(username+':'+ password));

(I have used these authorizationHeader .. but no one is working.)

String authorizationHeader =  'DIGEST '+EncodingUtil.convertToHex(headerValue );
//String authorizationHeader = 'DIGEST ' + EncodingUtil.base64Encode(headerValue );
//String authorizationHeader ='DIGEST ' +  EncodingUtil.urlEncode(EncodingUtil.base64Encode(headerValue), 'UTF-8');

req.setHeader('Authorization',authorizationHeader );
req.setHeader('charset', 'UTF-8');    
req.setHeader('Content-Type', 'application/atom+xml');

Http http = new Http();
HTTPResponse res = http.send(req);

status= res.getStatus();

Here it is returns me "UnAuthorized" message...  I need to study more about this "Crypto.generateDigest". How it works? What must be the out put? Any Detail documentation of the this Method? Please share.

--Jamshaid..

SuperfellSuperfell
At the risk of sounding like a broken record, your code is not using your developerKey.
David VPDavid VP
These RESTfull webservices typically need a combination of username / password to tell them who you are + a signed version of a string that only you can produce using something that you keep 'secret' at all times. In this case, the 'secret' part is your developer key. You only send over the 'signed' string (or the 'digest'), never your key.


The idea is that you provide the web service with an encrypted version of a string (I don't know which service you're trying to contact, so I can't tell you what string you need to construct), which you then sign using your Developer key ... (your 'secret').


What Simon is saying is that your code can never work because nowhere in your code do you use your developer key to encrypt your credentials.

-> Blob headerValue = Crypto.generateDigest('MD5', Blob.valueOf(username+':'+ password));


You definately need more than just username and password.

Read this perhaps to see what the concept is all about :
http://en.wikipedia.org/wiki/Cryptographic_hash_function


Bottom line : you need to check the docs of the service you're trying to connect to carefully, and look for the string they want you to sign/send over. It surely must be something more than just your username + password ....


Hope this helps,


David

Edit : I just noticed that you mentioned something about what they want you to send over.
->   myDeveloperAPIKey + '%' + 'xxxxxName'

Perhaps it would work with :
Blob headerValue = Crypto.generateDigest('MD5', Blob.valueOf(myDeveloperAPIKey + '%' + username +':'+ password));





Message Edited by David VP on 10-08-2008 05:39 PM
j4jamshaidj4jamshaid
Hi,
     David, i have it both ways, But it is not working ...

-> Blob headerValue = Crypto.generateDigest('MD5', Blob.valueOf(username+':'+ password));//( where username = (developerKey % UserName))
-> Blob headerValue = Crypto.generateDigest('MD5', Blob.valueOf(developerKey+'%'+username+':'+ password));



My target Restful webservice requires the developerKey +'%' appended by username and password is used as it is. Here is my java code implimentation. It is working fine. It may help to understand.

UsernamePasswordCredentials credentials =
            new UsernamePasswordCredentials(DeveloperApiKey()+ "%" + UserName(), Password());

        HostConfiguration hostConfiguration = new HostConfiguration();
        hostConfiguration.setHost(Constants.CTCT_HOST);

        AuthScope authScope = new AuthScope(HOST, PORT,HOST);

        AuthPolicy.registerAuthScheme("Digest", DigestScheme.class);
       
        HttpClient httpClient = new HttpClient();
        httpClient.setHostConfiguration(hostConfiguration) ;

        List<String> list = new ArrayList<String>();
        list.add(AuthPolicy.DIGEST);

        //authorizationHeader
        httpClient.getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, list);
        httpClient.getState().setCredentials(authScope, credentials);

         GetMethod httpGet = new GetMethod(uri);
  
          int status = httpClient.executeMethod(httpGet);



--Jamshiad..


SuperfellSuperfell
That looks like you're using HTTP digest authentication, which it more complicated than adding an authentication HTTP header. I don't know if the apex http class has native support for HTTP digest auth, if not you'll need to read this http://www.ietf.org/rfc/rfc2617
j4jamshaidj4jamshaid
Hi,
      i am implementing RFC 2617. Following is my apex implementation code. When i debug my java code and get the value of Authorization Header it is as following. When i use the same values even nonce and cnonce, in my apex implementation the response value is different. I have tried many combination to generate the same response value using same value in Apex implementation but response value is different. What is missing in my apex implementation?

/**************** Java Code Authorization Header *********
 Authorization: Digest username="userA",
 realm="abc.com",
 nonce="e8cd2bbc2f36e111dbb5679ac1d29412",
 uri="http://abc.com/ws/opens",
 response="f2ebf049628bf0bba7f4ff078e875c16",
 qop=auth, nc=00000001,
 cnonce="c1dc11b3c4eec3c1e55b3e41a23da365"
***************************************************************/

/************** Apex Code Implementation **********************/

static String basicAuthCallout(String abc){
try{

String uri = 'http://abc.com/ws/opens';
String myDeveloperAPIKey = 'xyxz-sfssbsd-23423424-xxxx';
String username = 'userA';
String password = 'userA';
String realm='abc.com';

HttpRequest req = new HttpRequest();
String nonce = 'e8cd2bbc2f36e111dbb5679ac1d29412';

String a1 = myDeveloperAPIKey+'%'+username+':'+realm+':'+ password;
String a2 = 'GET:'+ uri;
String HA1= EncodingUtil.convertToHex(Crypto.generateDigest('MD5', Blob.valueOf(a1)));
String HA2 =EncodingUtil.convertToHex(Crypto.generateDigest('MD5', Blob.valueOf(a2)));

//String resString = HA1+':'+nonce+':'+HA2 ;

String resString = HA1+':'+nonce+':00000001:c1dc11b3c4eec3c1e55b3e41a23da365:"auth":'+HA2 ;
//Here  cnounce is same as java code value.

String responseDigest = EncodingUtil.convertToHex(Crypto.generateDigest('MD5', Blob.valueOf(resString)));

String authorizationHeader  = createAuthorizationHeader(myDeveloperAPIKey +'%'+username,realm, nonce, uri,responseDigest);

req.setEndpoint(uri);
req.setMethod('GET');

req.setHeader('Authorization',authorizationHeader );
req.setHeader('Content-Type', 'application/atom+xml');

Http http = new Http();
HTTPResponse res = http.send(req);

return 'status='+res.getStatus();
}catch(Exception ex){
return 'Exception=' + ex.getMessage();
}
}

/*********************
Other Static methods.
**********************/

// Here cnounce is same as java code value ...
Static String createAuthorizationHeader(String username, String realm, String nonce, String uri,
                                             String responseDigest) {
return 'Digest username="'+ username + '", realm="' + realm + '", nonce="' + nonce +'",uri="'+ uri +'", response="' + responseDigest + '", qop=auth, nc=00000001,cnonce="c1dc11b3c4eec3c1e55b3e41a23da365"';

     }
}
SuperfellSuperfell
IIRC, the nonce comes from the server as part of the challenge. You certainly can't just use the same one over and over again.
j4jamshaidj4jamshaid
Hi Simon,
               You are right, nonce value can't be used again over and over. But point is that using the same old nonce and cnonce values of java code must generate the same response value, that will prove the correct apex code implementation. But using same old nonce and cnonce value in apex implementation, response value is different. it means somthing is worng in above apex implimentation. What it can be ???

Thanks
--Jamshaid.