Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 33 additions & 16 deletions src/main/java/com/razorpay/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,27 +29,40 @@ public static boolean verifySubscription(JSONObject attributes, String apiSecret
String payload = paymentId + '|' + subscriptionId;
return verifySignature(payload, expectedSignature, apiSecret);
}

public static boolean verifyPaymentLink(JSONObject attributes, String apiSecret)
throws RazorpayException {
String expectedSignature = attributes.getString("razorpay_signature");
String paymentLinkStatus = attributes.getString("payment_link_status");
String paymentLinkId = attributes.getString("payment_link_id");
String paymentLinkRefId = attributes.getString("payment_link_reference_id");
String paymentId = attributes.getString("razorpay_payment_id");
String payload = paymentLinkId + '|' + paymentLinkRefId + '|' + paymentLinkStatus + '|' + paymentId;
return verifySignature(payload, expectedSignature, apiSecret);
throws RazorpayException {
String expectedSignature = attributes.getString("razorpay_signature");
String paymentLinkStatus = attributes.getString("payment_link_status");
String paymentLinkId = attributes.getString("payment_link_id");
String paymentLinkRefId = attributes.getString("payment_link_reference_id");
String paymentId = attributes.getString("razorpay_payment_id");
String payload = paymentLinkId + '|' + paymentLinkRefId + '|' + paymentLinkStatus + '|' + paymentId;
return verifySignature(payload, expectedSignature, apiSecret);
}

public static boolean verifyWebhookSignature(String payload, String expectedSignature,
String webhookSecret) throws RazorpayException {
return verifySignature(payload, expectedSignature, webhookSecret);
}

public static boolean verifyWebhookSignature(byte[] payload, String expectedSignature,
String webhookSecret) throws RazorpayException {
return verifySignature(payload, expectedSignature, webhookSecret);
}

public static boolean verifySignature(String payload, String expectedSignature, String secret)
throws RazorpayException {
String actualSignature = getHash(payload, secret);
return isEqual(actualSignature.getBytes(), expectedSignature.getBytes());
return isEqual(actualSignature.getBytes(StandardCharsets.UTF_8),
expectedSignature.getBytes(StandardCharsets.UTF_8));
}

public static boolean verifySignature(byte[] payload, String expectedSignature, String secret)
throws RazorpayException {
String actualSignature = getHash(payload, secret);
return isEqual(actualSignature.getBytes(StandardCharsets.UTF_8),
expectedSignature.getBytes(StandardCharsets.UTF_8));
}

public static String generateOnboardingSignature(JSONObject attributes, String secret) throws RazorpayException {
Expand All @@ -68,8 +81,7 @@ public static String encrypt(String dataToEncrypt, String secret) throws Razorpa
cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmSpec);
byte[] encryptedData = cipher.doFinal(dataToEncrypt.getBytes(StandardCharsets.UTF_8));
return bytesToHex(encryptedData);
}
catch (Exception e) {
} catch (Exception e) {
throw new RazorpayException(e.getMessage());
}
}
Expand All @@ -87,20 +99,25 @@ public static String bytesToHex(byte[] bytes) {
}

public static String getHash(String payload, String secret) throws RazorpayException {
return getHash(payload.getBytes(StandardCharsets.UTF_8), secret);
}

public static String getHash(byte[] payload, String secret) throws RazorpayException {
Mac sha256_HMAC;
try {
sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
sha256_HMAC.init(secret_key);
byte[] hash = sha256_HMAC.doFinal(payload.getBytes());
byte[] hash = sha256_HMAC.doFinal(payload);
return new String(Hex.encodeHex(hash));
} catch (Exception e) {
throw new RazorpayException(e.getMessage());
}
}

