สร้างคำขอ API แบบคลาสสิก

หากวางแผนที่จะส่งคำขอ API มาตรฐานเท่านั้น ซึ่งเหมาะสำหรับนักพัฒนาแอปส่วนใหญ่ คุณสามารถข้ามไปที่ผลการตรวจสอบความสมบูรณ์ได้ หน้านี้อธิบายวิธีส่งคำขอ API แบบคลาสสิกเพื่อขอคำตัดสินความสมบูรณ์ ซึ่งใช้ได้ใน Android 4.4 (API ระดับ 19) ขึ้นไป

ข้อควรพิจารณา

เปรียบเทียบคำขอมาตรฐานกับคำขอแบบคลาสสิก

คุณสามารถสร้างคำขอมาตรฐาน คำขอแบบคลาสสิก หรือผสมผสานกันทั้ง 2 แบบก็ได้ ทั้งนี้ขึ้นอยู่กับความต้องการด้านความปลอดภัยและการป้องกันการละเมิดของแอป คำขอมาตรฐานเหมาะสำหรับแอปและเกมทุกประเภท และสามารถใช้เพื่อตรวจสอบว่าการดำเนินการหรือการเรียกเซิร์ฟเวอร์นั้นถูกต้อง ในขณะที่มอบสิทธิ์บางส่วนในการปกป้องจากการเล่นซ้ำและการลักลอบนำข้อมูลออกให้แก่ Google Play คำขอแบบคลาสสิกมีค่าใช้จ่ายสูงกว่าและคุณมีหน้าที่รับผิดชอบในการใช้งานอย่างถูกต้องเพื่อป้องกันการลักลอบนำข้อมูลออกและการโจมตีบางประเภท คําขอแบบคลาสสิกควรมีการใช้งานน้อยกว่าคําขอมาตรฐาน เช่น ใช้แบบครั้งเดียวเป็นครั้งคราวเพื่อตรวจสอบว่าการดำเนินการที่มีคุณค่าสูงหรือมีความละเอียดอ่อนนั้นเกิดขึ้นจริง

ตารางต่อไปนี้จะไฮไลต์ความแตกต่างที่สําคัญของคําขอทั้ง 2 ประเภท

