001/* 002 * Copyright 2010-2013 Ning, Inc. 003 * 004 * Ning licenses this file to you under the Apache License, version 2.0 005 * (the "License"); you may not use this file except in compliance with the 006 * License. You may obtain a copy of the License at: 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 013 * License for the specific language governing permissions and limitations 014 * under the License. 015 */ 016 017package com.ning.billing.recurly; 018 019import com.google.common.base.Joiner; 020import com.google.common.io.BaseEncoding; 021import org.slf4j.Logger; 022import org.slf4j.LoggerFactory; 023 024import javax.crypto.Mac; 025import javax.crypto.SecretKey; 026import javax.crypto.spec.SecretKeySpec; 027import java.util.ArrayList; 028import java.util.List; 029import java.util.UUID; 030 031public class RecurlyJs { 032 033 private static final Logger log = LoggerFactory.getLogger(RecurlyJs.class); 034 035 // Specific to signature generation 036 public static final String PARAMETER_FORMAT = "%s=%s"; 037 public static final String PARAMETER_SEPARATOR = "&"; 038 public static final String NONCE_PARAMETER = "nonce"; 039 public static final String TIMESTAMP_PARAMETER = "timestamp"; 040 041 /** 042 * Get Recurly.js Signature 043 * See spec here: http://docs.recurly.com/api/recurlyjs/signatures 044 * <p/> 045 * Returns a signature key for use with recurly.js BuildSubscriptionForm. 046 * 047 * @param privateJsKey recurly.js private key 048 * @return signature string on success, null otherwise 049 */ 050 public static String getRecurlySignature(String privateJsKey) { 051 return getRecurlySignature(privateJsKey, new ArrayList<String>()); 052 } 053 054 /** 055 * Get Recurly.js Signature 056 * See spec here: http://docs.recurly.com/api/recurlyjs/signatures 057 * <p/> 058 * Returns a signature key for use with recurly.js BuildSubscriptionForm. 059 * 060 * @param privateJsKey recurly.js private key 061 * @param extraParams extra parameters to include in the signature 062 * @return signature string on success, null otherwise 063 */ 064 public static String getRecurlySignature(String privateJsKey, List<String> extraParams) { 065 final long unixTime = System.currentTimeMillis() / 1000L; 066 final String uuid = UUID.randomUUID().toString().replaceAll("-", ""); 067 return getRecurlySignature(privateJsKey, unixTime, uuid, extraParams); 068 } 069 070 /** 071 * Get Recurly.js Signature with extra parameter strings in the format "[param]=[value]" 072 * See spec here: http://docs.recurly.com/api/recurlyjs/signatures 073 * <p/> 074 * Returns a signature key for use with recurly.js BuildSubscriptionForm. 075 * 076 * @param privateJsKey recurly.js private key 077 * @param unixTime Unix timestamp, i.e. elapsed seconds since Midnight, Jan 1st 1970, UTC 078 * @param nonce A randomly generated string (number used only once) 079 * @param extraParams extra parameters to include in the signature 080 * @return signature string on success, null otherwise 081 */ 082 public static String getRecurlySignature(String privateJsKey, Long unixTime, String nonce, List<String> extraParams) { 083 // Mandatory parameters shared by all signatures (as per spec) 084 extraParams = (extraParams == null) ? new ArrayList<String>() : extraParams; 085 extraParams.add(String.format(PARAMETER_FORMAT, TIMESTAMP_PARAMETER, unixTime)); 086 extraParams.add(String.format(PARAMETER_FORMAT, NONCE_PARAMETER, nonce)); 087 String protectedParams = Joiner.on(PARAMETER_SEPARATOR).join(extraParams); 088 089 return generateRecurlyHMAC(privateJsKey, protectedParams) + "|" + protectedParams; 090 } 091 092 /** 093 * HMAC-SHA1 Hash Generator - Helper method 094 * <p/> 095 * Returns a signature key for use with recurly.js BuildSubscriptionForm. 096 * 097 * @param privateJsKey recurly.js private key 098 * @param protectedParams protected parameter string in the format: <secure_hash>|<protected_string> 099 * @return subscription object on success, null otherwise 100 */ 101 private static String generateRecurlyHMAC(String privateJsKey, String protectedParams) { 102 try { 103 SecretKey sk = new SecretKeySpec(privateJsKey.getBytes(), "HmacSHA1"); 104 Mac mac = Mac.getInstance("HmacSHA1"); 105 mac.init(sk); 106 byte[] result = mac.doFinal(protectedParams.getBytes("UTF-8")); 107 return BaseEncoding.base16().encode(result).toLowerCase(); 108 } catch (Exception e) { 109 log.error("Error while trying to generate Recurly HMAC signature", e); 110 return null; 111 } 112 } 113 114}