/**
* We are not using String.equals() method because of security issue mentioned in
* We are not using String.equals() method because of security issue mentioned
* in
* <a href="http://security.stackexchange.com/a/83670">StackOverflow</a>
*
* @param a
Expand All @@ -117,4 +134,4 @@ private static boolean isEqual(byte[] a, byte[] b) {
}
return result == 0;
}
}
}
48 changes: 42 additions & 6 deletions src/test/java/com/razorpay/UtilsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ public class UtilsTest {

/**
* Verify razorpay payment signature
*
* @throws RazorpayException
*/
@Test
public void verifyPaymentSignature() throws RazorpayException{
public void verifyPaymentSignature() throws RazorpayException {
JSONObject options = new JSONObject();
options.put("razorpay_order_id", "order_IEIaMR65cu6nz3");
options.put("razorpay_payment_id", "pay_IH4NVgf4Dreq1l");
Expand All @@ -28,10 +29,11 @@ public void verifyPaymentSignature() throws RazorpayException{

/**
* Verify razorpay subscription
*
* @throws RazorpayException
*/
@Test
public void verifySubscription() throws RazorpayException{
public void verifySubscription() throws RazorpayException {
JSONObject options = new JSONObject();
options.put("razorpay_subscription_id", "sub_ID6MOhgkcoHj9I");
options.put("razorpay_payment_id", "pay_IDZNwZZFtnjyym");
Expand All @@ -42,10 +44,11 @@ public void verifySubscription() throws RazorpayException{

/**
* Verify razorpay payment link signature
*
* @throws RazorpayException
*/
@Test
public void verifyPaymentLink() throws RazorpayException{
public void verifyPaymentLink() throws RazorpayException {
JSONObject options = new JSONObject();
options.put("payment_link_reference_id", "TSsd1989");
options.put("razorpay_payment_id", "pay_IH3d0ara9bSsjQ");
Expand All @@ -58,14 +61,15 @@ public void verifyPaymentLink() throws RazorpayException{

/**
* Verify razorpay webhook signature
*
* @throws RazorpayException
*/
@Test
public void verifyWebhookSignature() throws RazorpayException{
public void verifyWebhookSignature() throws RazorpayException {
String signature = "2fe04e22977002e6c7cb553adab8b460cb9e2a4970d5953cb27a8472752e3bbc";
String payload = "{\"a\":1,\"b\":2,\"c\":{\"d\":3}}";
String secret = "123456";
assertTrue(Utils.verifyWebhookSignature(payload,signature, secret));
assertTrue(Utils.verifyWebhookSignature(payload, signature, secret));
}

@Test
Expand All @@ -86,10 +90,42 @@ public void testGenerateOnboardingSignature() throws RazorpayException {
}
}

@Test
public void verifyWebhookSignatureUtf8Payload() throws Exception {
String payload = "{\"name\":\"café ₹ 😄\",\"notes\":\"हेलो\"}";
String secret = "123456";

String signature = computeWebhookSignature(payload, secret);

assertTrue(Utils.verifyWebhookSignature(payload, signature, secret));
assertTrue(Utils.verifyWebhookSignature(payload.getBytes(StandardCharsets.UTF_8), signature, secret));
}

private String computeWebhookSignature(String payload, String secret) throws Exception {
javax.crypto.Mac sha256_HMAC = javax.crypto.Mac.getInstance("HmacSHA256");
SecretKeySpec secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
sha256_HMAC.init(secretKey);
byte[] hash = sha256_HMAC.doFinal(payload.getBytes(StandardCharsets.UTF_8));
return bytesToHex(hash);
}

private String bytesToHex(byte[] bytes) {
StringBuilder hexString = new StringBuilder();
for (byte b : bytes) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
}

private String decryptData(String encryptedHexData, String secret) throws Exception {
byte[] encryptedData = hexStringToByteArray(encryptedHexData);
return decrypt(encryptedData, secret);
}

public static String decrypt(byte[] encryptedData, String secret) throws Exception {
byte[] keyBytes = secret.substring(0, 16).getBytes(StandardCharsets.UTF_8);
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
Expand All @@ -107,7 +143,7 @@ public static byte[] hexStringToByteArray(String s) {
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
+ Character.digit(s.charAt(i + 1), 16));
}
return data;
}
Expand Down