All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.mizhousoft.apple.open.service.impl.JWKAuthenticationServiceImpl Maven / Gradle / Ivy

The newest version!
package com.mizhousoft.apple.open.service.impl;

import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPublicKeySpec;
import java.util.List;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.mizhousoft.apple.common.AppleException;
import com.mizhousoft.apple.open.model.AppleJWKKey;
import com.mizhousoft.apple.open.model.AppleJWTPlayload;
import com.mizhousoft.apple.open.model.ApplePublicKeys;
import com.mizhousoft.apple.open.result.AppleVerifyResult;
import com.mizhousoft.apple.open.service.JWKAuthenticationService;
import com.mizhousoft.commons.json.JSONException;
import com.mizhousoft.commons.json.JSONUtils;
import com.mizhousoft.commons.lang.CharEncoding;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import kong.unirest.core.Unirest;
import kong.unirest.core.UnirestException;

/**
 * 苹果认证服务
 *
 * @version
 */
public class JWKAuthenticationServiceImpl implements JWKAuthenticationService
{
	private static final Logger LOG = LoggerFactory.getLogger(JWKAuthenticationServiceImpl.class);

	private ApplePublicKeys applePublicKeys;

	/**
	 * {@inheritDoc}
	 * 
	 * @throws AppleException
	 */
	@Override
	public AppleVerifyResult verify(String audience, String subject, String identityToken) throws AppleException
	{
		ApplePublicKeys applePubKeys = getApplePublicKeys();
		List keys = applePubKeys.getKeys();

		AppleException exception = null;

		String jwt = new String(Base64.decodeBase64(identityToken), CharEncoding.UTF8);
		AppleJWTPlayload playload = parseJWTPlayload(jwt);

		for (AppleJWKKey key : keys)
		{
			String n = key.getN();
			String e = key.getE();
			PublicKey pk = getPublicKey(n, e);

			try
			{
				Claims claims = verify(pk, jwt, audience, subject);

				AppleVerifyResult result = new AppleVerifyResult();
				result.setSubject(claims.getSubject());
				result.setEmail(playload.getEmail());

				return result;
			}
			catch (AppleException ex)
			{
				exception = ex;
			}
		}

		if (null == exception)
		{
			exception = new AppleException("Subject verify failed.");
		}

		// 防止公钥失效
		this.applePublicKeys = null;

		throw exception;
	}

	private synchronized ApplePublicKeys getApplePublicKeys() throws AppleException
	{
		if (null != applePublicKeys)
		{
			return applePublicKeys;
		}

		ApplePublicKeys applePubKeys = null;
		AppleException exception = null;

		for (int i = 0; i < 3; i++)
		{
			LOG.info("Start to fetch apple public key.");

			try
			{
				applePubKeys = Unirest.get(FETCH_PUBLIC_KEY_URL).asObject(ApplePublicKeys.class).getBody();

				if (null != applePubKeys && !CollectionUtils.isEmpty(applePubKeys.getKeys()))
				{
					LOG.info("Fetch apple public key successfully.");

					break;
				}
			}
			catch (UnirestException e)
			{
				exception = new AppleException("Fetch public key failed.", e);
			}
		}

		if (null == applePubKeys)
		{
			if (null == exception)
			{
				exception = new AppleException("AppleJWKKey is null.");
			}

			throw exception;
		}

		applePublicKeys = applePubKeys;

		return applePubKeys;
	}

	private PublicKey getPublicKey(String n, String e) throws AppleException
	{
		try
		{
			BigInteger bigIntModulus = new BigInteger(1, Base64.decodeBase64(n));
			BigInteger bigIntPrivateExponent = new BigInteger(1, Base64.decodeBase64(e));
			RSAPublicKeySpec keySpec = new RSAPublicKeySpec(bigIntModulus, bigIntPrivateExponent);
			KeyFactory keyFactory = KeyFactory.getInstance("RSA");
			PublicKey publicKey = keyFactory.generatePublic(keySpec);
			return publicKey;
		}
		catch (NoSuchAlgorithmException ex)
		{
			throw new AppleException("no such algorithm.", ex);
		}
		catch (InvalidKeySpecException ex)
		{
			throw new AppleException("invalid key.", ex);
		}
	}

	private Claims verify(PublicKey key, String jwt, String audience, String subject) throws AppleException
	{
		try
		{
			JwtParser jwtParser = Jwts.parser().verifyWith(key).requireAudience(audience).requireSubject(subject).requireIssuer(ISSUER)
			        .build();

			Jws claim = jwtParser.parseSignedClaims(jwt);
			if (claim != null && claim.getPayload().containsKey("auth_time"))
			{
				return claim.getPayload();
			}

			throw new AppleException("Jwt verify failed.");
		}
		catch (ExpiredJwtException e)
		{
			throw new AppleException("Jwt expired.", e);
		}
		catch (Exception e)
		{
			throw new AppleException("Jwt verify failed.", e);
		}
	}

	private AppleJWTPlayload parseJWTPlayload(String jwt) throws AppleException
	{
		try
		{
			int index1 = jwt.indexOf(".");
			int index2 = jwt.indexOf(".", index1 + 1);
			if (index1 < 0 || index2 < 0)
			{
				throw new AppleException("jwt data is invalid, data:" + jwt);
			}

			String str = jwt.substring(index1 + 1, index2);
			String body = new String(Base64.decodeBase64(str), CharEncoding.UTF8);

			AppleJWTPlayload playload = JSONUtils.parse(body, AppleJWTPlayload.class);
			return playload;
		}
		catch (JSONException e)
		{
			throw new AppleException("jwt data is invalid, data:" + jwt, e);
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy