com.google.code.facebookapi.FacebookSignatureUtil Maven / Gradle / Ivy
/*
+---------------------------------------------------------------------------+
| Facebook Development Platform Java Client |
+---------------------------------------------------------------------------+
| Copyright (c) 2007 Facebook, Inc. |
| All rights reserved. |
| |
| Redistribution and use in source and binary forms, with or without |
| modification, are permitted provided that the following conditions |
| are met: |
| |
| 1. Redistributions of source code must retain the above copyright |
| notice, this list of conditions and the following disclaimer. |
| 2. Redistributions in binary form must reproduce the above copyright |
| notice, this list of conditions and the following disclaimer in the |
| documentation and/or other materials provided with the distribution. |
| |
| THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
| IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
| INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+---------------------------------------------------------------------------+
| For help with this library, contact [email protected] |
+---------------------------------------------------------------------------+
*/
package com.google.code.facebookapi;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Utility for managing Facebook-specific parameters, specifically those related to session/login aspects.
*/
public final class FacebookSignatureUtil {
protected static Log log = LogFactory.getLog( FacebookSignatureUtil.class );
public static Map pulloutFbSigParams( Map reqParams ) {
Map result = new TreeMap();
for ( Map.Entry entry : reqParams.entrySet() ) {
String key = entry.getKey();
String[] values = entry.getValue();
if ( values.length > 0 && FacebookParam.isInNamespace( key ) ) {
result.put( key, values[0] );
}
}
return result;
}
/**
* Out of the passed in reqParams
, extracts the parameters that are in the FacebookParam namespace and returns them.
*
* @param reqParams
* A map of request parameters to their values. Values are arrays of strings, as returned by ServletRequest.getParameterMap(). Only the first element in a
* given array is significant.
* @return a boolean indicating whether the calculated signature matched the expected signature
*/
public static Map extractFacebookParamsFromArray( Map reqParams ) {
if ( null == reqParams ) {
return null;
}
Map result = new TreeMap();
for ( Map.Entry entry : reqParams.entrySet() ) {
String key = entry.getKey().toString();
CharSequence[] values = entry.getValue();
if ( values.length > 0 && FacebookParam.isInNamespace( key ) ) {
result.put( key, toString( values[0] ) );
}
}
return result;
}
public static String toString( CharSequence cs ) {
if ( cs != null ) {
return cs.toString();
}
return null;
}
/**
* Out of the passed in reqParams
, extracts the parameters that are in the FacebookParam namespace and returns them.
*
* @param reqParams
* A map of request parameters to their values. Values are arrays of strings, as returned by ServletRequest.getParameterMap(). Only the first element in a
* given array is significant.
* @return a boolean indicating whether the calculated signature matched the expected signature
*/
// Facebook likes to refer to everything as a CharSequence, even when referencing Objects defined by an external API that explicitly
// specifies the use of String, and *not* CharSequence.
public static Map extractFacebookParamsFromStandardsCompliantArray( Map reqParams ) {
if ( null == reqParams ) {
return null;
}
Map result = new TreeMap();
for ( Map.Entry entry : reqParams.entrySet() ) {
String key = entry.getKey();
if ( FacebookParam.isInNamespace( key ) ) {
String[] value = entry.getValue();
if ( value.length > 0 ) {
result.put( key, value[0] );
}
}
}
return result;
}
/**
* Out of the passed in reqParams
, extracts the parameters that are in the FacebookParam namespace and returns them.
*
* @param reqParams
* a map of request parameters to their values
* @return a boolean indicating whether the calculated signature matched the expected signature
*/
public static Map extractFacebookNamespaceParams( Map reqParams ) {
if ( null == reqParams ) {
return null;
}
Map result = new TreeMap();
for ( Map.Entry entry : reqParams.entrySet() ) {
String key = entry.getKey().toString();
if ( FacebookParam.isInNamespace( key ) ) {
result.put( key, entry.getValue() );
}
}
return result;
}
/**
* Out of the passed in reqParams
, extracts the parameters that are known FacebookParams and returns them.
*
* @param reqParams
* a map of request parameters to their values
* @return a map suitable for being passed to verify signature
*/
public static EnumMap extractFacebookParams( Map reqParams ) {
if ( null == reqParams )
return null;
EnumMap result = new EnumMap( FacebookParam.class );
for ( Map.Entry entry : reqParams.entrySet() ) {
FacebookParam matchingFacebookParam = FacebookParam.get( entry.getKey().toString() );
if ( null != matchingFacebookParam ) {
result.put( matchingFacebookParam, entry.getValue() );
}
}
return result;
}
/**
* Verifies that a signature received matches the expected value. Removes FacebookParam.SIGNATURE from params if present.
*
* @param params
* a map of parameters and their values, such as one obtained from extractFacebookParams; expected to the expected signature as the FacebookParam.SIGNATURE
* parameter
* @param secret
* @return a boolean indicating whether the calculated signature matched the expected signature
*/
public static boolean verifySignature( EnumMap params, String secret ) {
if ( null == params || params.isEmpty() )
return false;
CharSequence sigParam = params.remove( FacebookParam.SIGNATURE );
return ( null == sigParam ) ? false : verifySignature( params, secret, sigParam.toString() );
}
/**
* Verifies that a signature received matches the expected value.
*
* @param params
* a map of parameters and their values, such as one obtained from extractFacebookParams
* @param secret
* the developers 'secret' API key
* @param expected
* the expected resulting value of computing the MD5 sum of the 'sig' params and the 'secret' key
* @return a boolean indicating whether the calculated signature matched the expected signature
*/
public static boolean verifySignature( EnumMap params, String secret, String expected ) {
assert ! ( null == secret || "".equals( secret ) );
if ( null == params || params.isEmpty() )
return false;
if ( null == expected || "".equals( expected ) ) {
return false;
}
params.remove( FacebookParam.SIGNATURE );
List sigParams = convertFacebookParams( params.entrySet() );
return verifySignature( sigParams, secret, expected );
}
/**
* Verifies that a signature received matches the expected value. Removes FacebookParam.SIGNATURE from params if present.
*
* @param params
* a map of parameters and their values, such as one obtained from extractFacebookNamespaceParams; expected to contain the signature as the
* FacebookParam.SIGNATURE parameter
* @param secret
* the developers 'secret' API key
* @return a boolean indicating whether the calculated signature matched the expected signature
*/
public static boolean verifySignature( Map params, String secret ) {
if ( null == params || params.isEmpty() ) {
return false;
}
CharSequence sigParam = params.remove( FacebookParam.SIGNATURE.toString() );
return ( null == sigParam ) ? false : verifySignature( params, secret, sigParam.toString() );
}
/**
* Verifies that a signature received matches the expected value. This method will perform any necessary conversion of the parameter map passed to it (should the map
* be immutable, etc.), meaning that you may safely call it without doing any manual preprocessing of the parameters first.
*
* @param requestParams
* A map of request parameters to their values, as returned by ServletRequest.getParameterMap(), for example.
* @param secret
* the developers 'secret' API key
* @param expected
* the expected resulting value of computing the MD5 sum of the 'sig' params and the 'secret' key
*
* @return a boolean indicating whether the calculated signature matched the expected signature
*/
public static boolean autoVerifySignature( Map requestParams, String secret, String expected ) {
Map convertedMap = extractFacebookParamsFromStandardsCompliantArray( requestParams );
return verifySignature( convertedMap, secret, expected );
}
/**
* Verifies that a signature received matches the expected value. This method will perform any necessary conversion of the parameter map passed to it (should the map
* be immutable, etc.), meaning that you may safely call it without doing any manual preprocessing of the parameters first.
*
* @param requestParams
* A map of request parameters to their values, as returned by ServletRequest.getParameterMap(), for example.
* @param secret
* the developers 'secret' API key
* @param expected
* the expected resulting value of computing the MD5 sum of the 'sig' params and the 'secret' key
*
* @return a boolean indicating whether the calculated signature matched the expected signature
*/
public static boolean autoVerifySignature( Map requestParams, String secret ) {
String expected = requestParams.get( "fb_sig" )[0];
return autoVerifySignature( requestParams, secret, expected );
}
/**
* Verifies that a signature received matches the expected value.
*
* @param params
* a map of parameters and their values, such as one obtained from extractFacebookNamespaceParams
* @param secret
* the developers 'secret' API key
* @param expected
* the expected resulting value of computing the MD5 sum of the 'sig' params and the 'secret' key
* @return a boolean indicating whether the calculated signature matched the expected signature
*/
public static boolean verifySignature( Map params, String secret, String expected ) {
assert ! ( null == secret || "".equals( secret ) );
if ( null == params || params.isEmpty() )
return false;
if ( null == expected || "".equals( expected ) ) {
return false;
}
params.remove( FacebookParam.SIGNATURE.toString() );
List sigParams = convert( params.entrySet() );
return verifySignature( sigParams, secret, expected );
}
private static boolean verifySignature( List sigParams, String secret, String expected ) {
if ( null == expected || "".equals( expected ) ) {
return false;
}
String signature = generateSignature( sigParams, secret );
return expected.equals( signature );
}
/**
* Converts a Map of key-value pairs into the form expected by generateSignature
*
* @param entries
* a collection of Map.Entry's, such as can be obtained using myMap.entrySet()
* @return a List suitable for being passed to generateSignature
*/
public static List convert( Collection> entries ) {
List result = new ArrayList( entries.size() );
for ( Map.Entry entry : entries ) {
result.add( FacebookParam.stripSignaturePrefix( entry.getKey() ) + "=" + entry.getValue() );
}
return result;
}
/**
* Converts a Map of key-value pairs into the form expected by generateSignature
*
* @param entries
* a collection of Map.Entry's, such as can be obtained using myMap.entrySet()
* @return a List suitable for being passed to generateSignature
*/
public static List convertFacebookParams( Collection> entries ) {
List result = new ArrayList( entries.size() );
for ( Map.Entry entry : entries ) {
result.add( entry.getKey().getSignatureName() + "=" + entry.getValue() );
}
return result;
}
/**
* Calculates the signature for the given set of params using the supplied secret
*
* @param params
* Strings of the form "key=value"
* @param secret
* @return the signature
*/
public static String generateSignature( List params, String secret ) {
StringBuffer buffer = new StringBuffer();
Collections.sort( params );
for ( String param : params ) {
buffer.append( param );
}
buffer.append( secret );
try {
java.security.MessageDigest md = java.security.MessageDigest.getInstance( "MD5" );
StringBuffer result = new StringBuffer();
try {
for ( byte b : md.digest( buffer.toString().getBytes( "UTF-8" ) ) ) {
result.append( Integer.toHexString( ( b & 0xf0 ) >>> 4 ) );
result.append( Integer.toHexString( b & 0x0f ) );
}
}
catch ( UnsupportedEncodingException e ) {
for ( byte b : md.digest( buffer.toString().getBytes() ) ) {
result.append( Integer.toHexString( ( b & 0xf0 ) >>> 4 ) );
result.append( Integer.toHexString( b & 0x0f ) );
}
}
return result.toString();
}
catch ( java.security.NoSuchAlgorithmException ex ) {
log.error( "MD5 does not appear to be supported" + ex, ex );
}
return "";
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy