package com.sap.jam.oauth.client; import java.io.UnsupportedEncodingException; import java.net.URL; import java.net.URLDecoder; import java.net.URLEncoder; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Set; import java.util.Comparator; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; /** * Various static utility functions and constants for OAuth. */ public final class OAuthUtils { private OAuthUtils() {} //the OAuth protocol parameters public static final String OAUTH_CONSUMER_KEY = "oauth_consumer_key"; public static final String OAUTH_TOKEN = "oauth_token"; public static final String OAUTH_TOKEN_SECRET = "oauth_token_secret"; public static final String OAUTH_SIGNATURE_METHOD = "oauth_signature_method"; public static final String OAUTH_SIGNATURE = "oauth_signature"; public static final String OAUTH_TIMESTAMP = "oauth_timestamp"; public static final String OAUTH_NONCE = "oauth_nonce"; public static final String OAUTH_VERSION = "oauth_version"; public static final String OAUTH_CALLBACK = "oauth_callback"; public static final String OAUTH_VERIFIER = "oauth_verifier"; public static final String X_AUTH_MODE = "x_auth_mode"; public static final String X_AUTH_USERNAME = "x_auth_username"; public static final String X_AUTH_PASSWORD = "x_auth_password"; public static final String X_AUTH_EXPIRES = "x_auth_expires"; public static final String CLIENT_AUTH = "client_auth"; //special param that may occur in Authorization headers that is treated specially in forming OAuth signatures public static final String REALM_PARAM = "realm"; public static final Set<String> OAUTH_PROTOCOL_PARAMS = Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(OAUTH_CALLBACK, OAUTH_CONSUMER_KEY, OAUTH_TOKEN, OAUTH_SIGNATURE_METHOD, OAUTH_TIMESTAMP, OAUTH_NONCE, OAUTH_VERIFIER, OAUTH_VERSION, OAUTH_SIGNATURE))); public static final Comparator<Pair<String, String>> STRING_PAIR_COMPARATOR = new Comparator<Pair<String, String>>() { @Override public int compare(Pair<String, String> o1, Pair<String, String> o2) { int c1 = o1.fst().compareTo(o2.fst()); if (c1 != 0) { return c1; } return o1.snd().compareTo(o2.snd()); } }; public static String calculateHmacSha1Signature(String message, String key) { try { SecretKeySpec secretKey = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA1"); Mac messageAuthnCode = Mac.getInstance("HmacSHA1"); messageAuthnCode.init(secretKey); byte[] rawEncodedMessage = messageAuthnCode.doFinal(message.getBytes("UTF-8")); return Base64Util.encode(rawEncodedMessage); } catch(UnsupportedEncodingException e) { throw new IllegalStateException("UTF-8 encoding not supported", e); } catch (InvalidKeyException e) { throw new IllegalStateException("Invalid secret key", e); } catch (NoSuchAlgorithmException e) { throw new IllegalStateException("HmacSHA1 not supported", e); } } public static String calculateRsaSha1Signature(String message, PrivateKey privateKey) { try { return SignatureUtil.generateBase64Signature(privateKey, message.getBytes("UTF-8")); } catch (UnsupportedEncodingException e) { throw new IllegalArgumentException("UTF-8 charset supported for decoding the message argument."); } } public static boolean verifyRsaSha1Signature(PublicKey publicKey, String signatureBase64, String signatureBaseString) { try { return SignatureUtil.verifyBase64Signature(publicKey, signatureBase64, signatureBaseString.getBytes("UTF-8")); } catch (Exception e) { return false; } } /** * Encode according to the OAuth spec http://tools.ietf.org/html/rfc5849#section-3.6 * Convert to UTF-8 octets and then % escape all characters other than a-z, A-Z, 0-9, -, ., _, ~. * This is slightly different from url-encoding. */ public static String oauthEncode(String s) { if (s == null) { return ""; } try { //URLEncoder doesn't encode *, encodes ~, and encodes spaces as + instead of %20 return URLEncoder.encode(s, "UTF-8").replace("*", "%2A").replace("%7E", "~").replace("+", "%20"); } catch (UnsupportedEncodingException e) { throw new IllegalStateException("UTF-8 encoding not supported", e); } } public static String oauthDecode(String s) { try { return URLDecoder.decode(s.replace("+", "%2B"), "UTF-8"); } catch (UnsupportedEncodingException e) { throw new IllegalStateException("UTF-8 encoding not supported", e); } } public static String urlEncode(String s) { try { return URLEncoder.encode(s, "UTF-8"); } catch (UnsupportedEncodingException e) { throw new IllegalStateException("UTF-8 encoding not supported", e); } } public static String urlDecode(String s) { try { return URLDecoder.decode(s, "UTF-8"); } catch (UnsupportedEncodingException e) { throw new IllegalStateException("UTF-8 encoding not supported", e); } } public static String baseStringUrl(URL url) { boolean showPort = url.getPort() != -1 && (url.getProtocol().equals("http") && url.getPort() != 80 || url.getProtocol().equals("https") && url.getPort() != 443); return url.getProtocol() + "://" + url.getHost().toLowerCase(Locale.ROOT) + (showPort ? ":" + url.getPort() : "") + (url.getPath().isEmpty() ? "/" : url.getPath()); } public static String baseOAuthSignature(String consumerSecret, String tokenSecret) { return tokenSecret != null ? oauthEncode(consumerSecret) + "&" + oauthEncode(tokenSecret) : oauthEncode(consumerSecret) + "&"; } public static String joinPostBodyParams(List<Pair<String, String>> postParams) { StringBuilder sb = new StringBuilder(); boolean first = true; for (Pair<String, String> item : postParams) { if (first) { first = false; } else { sb.append("&"); } sb.append(item.fst()).append("=").append(item.snd()); } return sb.toString(); } }