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