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.Locale; 031import java.util.UUID; 032 033public class RecurlyJs { 034 035 private static final Logger log = LoggerFactory.getLogger(RecurlyJs.class); 036 037 // Specific to signature generation 038 public static final String PARAMETER_FORMAT = "%s=%s"; 039 public static final String PARAMETER_SEPARATOR = "&"; 040 public static final String NONCE_PARAMETER = "nonce"; 041 public static final String TIMESTAMP_PARAMETER = "timestamp"; 042 043 /** 044 * Get Recurly.js Signature 045 * See spec here: https://docs.recurly.com/deprecated-api-docs/recurlyjs/signatures 046 * <p> 047 * Returns a signature key for use with recurly.js BuildSubscriptionForm. 048 * 049 * @param privateJsKey recurly.js private key 050 * @return signature string on success, null otherwise 051 */ 052 public static String getRecurlySignature(String privateJsKey) { 053 return getRecurlySignature(privateJsKey, new ArrayList<String>()); 054 } 055 056 /** 057 * Get Recurly.js Signature 058 * See spec here: https://docs.recurly.com/deprecated-api-docs/recurlyjs/signatures 059 * <p> 060 * Returns a signature key for use with recurly.js BuildSubscriptionForm. 061 * 062 * @param privateJsKey recurly.js private key 063 * @param extraParams extra parameters to include in the signature 064 * @return signature string on success, null otherwise 065 */ 066 public static String getRecurlySignature(String privateJsKey, List<String> extraParams) { 067 final long unixTime = System.currentTimeMillis() / 1000L; 068 final String uuid = UUID.randomUUID().toString().replaceAll("-", ""); 069 return getRecurlySignature(privateJsKey, unixTime, uuid, extraParams); 070 } 071 072 /** 073 * Get Recurly.js Signature with extra parameter strings in the format "[param]=[value]" 074 * See spec here: https://docs.recurly.com/deprecated-api-docs/recurlyjs/signatures 075 * <p> 076 * Returns a signature key for use with recurly.js BuildSubscriptionForm. 077 * 078 * @param privateJsKey recurly.js private key 079 * @param unixTime Unix timestamp, i.e. elapsed seconds since Midnight, Jan 1st 1970, UTC 080 * @param nonce A randomly generated string (number used only once) 081 * @param extraParams extra parameters to include in the signature 082 * @return signature string on success, null otherwise 083 */ 084 public static String getRecurlySignature(String privateJsKey, Long unixTime, String nonce, List<String> extraParams) { 085 // Mandatory parameters shared by all signatures (as per spec) 086 extraParams = (extraParams == null) ? new ArrayList<String>() : extraParams; 087 extraParams.add(String.format(Locale.ROOT, PARAMETER_FORMAT, TIMESTAMP_PARAMETER, unixTime)); 088 extraParams.add(String.format(Locale.ROOT, PARAMETER_FORMAT, NONCE_PARAMETER, nonce)); 089 String protectedParams = Joiner.on(PARAMETER_SEPARATOR).join(extraParams); 090 091 return generateRecurlyHMAC(privateJsKey, protectedParams) + "|" + protectedParams; 092 } 093 094 /** 095 * HMAC-SHA1 Hash Generator - Helper method 096 * <p> 097 * Returns a signature key for use with recurly.js BuildSubscriptionForm. 098 * 099 * @param privateJsKey recurly.js private key 100 * @param protectedParams protected parameter string in the format: <secure_hash>|<protected_string> 101 * @return subscription object on success, null otherwise 102 */ 103 private static String generateRecurlyHMAC(String privateJsKey, String protectedParams) { 104 try { 105 SecretKey sk = new SecretKeySpec(privateJsKey.getBytes(), "HmacSHA1"); 106 Mac mac = Mac.getInstance("HmacSHA1"); 107 mac.init(sk); 108 byte[] result = mac.doFinal(protectedParams.getBytes("UTF-8")); 109 return BaseEncoding.base16().encode(result).toLowerCase(); 110 } catch (Exception e) { 111 log.error("Error while trying to generate Recurly HMAC signature", e); 112 return null; 113 } 114 } 115 116}