Java服务器验证苹果支付回调过程中,可以使用哪些算法对收据进行签名验证
苹果支付回调过程中,可以使用以下算法对收据进行签名验证:
RSA算法:使用公钥对收据进行签名验证,苹果支付返回的收据中包含了用于RSA签名的公钥。
HMAC算法:使用共享密钥对收据进行签名验证,苹果支付返回的收据中包含了用于HMAC签名的共享密钥。
ECDSA算法:使用椭圆曲线数字签名算法对收据进行签名验证,苹果支付返回的收据中包含了用于ECDSA签名的公钥。
需要注意的是,苹果支付回调过程中,还需要验证收据的有效期和交易状态等信息,以确保交易的有效性。
用Java实现服务器验证苹果支付回调的逻辑。verifyReceipt方法将收据发送到苹果服务器进行验证,并返回验证结果。verifySignature方法使用共享密钥对收据进行签名验证。verify方法将验证收据和签名是否有效。使用ECDSA算法对收据进行签名
下面是用Java实现服务器验证苹果支付回调的逻辑的代码:
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
public class ApplePayVerifier {
private static final String VERIFY_URL = "https://buy.itunes.apple.com/verifyReceipt";
private static final String SHARED_SECRET = "your_shared_secret_here";
public boolean verify(String receipt, String signature) {
boolean isValidReceipt = verifyReceipt(receipt);
boolean isValidSignature = verifySignature(receipt, signature);
return isValidReceipt && isValidSignature;
}
private boolean verifyReceipt(String receipt) {
try {
URL url = new URL(VERIFY_URL);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setDoOutput(true);
String data = "{\"receipt-data\":\"" + receipt + "\",\"password\":\"" + SHARED_SECRET + "\"}";
connection.getOutputStream().write(data.getBytes("UTF-8"));
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
response.append(line);
}
reader.close();
return response.toString().contains("\"status\":0");
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
private boolean verifySignature(String receipt, String signature) {
try {
byte[] decodedSignature = Base64.getDecoder().decode(signature);
byte[] decodedReceipt = receipt.getBytes("UTF-8");
KeyFactory keyFactory = KeyFactory.getInstance("EC");
byte[] publicKeyBytes = Base64.getDecoder().decode("your_public_key_here");
PublicKey publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(publicKeyBytes));
Signature ecdsaVerify = Signature.getInstance("SHA256withECDSA");
ecdsaVerify.initVerify(publicKey);
ecdsaVerify.update(decodedReceipt);
return ecdsaVerify.verify(decodedSignature);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
}
在上述代码中,verify方法调用了verifyReceipt方法和verifySignature方法,分别对收据和签名进行验证,并返回验证结果。
verifyReceipt方法将收据发送到苹果服务器进行验证。在发送请求时,需要将收据和共享密钥组成一个JSON字符串并进行Base64编码。苹果服务器返回的响应中包含了验证结果,我们只需要判断响应中是否包含了"status":0即可。
verifySignature方法使用ECDSA算法对收据进行签名验证。在验证签名时,需要将签名和收据进行Base64解码,并使用椭圆曲线数字签名算法对收据进行验证。在本例中,我们使用了SHA256withECDSA算法对收据进行签名验证。
需要注意的是,本例中使用的是ECDSA算法对收据进行签名验证。如果使用RSA算法或HMAC算法,需要相应地修改代码。
应该判定下是否是沙盒环境
在服务器端验证苹果支付回调时,需要判断当前环境是否为沙盒环境(Sandbox)。因为在沙盒环境中,测试账号和测试收据都是不真实的,因此需要使用苹果提供的沙盒验证地址和测试账号进行验证。
下面是修改后的代码,增加了判断是否为沙盒环境的逻辑:
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
public class ApplePayVerifier {
private static final String PRODUCTION_VERIFY_URL = "https://buy.itunes.apple.com/verifyReceipt";
private static final String SANDBOX_VERIFY_URL = "https://sandbox.itunes.apple.com/verifyReceipt";
private static final String SHARED_SECRET = "your_shared_secret_here";
private static final String PRODUCTION_PUBLIC_KEY = "your_production_public_key_here";
private static final String SANDBOX_PUBLIC_KEY = "your_sandbox_public_key_here";
private static final String SANDBOX_ENVIRONMENT = "sandbox";
public boolean verify(String receipt, String signature, String environment) {
boolean isValidReceipt = verifyReceipt(receipt, environment);
boolean isValidSignature = verifySignature(receipt, signature, environment);
return isValidReceipt && isValidSignature;
}
private boolean verifyReceipt(String receipt, String environment) {
try {
URL url = new URL(getVerifyUrl(environment));
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setDoOutput(true);
String data = "{\"receipt-data\":\"" + receipt + "\",\"password\":\"" + SHARED_SECRET + "\"}";
connection.getOutputStream().write(data.getBytes("UTF-8"));
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
response.append(line);
}
reader.close();
return response.toString().contains("\"status\":0");
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
private boolean verifySignature(String receipt, String signature, String environment) {
try {
byte[] decodedSignature = Base64.getDecoder().decode(signature);
byte[] decodedReceipt = receipt.getBytes("UTF-8");
PublicKey publicKey = getPublicKey(environment);
Signature ecdsaVerify = Signature.getInstance("SHA256withECDSA");
ecdsaVerify.initVerify(publicKey);
ecdsaVerify.update(decodedReceipt);
return ecdsaVerify.verify(decodedSignature);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
private String getVerifyUrl(String environment) {
if (SANDBOX_ENVIRONMENT.equals(environment)) {
return SANDBOX_VERIFY_URL;
} else {
return PRODUCTION_VERIFY_URL;
}
}
private PublicKey getPublicKey(String environment) throws Exception {
KeyFactory keyFactory = KeyFactory.getInstance("EC");
byte[] publicKeyBytes = Base64.getDecoder().decode(getPublicKeyString(environment));
return keyFactory.generatePublic(new X509EncodedKeySpec(publicKeyBytes));
}
private String getPublicKeyString(String environment) {
if (SANDBOX_ENVIRONMENT.equals(environment)) {
return SANDBOX_PUBLIC_KEY;
} else {
return PRODUCTION_PUBLIC_KEY;
}
}
}
在上述代码中,我们新增了一个environment参数,用于传入当前环境是否为沙盒环境。在verifyReceipt方法和verifySignature方法中,我们分别根据当前环境获取验证地址和公钥。在getVerifyUrl方法和getPublicKeyString方法中,我们根据当前环境返回对应的验证地址和公钥。在getPublicKey方法中,我们使用KeyFactory和X509EncodedKeySpec将公钥字符串转换为PublicKey对象。
使用时,可以通过传入environment参数来判断当前环境是否为沙盒环境,例如:
ApplePayVerifier verifier = new ApplePayVerifier();
boolean isValid = verifier.verify(receipt, signature, "sandbox");