metaInfo = new HashMap<>();
+
+ metaInfo.put("CLIENT_ID", AUD);
+ metaInfo.put("REDIRECT_URI", APPLE_WEBSITE_URL);
+ metaInfo.put("NONCE", "20B20D-0S8-1K8"); // Test value
+
+ return metaInfo;
+ }
+
+ /**
+ * id_token을 decode해서 payload 값 가져오기
+ *
+ * @param id_token
+ * @return
+ */
+ public JSONObject decodeFromIdToken(String id_token) {
+
+ try {
+ SignedJWT signedJWT = SignedJWT.parse(id_token);
+ System.out.println("=====payload1"+signedJWT );
+ ReadOnlyJWTClaimsSet getPayload = signedJWT.getJWTClaimsSet();
+ System.out.println("=====payload2"+getPayload);
+ ObjectMapper objectMapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false);
+// objectMapper.enable(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT); // null 값 허용
+// objectMapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
+// Payload payload = objectMapper.readValue(getPayload.toJSONObject().toJSONString(), Payload.class);
+ JSONObject payload = objectMapper.readValue(getPayload.toJSONObject().toJSONString(), JSONObject.class);
+
+ System.out.println("=====payload3"+payload );
+ if (payload != null) {
+ return payload;
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+
+}
diff --git a/server/src/main/java/com/yogit/server/applelogin/util/ECPrivateKeyImpl2.java b/server/src/main/java/com/yogit/server/applelogin/util/ECPrivateKeyImpl2.java
new file mode 100644
index 0000000..ecb82e8
--- /dev/null
+++ b/server/src/main/java/com/yogit/server/applelogin/util/ECPrivateKeyImpl2.java
@@ -0,0 +1,185 @@
+package com.yogit.server.applelogin.util;
+
+import sun.security.pkcs.PKCS8Key;
+import sun.security.util.*;
+import sun.security.x509.AlgorithmId;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.AlgorithmParameters;
+import java.security.InvalidKeyException;
+import java.security.interfaces.ECPrivateKey;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.InvalidParameterSpecException;
+import java.util.Arrays;
+
+/**
+ * Key implementation for EC private keys.
+ *
+ * ASN.1 syntax for EC private keys from SEC 1 v1.5 (draft):
+ *
+ *
+ * EXPLICIT TAGS
+ *
+ * ECPrivateKey ::= SEQUENCE {
+ * version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
+ * privateKey OCTET STRING,
+ * parameters [0] ECDomainParameters {{ SECGCurveNames }} OPTIONAL,
+ * publicKey [1] BIT STRING OPTIONAL
+ * }
+ *
+ *
+ * We currently ignore the optional parameters and publicKey fields. We
+ * require that the parameters are encoded as part of the AlgorithmIdentifier,
+ * not in the private key structure.
+ *
+ * @since 1.6
+ * @author Andreas Sterbenz
+ */
+public final class ECPrivateKeyImpl2 extends PKCS8Key implements ECPrivateKey {
+
+ private static final long serialVersionUID = 88695385615075129L;
+
+ private BigInteger s; // private value
+ private byte[] arrayS; // private value as a little-endian array
+ private ECParameterSpec params;
+
+ /**
+ * Construct a key from its encoding. Called by the ECKeyFactory.
+ */
+ ECPrivateKeyImpl2(byte[] encoded) throws InvalidKeyException {
+ super(encoded);
+ parseKeyBits();
+ }
+
+ /**
+ * Construct a key from its components. Used by the
+ * KeyFactory.
+ */
+ ECPrivateKeyImpl2(BigInteger s, ECParameterSpec params)
+ throws InvalidKeyException {
+ this.s = s;
+ this.params = params;
+ makeEncoding(s);
+
+ }
+
+ ECPrivateKeyImpl2(byte[] s, ECParameterSpec params)
+ throws InvalidKeyException {
+ this.arrayS = s.clone();
+ this.params = params;
+ makeEncoding(s);
+ }
+
+ private void makeEncoding(byte[] s) throws InvalidKeyException {
+ algid = new AlgorithmId
+ (AlgorithmId.EC_oid, ECParameters.getAlgorithmParameters(params));
+ try {
+ DerOutputStream out = new DerOutputStream();
+ out.putInteger(1); // version 1
+ byte[] privBytes = s.clone();
+ ArrayUtil.reverse(privBytes);
+ out.putOctetString(privBytes);
+ Arrays.fill(privBytes, (byte)0);
+ DerValue val = DerValue.wrap(DerValue.tag_Sequence, out);
+ key = val.toByteArray();
+ val.clear();
+ } catch (IOException exc) {
+ // should never occur
+ throw new InvalidKeyException(exc);
+ }
+ }
+
+ private void makeEncoding(BigInteger s) throws InvalidKeyException {
+ algid = new AlgorithmId(AlgorithmId.EC_oid,
+ ECParameters.getAlgorithmParameters(params));
+ try {
+ byte[] sArr = s.toByteArray();
+ // convert to fixed-length array
+ int numOctets = (params.getOrder().bitLength() + 7) / 8;
+ byte[] sOctets = new byte[numOctets];
+ int inPos = Math.max(sArr.length - sOctets.length, 0);
+ int outPos = Math.max(sOctets.length - sArr.length, 0);
+ int length = Math.min(sArr.length, sOctets.length);
+ System.arraycopy(sArr, inPos, sOctets, outPos, length);
+ Arrays.fill(sArr, (byte)0);
+
+ DerOutputStream out = new DerOutputStream();
+ out.putInteger(1); // version 1
+ out.putOctetString(sOctets);
+ Arrays.fill(sOctets, (byte)0);
+ DerValue val = DerValue.wrap(DerValue.tag_Sequence, out);
+ key = val.toByteArray();
+ val.clear();
+ } catch (IOException exc) {
+ throw new AssertionError("Should not happen", exc);
+ }
+ }
+
+ // see JCA doc
+ public String getAlgorithm() {
+ return "EC";
+ }
+
+ // see JCA doc
+ public BigInteger getS() {
+ if (s == null) {
+ byte[] arrCopy = arrayS.clone();
+ ArrayUtil.reverse(arrCopy);
+ s = new BigInteger(1, arrCopy);
+ Arrays.fill(arrCopy, (byte)0);
+ }
+ return s;
+ }
+
+ public byte[] getArrayS() {
+ if (arrayS == null) {
+ arrayS = ECUtil.sArray(getS(), params);
+ }
+ return arrayS.clone();
+ }
+
+ // see JCA doc
+ public ECParameterSpec getParams() {
+ return params;
+ }
+
+ private void parseKeyBits() throws InvalidKeyException {
+ try {
+ DerInputStream in = new DerInputStream(key);
+ DerValue derValue = in.getDerValue();
+ if (derValue.tag != DerValue.tag_Sequence) {
+ throw new IOException("Not a SEQUENCE");
+ }
+ DerInputStream data = derValue.data;
+ int version = data.getInteger();
+ if (version != 1) {
+ throw new IOException("Version must be 1");
+ }
+ byte[] privData = data.getOctetString();
+ ArrayUtil.reverse(privData);
+ arrayS = privData;
+ while (data.available() != 0) {
+ DerValue value = data.getDerValue();
+ if (value.isContextSpecific((byte) 0)) {
+ // ignore for now
+ } else if (value.isContextSpecific((byte) 1)) {
+ // ignore for now
+ } else {
+ throw new InvalidKeyException("Unexpected value: " + value);
+ }
+ }
+ AlgorithmParameters algParams = this.algid.getParameters();
+ if (algParams == null) {
+ throw new InvalidKeyException("EC domain parameters must be "
+ + "encoded in the algorithm identifier");
+ }
+ params = algParams.getParameterSpec(ECParameterSpec.class);
+ } catch (IOException e) {
+ throw new InvalidKeyException("Invalid EC private key", e);
+ } catch (InvalidParameterSpecException e) {
+ throw new InvalidKeyException("Invalid EC private key", e);
+ }
+ }
+}
+
diff --git a/server/src/main/java/com/yogit/server/applelogin/util/HttpClientUtils.java b/server/src/main/java/com/yogit/server/applelogin/util/HttpClientUtils.java
new file mode 100644
index 0000000..63efdf0
--- /dev/null
+++ b/server/src/main/java/com/yogit/server/applelogin/util/HttpClientUtils.java
@@ -0,0 +1,120 @@
+package com.yogit.server.applelogin.util;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.http.HttpEntity;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.util.EntityUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+public class HttpClientUtils {
+
+ private static Logger logger = LoggerFactory.getLogger(HttpClientUtils.class);
+ private static ObjectMapper objectMapper = new ObjectMapper();
+
+ public static String doGet(String url) {
+ String result = null;
+ CloseableHttpClient httpclient = null;
+ CloseableHttpResponse response = null;
+ Integer statusCode = null;
+ String reasonPhrase = null;
+
+ try {
+ httpclient = HttpClients.createDefault();
+ HttpGet get = new HttpGet(url);
+ response = httpclient.execute(get);
+ statusCode = response.getStatusLine().getStatusCode();
+ reasonPhrase = response.getStatusLine().getReasonPhrase();
+ HttpEntity entity = response.getEntity();
+ result = EntityUtils.toString(entity, "UTF-8");
+ EntityUtils.consume(entity);
+
+ if (statusCode != 200) {
+ logger.error(String.format("[doGet]http get url(%s) failed. status code:%s. reason:%s. result:%s", url, statusCode, reasonPhrase, result));
+ }
+ } catch (Throwable t) {
+ logger.error(String.format("[doGet]http get url(%s) failed. status code:%s. reason:%s.", url, statusCode, reasonPhrase), t);
+ } finally {
+ try {
+ if (response != null) {
+ response.close();
+ }
+ if (httpclient != null) {
+ httpclient.close();
+ }
+ } catch (IOException e) {
+ logger.error(String.format("[doGet]release http get resource failed. url(%s). reason:%s.", url, e.getMessage()));
+ }
+ }
+
+ return result;
+ }
+
+ public static String doPost(String url, Map param) {
+ String result = null;
+ CloseableHttpClient httpclient = null;
+ CloseableHttpResponse response = null;
+ Integer statusCode = null;
+ String reasonPhrase = null;
+ try {
+ httpclient = HttpClients.createDefault();
+ HttpPost httpPost = new HttpPost(url);
+ httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded");
+ List nvps = new ArrayList<>();
+ Set> entrySet = param.entrySet();
+ for (Entry entry : entrySet) {
+ String fieldName = entry.getKey();
+ String fieldValue = entry.getValue();
+ nvps.add(new BasicNameValuePair(fieldName, fieldValue));
+ }
+ UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(nvps);
+ httpPost.setEntity(formEntity);
+ response = httpclient.execute(httpPost);
+ statusCode = response.getStatusLine().getStatusCode();
+ reasonPhrase = response.getStatusLine().getReasonPhrase();
+ HttpEntity entity = response.getEntity();
+ result = EntityUtils.toString(entity, "UTF-8");
+
+ if (statusCode != 200) {
+ logger.error(String.format("[doPost]post url(%s) failed. status code:%s. reason:%s. param:%s. result:%s", url, statusCode, reasonPhrase, objectMapper.writeValueAsString(param), result));
+ }
+ EntityUtils.consume(entity);
+ } catch (Throwable t) {
+ try {
+ logger.error(String.format("[doPost]post url(%s) failed. status code:%s. reason:%s. param:%s.", url, statusCode, reasonPhrase, objectMapper.writeValueAsString(param)), t);
+ } catch (JsonProcessingException e) {
+ }
+ } finally {
+ try {
+ if (response != null) {
+ response.close();
+ }
+ if (httpclient != null) {
+ httpclient.close();
+ }
+ } catch (IOException e) {
+ try {
+ logger.error(String.format("[doPost]release http post resource failed. url(%s). reason:%s, param:%s.", url, e.getMessage(), objectMapper.writeValueAsString(param)));
+ } catch (JsonProcessingException ex) {
+ }
+ }
+ }
+ return result;
+ }
+
+}
diff --git a/server/src/main/resources/application.properties b/server/src/main/resources/application.properties
new file mode 100644
index 0000000..b0b6d37
--- /dev/null
+++ b/server/src/main/resources/application.properties
@@ -0,0 +1,15 @@
+logging.level.com.whitepaek.demosigninwithapple=DEBUG
+
+APPLE.AUTH.TOKEN.URL=https://appleid.apple.com/auth/token
+APPLE.PUBLICKEY.URL=https://appleid.apple.com/auth/keys
+# redirect url 정보
+APPLE.WEBSITE.URL=https://yogit.world/redirect
+APPLE.ISS=https://appleid.apple.com
+# client_ID
+APPLE.AUD=com.Branch.service
+#Team_ID
+APPLE.TEAM.ID=9487SKDZZB
+# kid
+APPLE.KEY.ID=QLHFNT37VK
+# key id path : AuthKey_[key_id], 애플 사이트에서 다운 받아야 함
+APPLE.KEY.PATH=static/AuthKey_QLHFNT37VK.p8
\ No newline at end of file
diff --git a/server/src/main/resources/static/AuthKey_QLHFNT37VK.p8 b/server/src/main/resources/static/AuthKey_QLHFNT37VK.p8
new file mode 100644
index 0000000..59a3419
--- /dev/null
+++ b/server/src/main/resources/static/AuthKey_QLHFNT37VK.p8
@@ -0,0 +1,6 @@
+-----BEGIN PRIVATE KEY-----
+MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQg6bpEOxRZ0VuuNgJ6
+drfzrk9/9v0uNhEaXqpqpiEdT1OgCgYIKoZIzj0DAQehRANCAARB7v5hC6Pv68oB
+H/gxjfHwjDrLTQBzXqWhjjVjK8UOf0vhGH2uGQ09KfFdt1JSfFJHycpkF4+VkwsQ
+yDh9qWTX
+-----END PRIVATE KEY-----
\ No newline at end of file
diff --git a/server/src/main/resources/templates/index.html b/server/src/main/resources/templates/index.html
new file mode 100644
index 0000000..15f03c3
--- /dev/null
+++ b/server/src/main/resources/templates/index.html
@@ -0,0 +1,22 @@
+
+
+
+
+ Apple sign-in
+
+
+웰컴 페이지
+
+
+
+
+
\ No newline at end of file