[英]What fields do I verify in a x509 (as x5c header in a JWS) to prove legitimacy of the Certificate?
I've already posted a similar question here , but I've realized that my issue could have more to do with x509 certificate rather than JWS in general.我已经在这里发布了一个类似的问题,但我意识到我的问题可能更多地与 x509 证书有关,而不是一般的 JWS。
Here's the thing, I'm pretty new to JWS, and Apple now transmits them as part of their server-to-server communication.事情是这样的,我对 JWS 很陌生,Apple 现在将它们作为服务器到服务器通信的一部分进行传输。 I'm trying to understand how to fully guarantee the validity of the JWS, because from what I understand, the signature verification only implies that the entire JWS wasn't tampered with.
我试图了解如何完全保证 JWS 的有效性,因为据我了解,签名验证仅意味着整个 JWS 未被篡改。 I don't know how to actually verify that this payload is indeed coming from a trusted source (aka Apple) .
我不知道如何实际验证此有效负载确实来自受信任的来源(又名 Apple) 。
Here's what I got so far (PHP):这是我到目前为止所得到的(PHP):
//1. explode jws and decode what's needed
$components = explode('.', $jws);
$headerJson = json_decode(base64_decode($components[0]),true);
$signature = base64Url_decode($components[2]);
//2. extract all certificates from 'x5c' header
foreach ($headerJson['x5c'] as $x5c){
$c = '-----BEGIN CERTIFICATE-----'.PHP_EOL;
$c .= chunk_split($x5c,64,PHP_EOL);
$c .= '-----END CERTIFICATE-----'.PHP_EOL;
$certificates[] = openssl_x509_read($c);
}
//3. verify validity of certificate chain (each one is signed by the next, except root cert)
for($i = 0; $i < count($certificates); $i++){
if ($i == count($certificates) - 1){
if (openssl_x509_verify($certificates[$i], $certificates[$i]) != 1){
throw new Exception("Invalid Root Certificate");
}
}
else{
if (openssl_x509_verify($certificates[$i], $certificates[$i+1]) != 1){
throw new Exception("Invalid Certificate");
}
}
}
//4. get public_key from first certificate
$public_key = openssl_pkey_get_public($certificates[0]);
//5. verify entire token, including signature (using the Firebase library)
$parsed_token = (array) \Firebase\JWT\JWT::decode($jws, $public_key, ['ES256']);
//helper function: a simple base64 url decoder
function base64Url_decode($data){
return base64_decode(str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', STR_PAD_RIGHT));
}
Thanks!谢谢!
I'm not sure if this is correct or pointless, but after doing a bit more research, this is what I've done:我不确定这是正确的还是毫无意义的,但在做了更多的研究之后,这就是我所做的:
openssl_x509_parse()
.openssl_x509_parse()
解析链中的每个证书。subject['C']
and subject['O']
with hardcoded values.subject['C']
和subject['O']
与硬编码值进行比较。subject['CN']
and issuer['CN']
with hardcoded values.subject['CN']
和issuer['CN']
与硬编码值进行比较。extensions['authorityKeyIdentifier']
hold a value, except for the last certificate (root).extensions['authorityKeyIdentifier']
都有一个值,最后一个证书(根)除外。 Does this make sense to anyone?这对任何人都有意义吗?
verify jws in Java code在 Java 代码中验证 jws
import cn.hutool.core.codec.Base64;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson2.JSONPath;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.ringle.framework.json.JSONUtils;
import com.ringle.framework.response.ServiceResponseStatusEnum;
import java.io.ByteArrayInputStream;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.ECPublicKey;
import java.util.List;
import lombok.Data;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
public interface ApplePayNotificationV2DTO {
@Setter
@Slf4j
class NotificationV2 {
private static Certificate appleRootCa;
private String signedPayload;
public NotificationV2() throws CertificateException {
if (ObjectUtil.isNull(appleRootCa)) {
appleRootCa = CertificateFactory.getInstance("X.509").generateCertificate(
ResourceUtil.getStream("applepay/AppleRootCA-G3.cer")
);
}
}
public ResponseBodyV2DecodedPayload getResponse() {
if (!verify()) {
throw ServiceResponseStatusEnum.BAD_REQUEST.buildException();
}
var decodedJWT = JWT.decode(signedPayload);
var payload = new String(Base64.decode(decodedJWT.getPayload()));
log.info("Apple Pay Notification Payload={}", payload);
return JSONUtils.fromJsonNonNull(
payload,
ResponseBodyV2DecodedPayload.class
);
}
/**
* 验证JWT
*/
private boolean verify() {
var valid = true;
try {
// 拿到 header
var decodedJWT = JWT.decode(signedPayload);
var header = new String(Base64.decode(decodedJWT.getHeader()));
// 获取证书链
var caChain = JSONUtils.<List<String>>fromJsonNonNull(
JSONPath.extract(header, "$.x5c").toString(),
List.class
);
// 获取公钥并验证根证书
var publicKey = getPublicKeyByX5c(caChain);
// 验证 token
var algorithm = Algorithm.ECDSA256((ECPublicKey) publicKey, null);
algorithm.verify(decodedJWT);
} catch (Exception e) {
valid = false;
log.error("Apple Pay JWS Verify Fail", e);
}
return valid;
}
/**
* 获取公钥
*
* @param x5c
*
* @return PublicKey
*/
private PublicKey getPublicKeyByX5c(List<String> x5c) throws CertificateException {
var factory = CertificateFactory.getInstance("X.509");
var jwtSignCa = (X509Certificate) factory.generateCertificate(
new ByteArrayInputStream(Base64.decode(x5c.get(0)))
);
var jwtRootCa = (X509Certificate) factory.generateCertificate(
new ByteArrayInputStream(Base64.decode(x5c.get(2)))
);
// 验证证书是否是苹果颁发的
try {
appleRootCa.verify(jwtRootCa.getPublicKey());
} catch (Exception e) {
throw new RuntimeException(e);
}
return jwtSignCa.getPublicKey();
}
}
@Data
@Slf4j
class ResponseBodyV2DecodedPayload {
private String notificationType;
private String subtype;
private String notificationUUID;
private Object data;
private String version;
private Long signedDate;
/**
* 获取交易详情
*
* @return
*/
public Transaction getTransaction() {
var signedTransactionInfo = JSONPath.extract(
JSONUtils.toJson(data),
"$.signedTransactionInfo"
);
if (ObjectUtil.isNull(signedTransactionInfo)) {
log.warn("Get ApplePay Transaction Fail : transaction is null. responseBody={}", this);
throw ServiceResponseStatusEnum.BAD_REQUEST.buildException();
}
var decodedJWT = JWT.decode(signedTransactionInfo.toString());
return JSONUtils.fromJsonNonNull(decodedJWT.getPayload(), Transaction.class);
}
}
@Data
class Transaction {
private String environment;
private String transactionId;
private Long revocationDate;
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.