คำขอ API มาตรฐาน คําขอ API แบบคลาสสิก
สิ่งที่ต้องมีก่อน
เวอร์ชัน Android SDK ขั้นต่ำที่ต้องการ Android 5.0 (API ระดับ 21) ขึ้นไป Android 4.4 (API ระดับ 19) ขึ้นไป
ข้อกำหนดของ Google Play Google Play Store และบริการ Google Play Google Play Store และบริการ Google Play
รายละเอียดการผสานรวม
ต้องอุ่นเครื่อง API ✔️ (2-3 วินาที)
เวลาในการตอบสนองของคำขอตามปกติ 2-3 ร้อยมิลลิวินาที 2-3 วินาที
ความถี่ของคำขอที่เป็นไปได้ บ่อย (ตรวจสอบการดำเนินการหรือคำขอตามคําขอ) ไม่บ่อย (การตรวจสอบแบบครั้งเดียวสําหรับการดําเนินการที่มีมูลค่าสูงสุดหรือคําขอที่มีความละเอียดอ่อนที่สุด)
หมดเวลา อุ่นเครื่องส่วนใหญ่ใช้เวลาไม่ถึง 10 วินาที แต่เกี่ยวข้องกับการเรียกเซิร์ฟเวอร์ จึงขอแนะนำให้ใช้การหมดเวลานาน (เช่น 1 นาที) คำขอคำตัดสินเกิดขึ้นฝั่งไคลเอ็นต์ คําขอส่วนใหญ่ใช้เวลาไม่ถึง 10 วินาที แต่เกี่ยวข้องกับการเรียกเซิร์ฟเวอร์ จึงขอแนะนําให้ใช้การหมดเวลานาน (เช่น 1 นาที)
โทเค็นการตัดสินความสมบูรณ์
มีรายละเอียดอุปกรณ์ แอป และบัญชี ✔️ ✔️
การแคชโทเค็น การแคชในอุปกรณ์ที่ได้รับการปกป้องโดย Google Play ไม่แนะนำ
ถอดรหัสและยืนยันโทเค็นผ่านเซิร์ฟเวอร์ Google Play ✔️ ✔️
เวลาในการตอบสนองของคำขอการถอดรหัสจากเซิร์ฟเวอร์ต่อเซิร์ฟเวอร์โดยทั่วไป 10 มิลลิวินาทีพร้อมความพร้อมใช้งาน 99.99% 10 มิลลิวินาทีพร้อมความพร้อมใช้งาน 99.999%
ถอดรหัสและยืนยันโทเค็นในเครื่องในสภาพแวดล้อมเซิร์ฟเวอร์ที่ปลอดภัย ✔️
ถอดรหัสและยืนยันโทเค็นฝั่งไคลเอ็นต์
ความใหม่ของการตัดสินความสมบูรณ์ Google Play แคชและรีเฟรชข้อมูลบางอย่างโดยอัตโนมัติ ระบบจะคํานวณผลการตรวจสอบทั้งหมดอีกครั้งในคําขอแต่ละรายการ
ขีดจํากัด
คำขอต่อแอปต่อวัน 10,000 รายการโดยค่าเริ่มต้น (ขอเพิ่มได้) 10,000 รายการโดยค่าเริ่มต้น (ขอเพิ่มได้)
คำขอต่ออินสแตนซ์แอปต่อนาที การทำ Warm Up: 5 รายการต่อนาที
โทเค็นความสมบูรณ์: ไม่มีขีดจำกัดสาธารณะ*
โทเค็นความสมบูรณ์: 5 รายการต่อนาที
การป้องกัน
ป้องกันการแทรกแซงและการโจมตีที่คล้ายกัน ใช้ช่อง requestHash ใช้ช่อง nonce กับการเชื่อมโยงเนื้อหาตามข้อมูลคำขอ
ป้องกันสแปมซ้ำและการโจมตีที่คล้ายกัน การลดความเสี่ยงโดยอัตโนมัติของ Google Play ใช้ช่อง nonce ที่มีตรรกะฝั่งเซิร์ฟเวอร์

* คำขอทั้งหมด รวมถึงคำขอที่ไม่มีขีดจำกัดสาธารณะ จะขึ้นอยู่กับขีดจำกัดการป้องกันแบบไม่สาธารณะที่มีค่าสูง

ส่งคำขอแบบคลาสสิกไม่บ่อย

การสร้างโทเค็นความสมบูรณ์ต้องใช้เวลา อินเทอร์เน็ต และแบตเตอรี่ และแต่ละแอปจะมีจำนวนคำขอแบบคลาสสิกสูงสุดที่ส่งได้ต่อวัน ดังนั้น คุณควรส่งคำขอแบบคลาสสิกเพื่อตรวจสอบว่าค่าสูงสุดหรือการดำเนินการที่มีความละเอียดอ่อนที่สุดเป็นการดำเนินการจริงก็ต่อเมื่อคุณต้องการการรับประกันเพิ่มเติมสำหรับคำขอมาตรฐาน คุณไม่ควรส่งคําขอแบบคลาสสิกสําหรับการดําเนินการที่มีมูลค่าต่ำหรือความถี่สูง อย่าส่งคำขอแบบคลาสสิกทุกครั้งที่แอปแสดงอยู่เบื้องหน้าหรือทุกๆ 2-3 นาทีที่ทำงานอยู่เบื้องหลัง และหลีกเลี่ยงการเรียกใช้จากอุปกรณ์จํานวนมากพร้อมกัน ระบบอาจจำกัดแอปที่เรียกใช้คำขอแบบคลาสสิกมากเกินไปเพื่อปกป้องผู้ใช้จากการใช้งานที่ไม่ถูกต้อง

หลีกเลี่ยงการแคชผลการตรวจสอบ

การแคชผลการตรวจสอบจะเพิ่มความเสี่ยงของการโจมตี เช่น การนำข้อมูลออกและการเล่นซ้ำ ซึ่งมีการนําผลการตรวจสอบที่ถูกต้องมาใช้ซ้ำจากสภาพแวดล้อมที่ไม่น่าเชื่อถือ หากคุณกำลังพิจารณาส่งคำขอแบบคลาสสิกแล้วแคชไว้ใช้ภายหลัง เราขอแนะนำให้ใช้คำขอมาตรฐานแบบออนดีมานด์แทน คำขอมาตรฐานจะเกี่ยวข้องกับการแคชบางส่วนในอุปกรณ์ แต่ Google Play ใช้เทคนิคการป้องกันเพิ่มเติมเพื่อลดความเสี่ยงของการโจมตีด้วยการเล่นซ้ำและการลักลอบนำข้อมูลออก

ใช้ช่อง Nonce เพื่อปกป้องคําขอแบบคลาสสิก

Play Integrity API มีช่องชื่อ nonce ซึ่งสามารถใช้เพื่อปกป้องแอปเพิ่มเติมจากการโจมตีบางประเภท เช่น การโจมตีด้วยการบันทึกซ้ำและการดัดแปลง Play Integrity API จะแสดงผลค่าที่คุณตั้งค่าในช่องนี้ภายในการตอบกลับความสมบูรณ์ที่ลงนาม โปรดทําตามคําแนะนําเกี่ยวกับวิธีสร้าง 'Nonces' อย่างละเอียดเพื่อปกป้องแอปจากการโจมตี

ลองส่งคำขอแบบคลาสสิกอีกครั้งด้วย Exponential Backoff

สภาพแวดล้อม เช่น การเชื่อมต่ออินเทอร์เน็ตที่ไม่เสถียรหรืออุปกรณ์ที่มีการใช้งานมากเกินไป อาจทำให้การตรวจสอบความสมบูรณ์ของอุปกรณ์ไม่สำเร็จ ซึ่งอาจส่งผลให้ระบบไม่สร้างป้ายกำกับสำหรับอุปกรณ์ที่เชื่อถือได้ หากต้องการบรรเทาสถานการณ์เหล่านี้ ให้ใส่ตัวเลือก "ลองใหม่" ที่มี Exponential Backoff

ภาพรวม

แผนภาพลำดับที่แสดงการออกแบบระดับสูงของ Play Integrity API

เมื่อผู้ใช้ดำเนินการที่มีคุณค่าสูงในแอปซึ่งคุณต้องการปกป้องด้วยการตรวจสอบความสมบูรณ์ ให้ทำตามขั้นตอนต่อไปนี้

  1. แบ็กเอนด์ฝั่งเซิร์ฟเวอร์ของแอปจะสร้างและส่งค่าที่ไม่ซ้ำกันไปยังตรรกะฝั่งไคลเอ็นต์ ขั้นตอนที่เหลือจะเรียกตรรกะนี้ว่า "แอป"
  2. แอปของคุณสร้าง nonce จากมูลค่าที่ไม่ซ้ำกันและเนื้อหาของการกระทําที่มีคุณค่าสูง จากนั้นจะเรียกใช้ Play Integrity API โดยส่ง nonce
  3. แอปของคุณจะได้รับผลการตัดสินที่เซ็นชื่อและเข้ารหัสจาก Play Integrity API
  4. แอปจะส่งผลการตัดสินที่เซ็นชื่อและเข้ารหัสไปยังแบ็กเอนด์ของแอป
  5. แบ็กเอนด์ของแอปจะส่งผลการตัดสินไปยังเซิร์ฟเวอร์ Google Play เซิร์ฟเวอร์ Google Play จะถอดรหัสและยืนยันผลการตัดสิน จากนั้นจะแสดงผลลัพธ์ไปยังแบ็กเอนด์ของแอป
  6. แบ็กเอนด์ของแอปจะเป็นผู้ตัดสินใจว่าจะดำเนินการต่ออย่างไร โดยอิงตามสัญญาณที่อยู่ในเพย์โหลดโทเค็น
  7. แบ็กเอนด์ของแอปจะส่งผลลัพธ์การตัดสินไปยังแอป

สร้าง Nonce

