시나리오: 저번 포스팅에서 서버를 만들어봤습니다. 이번에는 API 호출을 통해서 패스를 만들어보도록 하겠습니다.
2023.11.17 - [iOS 캐기/토이 프로젝트] - [애플월렛 패스] 참가증, 쿠폰, 티켓 같은 패스 만들어 애플월렛에 넣어보기
2024.08.12 - [iOS 캐기/토이 프로젝트] - [애플월렛 패스] 참가증, 쿠폰, 티켓같은 패스 서버 API 호출로 생성하기- 개발환경 세팅
2024.08.12 - [iOS 캐기/토이 프로젝트] - [애플월렛 패스] 참가증, 쿠폰, 티켓같은 패스 서버 API 호출로 생성하기- 구조 작성
이 코드는 Apple Wallet에서 사용할 수 있는 패스(Pass)를 생성하는 FastAPI 엔드포인트를 정의합니다. 주로 Apple Wallet용 패스(Pass)를 만들 때 필요한 작업을 처리하며, 생성된 패스를 클라이언트에게 파일로 반환합니다. 코드를 단계별로 설명하겠습니다.
@app.get("/generate_pass")
async def generate_pass():
이 데코레이터는 FastAPI에서 HTTP GET 요청을 처리하기 위한 엔드포인트를 정의합니다. 클라이언트가 `/generate_pass` 경로로 요청을 보내면, 이 함수가 호출됩니다.
logging.info("Start generating pass")
패스 생성 프로세스가 시작되었음을 기록합니다.
# 패스 정보 설정
card_info = EventTicket()
card_info.add_primary_field('event', 'Atlas Book Talk', 'event')
card_info.add_secondary_field('timestamp', '11/23/2024', 'DATE')
# Apple Pass 설정
team_identifier = "{#team_identifier}"
pass_type_identifier = "{#pass_type_identifier}"
organization_name = "{#organization_name}"
applepassgenerator_client = ApplePassGeneratorClient(team_identifier, pass_type_identifier, organization_name)
apple_pass = applepassgenerator_client.get_pass(card_info)
def extract_certificate_and_key(p12_path, cert_out_path, key_out_path, password):
try:
with open(p12_path, "rb") as p12_file:
p12_data = p12_file.read()
private_key, certificate, _ = pkcs12.load_key_and_certificates(p12_data, password.encode(), default_backend())
with open(cert_out_path, "wb") as pem_file:
pem_file.write(certificate.public_bytes(serialization.Encoding.PEM))
with open(key_out_path, "wb") as key_file:
key_file.write(private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.BestAvailableEncryption(password.encode())
))
logging.info("Certificate and private key extracted successfully.")
except Exception as e:
logging.error(f"Failed to extract certificate and key: {e}")
raise HTTPException(status_code=500, detail="Failed to extract certificate and key")
사용법:
# 인증서 및 키 파일 추출
extract_certificate_and_key(CERTIFICATE_P12, CERTIFICATE_PEM, PRIVATE_KEY_PEM, CERTIFICATE_PASSWORD)
extract_certificate_and_key(CERTIFICATE_P12, CERTIFICATE_PEM, PRIVATE_KEY_PEM, CERTIFICATE_PASSWORD) : P12 형식의 인증서를 PEM 형식으로 변환하고, 개인 키를 추출합니다. 이 과정에서 비밀번호가 필요합니다.
def convert_cer_to_pem(cer_path, pem_path):
try:
# OpenSSL을 사용하여 CER 파일을 PEM 형식으로 변환
subprocess.run(['openssl', 'x509', '-inform', 'DER', '-in', cer_path, '-out', pem_path], check=True)
logging.info(f"{cer_path} successfully converted to PEM format.")
except subprocess.CalledProcessError as e:
logging.error(f"Failed to convert {cer_path} to PEM: {e}")
raise HTTPException(status_code=500, detail=f"Failed to convert {cer_path} to PEM: {e}")
사용법:
# WWDR.cer 파일을 PEM 파일로 변환
convert_cer_to_pem(WWDR_CER, WWDR_PEM)
- convert_cer_to_pem(WWDR_CER, WWDR_PEM): WWDR(Apple WorldWide Developer Relations) 인증서를 PEM 형식으로 변환합니다. 이 파일은 Apple 패스 생성 시 필요합니다.
# 파일이 존재하는지 확인
for file_path in [LOGO_FILE, ICON_FILE, BACKGROUND_FILE, THUMBNAIL_FILE, CERTIFICATE_PEM, WWDR_PEM, PRIVATE_KEY_PEM]:
if not os.path.isfile(file_path):
logging.error(f"File not found: {file_path}")
raise HTTPException(status_code=500, detail=f"File not found: {file_path}")
# Apple Pass에 파일 추가
for file_key in [LOGO_FILE, ICON_FILE, BACKGROUND_FILE, THUMBNAIL_FILE]:
with open(file_key, "rb") as file:
apple_pass.add_file(os.path.basename(file_key), file)
for file_key in [LOGO_FILE, ICON_FILE, BACKGROUND_FILE, THUMBNAIL_FILE]n : 패스에 필요한 이미지 파일들을 추가합니다. add_file 메서드를 통해 파일을 추가합니다.
# 패스 파일 생성
try:
apple_pass.create(CERTIFICATE_PEM, PRIVATE_KEY_PEM, WWDR_PEM, CERTIFICATE_PASSWORD, OUTPUT_PASS_NAME)
except Exception as e:
logging.error(f"Failed to create pass: {e}")
raise HTTPException(status_code=500, detail=f"Failed to create pass: {e}")
return FileResponse(OUTPUT_PASS_NAME, media_type='application/vnd.apple.pkpass', filename=OUTPUT_PASS_NAME)
def convert_cer_to_pem(cer_path, pem_path):
...
- CER 형식의 인증서를 PEM 형식으로 변환하는 함수입니다. OpenSSL을 사용하여 변환합니다.
def extract_certificate_and_key(p12_path, cert_out_path, key_out_path, password):
...
- P12 파일에서 인증서와 개인 키를 추출하여 PEM 형식으로 저장합니다.
from applepassgenerator.client import ApplePassGeneratorClient
from fastapi import FastAPI,HTTPException
from fastapi.responses import FileResponse
from applepassgenerator.models import EventTicket
import subprocess
import os
import logging
from cryptography.hazmat.primitives.serialization import pkcs12
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend
app = FastAPI()
# 설정 파일
CERTIFICATE_P12 = "certificate.p12"
CERTIFICATE_PEM = "certificate.pem"
PRIVATE_KEY_PEM = "privatekey_encrypted.pem"
WWDR_PEM = "WWDR.pem"
WWDR_CER = "WWDR.cer"
CERTIFICATE_PASSWORD = "{#CERTIFICATE_PASSWORD}"
OUTPUT_PASS_NAME = "atlas_demo.pkpass"
# 로고 및 아이콘 파일 경로 설정
LOGO_FILE = "logo.png"
ICON_FILE = "icon.png"
BACKGROUND_FILE = "background.png"
THUMBNAIL_FILE = "thumbnail.png"
# 로그 설정
logging.basicConfig(level=logging.DEBUG)
def convert_cer_to_pem(cer_path, pem_path):
try:
# OpenSSL을 사용하여 CER 파일을 PEM 형식으로 변환
subprocess.run(['openssl', 'x509', '-inform', 'DER', '-in', cer_path, '-out', pem_path], check=True)
logging.info(f"{cer_path} successfully converted to PEM format.")
except subprocess.CalledProcessError as e:
logging.error(f"Failed to convert {cer_path} to PEM: {e}")
raise HTTPException(status_code=500, detail=f"Failed to convert {cer_path} to PEM: {e}")
def extract_certificate_and_key(p12_path, cert_out_path, key_out_path, password):
try:
with open(p12_path, "rb") as p12_file:
p12_data = p12_file.read()
private_key, certificate, _ = pkcs12.load_key_and_certificates(p12_data, password.encode(), default_backend())
with open(cert_out_path, "wb") as pem_file:
pem_file.write(certificate.public_bytes(serialization.Encoding.PEM))
with open(key_out_path, "wb") as key_file:
key_file.write(private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.BestAvailableEncryption(password.encode())
))
logging.info("Certificate and private key extracted successfully.")
except Exception as e:
logging.error(f"Failed to extract certificate and key: {e}")
raise HTTPException(status_code=500, detail="Failed to extract certificate and key")
@app.get("/generate_pass")
async def generate_pass():
logging.info("Start generating pass")
try:
# 패스 정보 설정
card_info = EventTicket()
card_info.add_primary_field('event', 'Atlas Book Talk', 'event')
card_info.add_secondary_field('timestamp', '11/23/2024', 'DATE')
# Apple Pass 설정
team_identifier = "{#team_identifier}"
pass_type_identifier = "{#pass_type_identifier}"
organization_name = "{#organization_name}"
applepassgenerator_client = ApplePassGeneratorClient(team_identifier, pass_type_identifier, organization_name)
apple_pass = applepassgenerator_client.get_pass(card_info)
# 인증서 및 키 파일 추출
extract_certificate_and_key(CERTIFICATE_P12, CERTIFICATE_PEM, PRIVATE_KEY_PEM, CERTIFICATE_PASSWORD)
# WWDR.cer 파일을 PEM 파일로 변환
convert_cer_to_pem(WWDR_CER, WWDR_PEM)
# 파일이 존재하는지 확인
for file_path in [LOGO_FILE, ICON_FILE, BACKGROUND_FILE, THUMBNAIL_FILE, CERTIFICATE_PEM, WWDR_PEM, PRIVATE_KEY_PEM]:
if not os.path.isfile(file_path):
logging.error(f"File not found: {file_path}")
raise HTTPException(status_code=500, detail=f"File not found: {file_path}")
# Apple Pass에 파일 추가
for file_key in [LOGO_FILE, ICON_FILE, BACKGROUND_FILE, THUMBNAIL_FILE]:
with open(file_key, "rb") as file:
apple_pass.add_file(os.path.basename(file_key), file)
# 패스 파일 생성
try:
apple_pass.create(CERTIFICATE_PEM, PRIVATE_KEY_PEM, WWDR_PEM, CERTIFICATE_PASSWORD, OUTPUT_PASS_NAME)
except Exception as e:
logging.error(f"Failed to create pass: {e}")
raise HTTPException(status_code=500, detail=f"Failed to create pass: {e}")
return FileResponse(OUTPUT_PASS_NAME, media_type='application/vnd.apple.pkpass', filename=OUTPUT_PASS_NAME)
except Exception as e:
logging.error(f"An error occurred: {e}")
raise HTTPException(status_code=500, detail=str(e))
- .p12 인증서를 폴더에 넣어주고 서버를 실행시킵니다.
- API 를 호출하면 필요한 PEM 파일을 생성하여 패스를 생성시킵니다.
미리 WWDR(AppleWWDRCAG4.cer) 파일을 넣어두었습니다
.p12 인증서 파일만 폴더에 넣고 사용이 가능하다는 장점이 있습니다.
인증서 생성관련해서 궁금하시면 이전 포스팅을 참고부탁드립니다.
2023.11.17 - [iOS 캐기/토이 프로젝트] - [애플월렛 패스] 참가증, 쿠폰, 티켓같은 패스 만들어 애플월렛에 넣어보기
작업한 폴더에서 서버를 실행합니다.
uvicorn main:app --reload
정상적으로 실행된 걸 확인합니다.
다운로드 된 pkpass를 실행시키면 패스를 확인할 수 있습니다.
- 코드는 비교적 간단했지만 인증서 관련해서 삽집을 많이했다 😉
ref.
https://github.com/PotatoArtie/Potato-iOS/tree/master/Labs/Playground/pass-generator
[애플월렛 패스] 참가증, 쿠폰, 티켓같은 패스 서버 API 호출로 생성하기- 인증서 관련 작업 정리 (0) | 2024.08.13 |
---|---|
[애플월렛 패스] 참가증, 쿠폰, 티켓같은 패스 서버 API 호출로 생성하기- 구조 작성 (0) | 2024.08.12 |
[애플월렛 패스] 참가증, 쿠폰, 티켓같은 패스 서버 API 호출로 생성하기- 개발환경 세팅 (0) | 2024.08.12 |
[애플월렛 패스] 참가증, 쿠폰, 티켓같은 패스 만들어 애플월렛에 넣어보기 (6) | 2023.11.17 |
[애플 라이다] LiDAR를 활용한 토이프로젝트 - 01 (0) | 2023.08.18 |
댓글 영역