เมื่อปกป้องการดำเนินการในแอปด้วย Play Integrity API คุณจะใช้ประโยชน์จากช่อง nonce เพื่อลดการโจมตีบางประเภท เช่น การโจมตีด้วยการเปลี่ยนเส้นทางข้อมูล (PITM) และการโจมตีด้วยการบันทึกและเล่นซ้ำ Play Integrity API จะแสดงค่าที่คุณตั้งค่าในช่องนี้ภายในการตอบกลับความสมบูรณ์ที่เซ็นชื่อ

ค่าที่ตั้งไว้ในช่อง nonce ต้องมีรูปแบบที่ถูกต้อง ดังนี้

  • String
  • ปลอดภัยสำหรับ URL
  • เข้ารหัสเป็น Base64 และไม่มีการตัดขึ้นบรรทัดใหม่
  • อักขระอย่างน้อย 16 ตัว
  • มีอักขระได้สูงสุด 500 ตัว

ต่อไปนี้เป็นวิธีทั่วไปในการใช้ช่อง nonce ใน Play Integrity API หากต้องการรับการปกป้องที่แข็งแกร่งที่สุดจาก nonce ให้ใช้วิธีการต่อไปนี้ร่วมกัน

ใส่แฮชคำขอเพื่อป้องกันการแทรกแซง

คุณสามารถใช้พารามิเตอร์ nonce ในคําขอ API แบบคลาสสิกได้เช่นเดียวกับพารามิเตอร์ requestHash ในคําขอ API มาตรฐานเพื่อปกป้องเนื้อหาของคําขอจากการดัดแปลง

สิ่งที่จะเกิดขึ้นเมื่อคุณขอการตัดสินความสมบูรณ์

  1. คํานวณข้อมูลสรุปของพารามิเตอร์คําขอที่สําคัญทั้งหมด (เช่น SHA256 ของการจัดรูปแบบคําขอแบบเสถียร) จากการดำเนินการของผู้ใช้หรือคําขอเซิร์ฟเวอร์ที่เกิดขึ้น
  2. ใช้ setNonce เพื่อตั้งค่าช่อง nonce เป็นค่าของข้อมูลสรุปที่คำนวณแล้ว

สิ่งที่จะเกิดขึ้นเมื่อคุณได้รับการตัดสินความสมบูรณ์

  1. ถอดรหัสและยืนยันโทเค็นความสมบูรณ์ รวมถึงรับข้อมูลสรุปจากช่อง nonce
  2. คำนวณข้อมูลสรุปของคำขอในลักษณะเดียวกับในแอป (เช่น SHA256 ของการจัดรูปแบบคำขอแบบเสถียร)
  3. เปรียบเทียบข้อมูลสรุปฝั่งแอปและฝั่งเซิร์ฟเวอร์ หากไม่ตรงกัน แสดงว่าคำขอไม่น่าเชื่อถือ

ใส่ค่าที่ไม่ซ้ำกันเพื่อป้องกันการโจมตีด้วยการเล่นซ้ำ

คุณสามารถใช้ช่อง nonce เพื่อระบุข้อความแต่ละรายการที่ไม่ซ้ำกันเพื่อป้องกันไม่ให้ผู้ใช้ที่เป็นอันตรายนำการตอบกลับก่อนหน้าจาก Play Integrity API มาใช้ซ้ำ

สิ่งที่จะเกิดขึ้นเมื่อคุณขอการตัดสินความสมบูรณ์

  1. รับค่าที่ไม่ซ้ำกันทั่วโลกในลักษณะที่ผู้ใช้ที่เป็นอันตรายไม่สามารถคาดเดาได้ ตัวอย่างเช่น ตัวเลขสุ่มที่ปลอดภัยตามวิทยาการเข้ารหัสซึ่งสร้างขึ้นฝั่งเซิร์ฟเวอร์อาจเป็นค่าดังกล่าว หรืออาจเป็นรหัสที่มีอยู่ก่อน เช่น เซสชันหรือรหัสธุรกรรม วิธีที่ง่ายกว่าแต่มีความปลอดภัยน้อยกว่าคือการสร้างตัวเลขแบบสุ่มในอุปกรณ์ เราขอแนะนําให้สร้างค่า 128 บิตขึ้นไป
  2. เรียกใช้ setNonce() เพื่อตั้งค่าช่อง nonce เป็นค่าที่ไม่ซ้ำกันจากขั้นตอนที่ 1

สิ่งที่จะเกิดขึ้นเมื่อคุณได้รับการตัดสินความสมบูรณ์

  1. ถอดรหัสและยืนยันโทเค็นความสมบูรณ์ รวมถึงรับค่าที่ไม่ซ้ำกันจากช่อง nonce
  2. หากค่าจากขั้นตอนที่ 1 สร้างขึ้นบนเซิร์ฟเวอร์ ให้ตรวจสอบว่าค่าที่ไม่ซ้ำกันที่ได้รับเป็นหนึ่งในค่าที่สร้างขึ้น และกำลังใช้งานเป็นครั้งแรก (เซิร์ฟเวอร์จะต้องเก็บบันทึกค่าที่สร้างขึ้นไว้เป็นระยะเวลาที่เหมาะสม) หากมีการใช้ค่าที่ไม่ซ้ำกันที่ได้รับแล้วหรือไม่ปรากฏในระเบียน ให้ปฏิเสธคำขอ
  3. หรือหากมีการสร้างค่าที่ไม่ซ้ำกันในอุปกรณ์ ให้ตรวจสอบว่าระบบใช้ค่าที่ได้รับเป็นครั้งแรก (เซิร์ฟเวอร์ของคุณต้องเก็บบันทึกค่าที่เคยเห็นแล้วไว้เป็นระยะเวลาที่เหมาะสม) หากมีการใช้ค่าที่ไม่ซ้ำกันที่ได้รับแล้ว ให้ปฏิเสธคำขอ

รวมการป้องกันทั้งการแทรกแซงและการโจมตีด้วยการเล่นซ้ำ (แนะนำ)

คุณสามารถใช้ช่อง nonce เพื่อป้องกันการแทรกแซงและการโจมตีด้วยการเล่นซ้ำพร้อมกันได้ โดยสร้างค่าที่ไม่ซ้ำกันตามที่อธิบายไว้ข้างต้น แล้วใส่ค่าดังกล่าวเป็นส่วนหนึ่งของคำขอ จากนั้นคํานวณแฮชคําขอ โดยอย่าลืมใส่ค่าที่ไม่ซ้ำกันเป็นส่วนหนึ่งของแฮช การใช้งานที่รวมทั้ง 2 แนวทางมีดังนี้

สิ่งที่จะเกิดขึ้นเมื่อคุณขอการตัดสินความสมบูรณ์

  1. ผู้ใช้เริ่มการดําเนินการที่มีมูลค่าสูง
  2. รับค่าที่ไม่ซ้ำกันสําหรับการดําเนินการนี้ตามที่อธิบายไว้ในส่วนรวมค่าที่ไม่ซ้ำกันเพื่อป้องกันการโจมตีด้วยการเล่นซ้ำ
  3. เตรียมข้อความที่ต้องการปกป้อง ใส่ค่าที่ไม่ซ้ำกันจากขั้นตอนที่ 2 ในข้อความ
  4. แอปจะคํานวณข้อมูลสรุปของข้อความที่ต้องการปกป้อง ตามที่อธิบายไว้ในส่วนใส่แฮชคําขอเพื่อป้องกันการดัดแปลง เนื่องจากข้อความมีค่าที่ไม่ซ้ำกัน ค่าที่ไม่ซ้ำกันจึงเป็นส่วนหนึ่งของแฮช
  5. ใช้ setNonce() เพื่อตั้งค่าช่อง nonce เป็นข้อมูลสรุปที่คำนวณจากขั้นตอนก่อนหน้า

สิ่งที่จะเกิดขึ้นเมื่อคุณได้รับการตัดสินความสมบูรณ์

  1. รับค่าที่ไม่ซ้ำกันจากคําขอ
  2. ถอดรหัสและยืนยันโทเค็นความสมบูรณ์ รวมถึงรับข้อมูลสรุปจากช่อง nonce
  3. ตามที่อธิบายไว้ในส่วนใส่แฮชคำขอเพื่อป้องกันการแทรกแซง ให้คํานวณข้อมูลสรุปอีกครั้งฝั่งเซิร์ฟเวอร์ และตรวจสอบว่าข้อมูลสรุปตรงกับข้อมูลสรุปที่ได้รับจากโทเค็นความสมบูรณ์
  4. ตรวจสอบความถูกต้องของค่าที่ไม่ซ้ำกันตามที่อธิบายไว้ในส่วนรวมค่าที่ไม่ซ้ำกันเพื่อป้องกันการโจมตีด้วยการเล่นซ้ำ

แผนภาพลำดับต่อไปนี้แสดงขั้นตอนเหล่านี้ด้วยnonceฝั่งเซิร์ฟเวอร์

แผนภาพลำดับที่แสดงวิธีป้องกันทั้งการแทรกแซงและการโจมตีแบบเล่นซ้ำ

ขอการตัดสินความสมบูรณ์

หลังจากสร้าง nonce แล้ว คุณจะขอผลการตัดสินความสมบูรณ์จาก Google Play ได้ โดยทำตามขั้นตอนต่อไปนี้

  1. สร้าง IntegrityManager ตามที่แสดงในตัวอย่างต่อไปนี้
  2. สร้าง IntegrityTokenRequest โดยระบุ nonce ผ่านเมธอด setNonce() ในบิลเดอร์ที่เกี่ยวข้อง แอปที่เผยแพร่ภายนอก Google Play และ SDK โดยเฉพาะจะต้องระบุหมายเลขโปรเจ็กต์ Google Cloud ผ่านเมธอด setCloudProjectNumber() ด้วย แอปใน Google Play จะลิงก์กับโปรเจ็กต์ Cloud ใน Play Console และไม่จำเป็นต้องตั้งค่าหมายเลขโปรเจ็กต์ Cloud ในคำขอ
  3. ใช้ตัวจัดการเพื่อโทรหา requestIntegrityToken() พร้อมระบุ IntegrityTokenRequest

Kotlin

// Receive the nonce from the secure server.
val nonce: String = ...

// Create an instance of a manager.
val integrityManager =
    IntegrityManagerFactory.create(applicationContext)

// Request the integrity token by providing a nonce.
val integrityTokenResponse: Task<IntegrityTokenResponse> =
    integrityManager.requestIntegrityToken(
        IntegrityTokenRequest.builder()
             .setNonce(nonce)
             .build())

Java

import com.google.android.gms.tasks.Task; ...

// Receive the nonce from the secure server.
String nonce = ...

// Create an instance of a manager.
IntegrityManager integrityManager =
    IntegrityManagerFactory.create(getApplicationContext());

// Request the integrity token by providing a nonce.
Task<IntegrityTokenResponse> integrityTokenResponse =
    integrityManager
        .requestIntegrityToken(
            IntegrityTokenRequest.builder().setNonce(nonce).build());

Unity

IEnumerator RequestIntegrityTokenCoroutine() {
    // Receive the nonce from the secure server.
    var nonce = ...

    // Create an instance of a manager.
    var integrityManager = new IntegrityManager();

    // Request the integrity token by providing a nonce.
    var tokenRequest = new IntegrityTokenRequest(nonce);
    var requestIntegrityTokenOperation =
        integrityManager.RequestIntegrityToken(tokenRequest);

    // Wait for PlayAsyncOperation to complete.
    yield return requestIntegrityTokenOperation;

    // Check the resulting error code.
    if (requestIntegrityTokenOperation.Error != IntegrityErrorCode.NoError)
    {
        AppendStatusLog("IntegrityAsyncOperation failed with error: " +
                requestIntegrityTokenOperation.Error);
        yield break;
    }

    // Get the response.
    var tokenResponse = requestIntegrityTokenOperation.GetResult();
}

Unreal Engine

// .h
void MyClass::OnRequestIntegrityTokenCompleted(
  EIntegrityErrorCode ErrorCode,
  UIntegrityTokenResponse* Response)
{
  // Check the resulting error code.
  if (ErrorCode == EIntegrityErrorCode::Integrity_NO_ERROR)
  {
    // Get the token.
    FString Token = Response->Token;
  }
}

// .cpp
void MyClass::RequestIntegrityToken()
{
  // Receive the nonce from the secure server.
  FString Nonce = ...

  // Create the Integrity Token Request.
  FIntegrityTokenRequest Request = { Nonce };

  // Create a delegate to bind the callback function.
  FIntegrityOperationCompletedDelegate Delegate;

  // Bind the completion handler (OnRequestIntegrityTokenCompleted) to the delegate.
  Delegate.BindDynamic(this, &MyClass::OnRequestIntegrityTokenCompleted);

  // Initiate the integrity token request, passing the delegate to handle the result.
  GetGameInstance()
    ->GetSubsystem<UIntegrityManager>()
    ->RequestIntegrityToken(Request, Delegate);
}

เนทีฟ

/// Create an IntegrityTokenRequest opaque object.
const char* nonce = RequestNonceFromServer();
IntegrityTokenRequest* request;
IntegrityTokenRequest_create(&request);
IntegrityTokenRequest_setNonce(request, nonce);

/// Prepare an IntegrityTokenResponse opaque type pointer and call
/// IntegerityManager_requestIntegrityToken().
IntegrityTokenResponse* response;
IntegrityErrorCode error_code =
        IntegrityManager_requestIntegrityToken(request, &response);

/// ...
/// Proceed to polling iff error_code == INTEGRITY_NO_ERROR
if (error_code != INTEGRITY_NO_ERROR)
{
    /// Remember to call the *_destroy() functions.
    return;
}
/// ...
/// Use polling to wait for the async operation to complete.
/// Note, the polling shouldn't block the thread where the IntegrityManager
/// is running.

IntegrityResponseStatus response_status;

/// Check for error codes.
IntegrityErrorCode error_code =
        IntegrityTokenResponse_getStatus(response, &response_status);
if (error_code == INTEGRITY_NO_ERROR
    && response_status == INTEGRITY_RESPONSE_COMPLETED)
{
    const char* integrity_token = IntegrityTokenResponse_getToken(response);
    SendTokenToServer(integrity_token);
}
/// ...
/// Remember to free up resources.
IntegrityTokenRequest_destroy(request);
IntegrityTokenResponse_destroy(response);
IntegrityManager_destroy();

ถอดรหัสและยืนยันการตัดสินความสมบูรณ์

เมื่อคุณขอการตัดสินความสมบูรณ์ Play Integrity API จะแสดงโทเค็นการตอบกลับที่ลงนาม nonce ที่คุณระบุไว้ในคําขอจะเป็นส่วนหนึ่งของโทเค็นการตอบกลับ

รูปแบบโทเค็น

โทเค็นนี้เป็น JSON Web Token (JWT) ที่ฝังอยู่ ซึ่งเป็น JSON Web Encryption (JWE) ของ JSON Web Signature (JWS) คอมโพเนนต์ JWE และ JWS จะแสดงโดยใช้การแปลงเป็นรูปแบบข้อความแบบกะทัดรัด

อัลกอริทึมการเข้ารหัส / การลงนามได้รับการรองรับอย่างครอบคลุมในการใช้งาน JWT ต่างๆ ดังนี้

  • JWE ใช้ A256KW สำหรับ alg และ A256GCM สำหรับ enc

  • JWS ใช้ ES256

ถอดรหัสและยืนยันในเซิร์ฟเวอร์ของ Google (แนะนำ)

Play Integrity API ช่วยให้คุณถอดรหัสและยืนยันผลการตัดสินความสมบูรณ์ในเซิร์ฟเวอร์ของ Google ซึ่งจะช่วยเพิ่มความปลอดภัยของแอป โดยทำตามขั้นตอนต่อไปนี้

  1. สร้างบัญชีบริการภายในโปรเจ็กต์ Google Cloud ที่ลิงก์กับแอป
  2. ในเซิร์ฟเวอร์ของแอป ให้ดึงข้อมูลโทเค็นการเข้าถึงจากข้อมูลเข้าสู่ระบบบัญชีบริการโดยใช้ขอบเขต playintegrity แล้วส่งคําขอต่อไปนี้

    playintegrity.googleapis.com/v1/PACKAGE_NAME:decodeIntegrityToken -d \
    '{ "integrity_token": "INTEGRITY_TOKEN" }'
  3. อ่านการตอบกลับ JSON

ถอดรหัสและยืนยันในเครื่อง

เพื่อปกป้องความปลอดภัยของแอป

หากเลือกจัดการและดาวน์โหลดคีย์การเข้ารหัสการตอบกลับ คุณจะถอดรหัสและยืนยันโทเค็นที่ส่งคืนภายในสภาพแวดล้อมเซิร์ฟเวอร์ที่ปลอดภัยของคุณเองได้ คุณสามารถรับโทเค็นที่แสดงผลได้โดยใช้IntegrityTokenResponse#token()วิธีนี้

ตัวอย่างต่อไปนี้แสดงวิธีถอดรหัสคีย์ AES และคีย์ EC สาธารณะที่เข้ารหัส DER สำหรับการยืนยันลายเซ็นจาก Play Console ไปยังคีย์เฉพาะภาษา (ในกรณีนี้คือภาษาโปรแกรม Java) ในแบ็กเอนด์ของแอป โปรดทราบว่าคีย์ได้รับการเข้ารหัส Base64 โดยใช้ Flag เริ่มต้น

Kotlin

// base64OfEncodedDecryptionKey is provided through Play Console.
var decryptionKeyBytes: ByteArray =
    Base64.decode(base64OfEncodedDecryptionKey, Base64.DEFAULT)

// Deserialized encryption (symmetric) key.
var decryptionKey: SecretKey = SecretKeySpec(
    decryptionKeyBytes,
    /* offset= */ 0,
    AES_KEY_SIZE_BYTES,
    AES_KEY_TYPE
)

// base64OfEncodedVerificationKey is provided through Play Console.
var encodedVerificationKey: ByteArray =
    Base64.decode(base64OfEncodedVerificationKey, Base64.DEFAULT)

// Deserialized verification (public) key.
var verificationKey: PublicKey = KeyFactory.getInstance(EC_KEY_TYPE)
    .generatePublic(X509EncodedKeySpec(encodedVerificationKey))

Java

// base64OfEncodedDecryptionKey is provided through Play Console.
byte[] decryptionKeyBytes =
    Base64.decode(base64OfEncodedDecryptionKey, Base64.DEFAULT);

// Deserialized encryption (symmetric) key.
SecretKey decryptionKey =
    new SecretKeySpec(
        decryptionKeyBytes,
        /* offset= */ 0,
        AES_KEY_SIZE_BYTES,
        AES_KEY_TYPE);

// base64OfEncodedVerificationKey is provided through Play Console.
byte[] encodedVerificationKey =
    Base64.decode(base64OfEncodedVerificationKey, Base64.DEFAULT);
// Deserialized verification (public) key.
PublicKey verificationKey =
    KeyFactory.getInstance(EC_KEY_TYPE)
        .generatePublic(new X509EncodedKeySpec(encodedVerificationKey));

จากนั้นใช้คีย์เหล่านี้เพื่อถอดรหัสโทเค็นความสมบูรณ์ (ส่วน JWE) ก่อน แล้วจึงตรวจสอบและดึงข้อมูลส่วน JWS ที่ฝังอยู่

Kotlin

val jwe: JsonWebEncryption =
    JsonWebStructure.fromCompactSerialization(integrityToken) as JsonWebEncryption
jwe.setKey(decryptionKey)

// This also decrypts the JWE token.
val compactJws: String = jwe.getPayload()

val jws: JsonWebSignature =
    JsonWebStructure.fromCompactSerialization(compactJws) as JsonWebSignature
jws.setKey(verificationKey)

// This also verifies the signature.
val payload: String = jws.getPayload()

Java

JsonWebEncryption jwe =
    (JsonWebEncryption)JsonWebStructure
        .fromCompactSerialization(integrityToken);
jwe.setKey(decryptionKey);

// This also decrypts the JWE token.
String compactJws = jwe.getPayload();

JsonWebSignature jws =
    (JsonWebSignature) JsonWebStructure.fromCompactSerialization(compactJws);
jws.setKey(verificationKey);

// This also verifies the signature.
String payload = jws.getPayload();

เพย์โหลดที่ได้จะเป็นโทเค็นข้อความธรรมดาที่มีผลการตรวจสอบความสมบูรณ์