# ๐Ÿ” ๅฎ‰ๅ…จ่ฎพๅค‡ Demo โ€” Android + Spring Boot > **ๅœบๆ™ฏ**๏ผš้ซ˜ๆƒ้™่ฎพๅค‡็ซฏ๏ผˆๆ— ็™ปๅฝ•๏ผ‰+ ็”จๆˆท็ซฏ๏ผˆๆœ‰็™ปๅฝ•ไฝ“็ณป๏ผ‰+ SN ็ป‘ๅฎš + ็งๅฏ†็…ง็‰‡ๅŠ ๅฏ† > **็›ฎๆ ‡**๏ผš่ฎพๅค‡็ซฏ TEE ๅฏ†้’ฅไธๅฏๅฏผๅ‡บใ€ๆœๅŠก็ซฏ้›ถ็Ÿฅ่ฏ†ใ€ๆขๅคๅ‡บๅŽ‚ๅŽๅฏๅฎ‰ๅ…จๆขๅค --- ## ๐Ÿ“ ๆžถๆž„ๆ€ป่งˆ ``` โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” HTTPS โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ Android ่ฎพๅค‡็ซฏ โ”‚ โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ โ”‚ Spring Boot ๅŽ็ซฏ โ”‚ โ”‚ (ๆ— ็™ปๅฝ•/้ซ˜ๆƒ้™) โ”‚ โ”‚ (็”จๆˆทๆœ‰็™ปๅฝ•ไฝ“็ณป) โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ โ”‚ โ‘  TEE ๅฏ†้’ฅๅฏน๏ผˆKeystore๏ผ‰ โ”‚ โ‘  ็”จๆˆทไธปๅฏ†้’ฅ๏ผˆUK๏ผ‰ โ”‚ โ‘ก AES-GCM ไฟกๅฐๅŠ ๅฏ†็…ง็‰‡ โ”‚ โ‘ก DEK ไฟกๅฐๅŠ ๅฏ†ๅญ˜ๅ‚จ โ”‚ โ‘ข ECDSA ็ญพๅๅ…ƒๆ•ฐๆฎ โ”‚ โ‘ข SN ็ป‘ๅฎš + ็Ÿญไฟก้ชŒ่ฏ โ”‚ โ‘ฃ ๆขๅคๅ‡บๅŽ‚ โ†’ ๆ–ฐๅฏ†้’ฅๅฏน โ”‚ โ‘ฃ Recovery Token ไธ‹ๅ‘ โ”‚ โ‘ค UK ่งฃ DEK โ†’ ่ฎพๅค‡ๅ…ฌ้’ฅๅŠ ๅฏ†ไธ‹ๅ‘ ``` --- ## ๐Ÿ”‘ ๅฏ†้’ฅๅˆ†ๅฑ‚ๆจกๅž‹ ``` โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ User Key (UK) โ€” ็”จๆˆทไธปๅฏ†้’ฅ โ”‚ โ”‚ โ”œโ”€โ”€ ็”Ÿๆˆ๏ผš็”จๆˆทๆณจๅ†Œๆ—ถๅˆ›ๅปบ๏ผˆAES-256๏ผ‰ โ”‚ โ”‚ โ”œโ”€โ”€ ๅญ˜ๅ‚จ๏ผšๆœๅŠก็ซฏๅŠ ๅฏ†ๅญ˜ๅ‚จ๏ผˆ็”Ÿไบง็Žฏๅขƒ็”จ KMS ๆ‰˜็ฎก๏ผ‰ โ”‚ โ”‚ โ””โ”€โ”€ ็”จ้€”๏ผšๅŠ ๅฏ†ๆ‰€ๆœ‰ DEK โ”‚ โ”‚ โ”‚ โ”‚ Data Encryption Key (DEK) โ€” ๆฏ็…ง็‰‡ไธ€ไธช โ”‚ โ”‚ โ”œโ”€โ”€ ็”Ÿๆˆ๏ผš่ฎพๅค‡็ซฏ SecureRandom๏ผˆๆฏๆฌก้šๆœบ๏ผ‰ โ”‚ โ”‚ โ”œโ”€โ”€ ๅŠ ๅฏ†๏ผšUK ๅŠ ๅฏ†ๅŽๅญ˜ๆœๅŠก็ซฏ โ”‚ โ”‚ โ””โ”€โ”€ ็”จ้€”๏ผšAES-256-GCM ๅŠ ๅฏ†็…ง็‰‡ โ”‚ โ”‚ โ”‚ โ”‚ Device Key Pair โ€” TEE ็กฌไปถๅฏ†้’ฅ โ”‚ โ”‚ โ”œโ”€โ”€ ็”Ÿๆˆ๏ผšAndroid Keystore๏ผˆStrongBox ไผ˜ๅ…ˆ๏ผ‰ โ”‚ โ”‚ โ”œโ”€โ”€ ็ง้’ฅ๏ผšๆฐธไธๅฏผๅ‡บ๏ผŒไป…็ญพๅ/่งฃๅฏ† โ”‚ โ”‚ โ”œโ”€โ”€ ๅ…ฌ้’ฅ๏ผšไธŠไผ ๆœๅŠก็ซฏ๏ผŒ็”จไบŽๅŠ ๅฏ†ไธ‹ๅ‘ โ”‚ โ”‚ โ””โ”€โ”€ ้”€ๆฏ๏ผšๆขๅคๅ‡บๅŽ‚ๆ—ถ่‡ชๅŠจๆธ…้™ค โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ``` --- ## ๐Ÿ”„ ๅฎŒๆ•ดๆต็จ‹ ### Phase 1๏ผš่ฎพๅค‡ๆณจๅ†Œ + ็”จๆˆท็ป‘ๅฎš ``` ่ฎพๅค‡็ซฏ ๅŽ็ซฏ ็”จๆˆท็ซฏ โ”‚ โ”‚ โ”‚ โ”‚โ”€โ”€ ็”Ÿๆˆ TEE ๅฏ†้’ฅๅฏน โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚ โ”‚ โ”‚โ”€โ”€ POST /api/device/register โ”‚ โ”‚ โ”‚ {sn, publicKey} โ”€โ”€โ–บโ”‚ โ”‚ โ”‚ โ”‚โ”€โ”€ ๅญ˜ๅ‚จ device(sn, pubKey) โ”‚ โ”‚โ—„โ”€โ”€ {deviceId} โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚โ—„โ”€โ”€ POST /api/device/bind โ”‚ โ”‚ โ”‚ {userId, sn} โ”‚ โ”‚ โ”‚โ”€โ”€ ็ป‘ๅฎš userId โ†” deviceId โ”‚ โ”‚ โ”‚โ—„โ”€โ”€ {ok} โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚ ``` ### Phase 2๏ผšๆ‹็…ง โ†’ ไฟกๅฐๅŠ ๅฏ† โ†’ ไธŠไผ  ``` ่ฎพๅค‡็ซฏ ๅŽ็ซฏ โ”‚ โ”‚ โ”‚โ”€โ”€ SecureRandom โ†’ DEK (256bit)โ”‚ โ”‚โ”€โ”€ AES-GCM(photo, DEK) โ†’ ct โ”‚ โ”‚โ”€โ”€ Sign(metadata, PrivKey) โ”‚ โ”‚ โ”‚ โ”‚โ”€โ”€ POST /api/photo/upload โ”‚ โ”‚ {sn, ct, iv, dek, sig} โ”€โ”€โ–บโ”‚ โ”‚ โ”‚โ”€โ”€ ้ชŒ็ญพ๏ผˆPubKey๏ผ‰ โ”‚ โ”‚โ”€โ”€ wrapDEK(dek, UK) โ†’ encDEK โ”‚ โ”‚โ”€โ”€ ๅญ˜ๅ‚จ {ct, iv, encDEK, sig} โ”‚โ—„โ”€โ”€ {photoId} โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚ ``` ### Phase 3๏ผšๆขๅคๅ‡บๅŽ‚ โ†’ ็Ÿญไฟก้ชŒ่ฏ โ†’ ๆขๅค ``` ่ฎพๅค‡็ซฏ(ๆ–ฐ) ๅŽ็ซฏ ็”จๆˆท็ซฏ โ”‚ โ”‚ โ”‚ โ”‚โ”€โ”€ ๆ–ฐ TEE ๅฏ†้’ฅๅฏน โ”‚ โ”‚ โ”‚โ”€โ”€ POST /api/device/register โ”‚ โ”‚ โ”‚ {sn, newPubKey} โ”€โ”€โ–บโ”‚๏ผˆๆ—ง่ฎพๅค‡่‡ชๅŠจๅœ็”จ๏ผ‰ โ”‚ โ”‚โ—„โ”€โ”€ {newDeviceId} โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚โ—„โ”€โ”€ POST /api/device/sms/send โ”‚ โ”‚ โ”‚ {phone} โ”‚ โ”‚ โ”‚โ”€โ”€ ๅ‘้€็Ÿญไฟก โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚โ—„โ”€โ”€ POST /api/device/recover โ”‚ โ”‚ โ”‚ {userId, sn, smsCode, โ”‚ โ”‚ โ”‚ newPubKey} โ”‚ โ”‚ โ”‚โ”€โ”€ โ‘  ้ชŒ่ฏ็Ÿญไฟก โ”‚โ”€โ”€ ่พ“ๅ…ฅ้ชŒ่ฏ็  โ”‚ โ”‚โ”€โ”€ โ‘ก ็กฎ่ฎค SN ๅฝ’ๅฑž โ”‚ โ”‚ โ”‚โ”€โ”€ โ‘ข ็”Ÿๆˆ Recovery Token โ”‚ โ”‚ โ”‚โ”€โ”€ โ‘ฃ RSA(newPubKey, Token) โ”‚ โ”‚โ—„โ”€โ”€ {encToken, nonce} โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚โ”€โ”€ PrivKey ่งฃๅฏ† Token โ”‚ โ”‚ โ”‚โ”€โ”€ POST /api/photo/recover โ”‚ โ”‚ โ”‚ {deviceId, token} โ”€โ”€โ–บโ”‚ โ”‚ โ”‚ โ”‚โ”€โ”€ โ‘  ้ชŒ่ฏ Token โ”‚ โ”‚ โ”‚โ”€โ”€ โ‘ก ้ๅކ็…ง็‰‡ โ”‚ โ”‚ โ”‚โ”€โ”€ โ‘ข UK ่งฃ DEK โ†’ PubKey ๅŠ ๅฏ† โ”‚ โ”‚โ—„โ”€โ”€ [{photoId, encDEK}, ...] โ”€โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚โ”€โ”€ PrivKey ่งฃๅฏ†ๆฏไธช DEK โ”‚ โ”‚ โ”‚โ”€โ”€ AES-GCM ่งฃๅฏ†็…ง็‰‡ โ”‚ โ”‚ โ”‚โ”€โ”€ โœ… ็…ง็‰‡ๆขๅคๅฎŒๆˆ โ”‚ โ”‚ ``` --- ## ๐Ÿ“ ้กน็›ฎ็ป“ๆž„ ``` secure-device-demo/ โ”œโ”€โ”€ android-device/ โ”‚ โ””โ”€โ”€ DeviceCrypto.java # Android ็ซฏๅฎŒๆ•ดๅฎ‰ๅ…จๆจกๅ— โ”‚ โ”œโ”€โ”€ springboot-server/ โ”‚ โ”œโ”€โ”€ pom.xml โ”‚ โ””โ”€โ”€ src/ โ”‚ โ”œโ”€โ”€ main/ โ”‚ โ”‚ โ”œโ”€โ”€ java/com/secure/demo/ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ SecureDemoApplication.java โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ controller/ โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ DeviceController.java โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ service/ โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ KeyManagementService.java โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ DeviceBindingService.java โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ model/ โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ User.java โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ Device.java โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ EncryptedPhoto.java โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ crypto/ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ AesGcmUtil.java โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ RsaUtil.java โ”‚ โ”‚ โ””โ”€โ”€ resources/ โ”‚ โ”‚ โ””โ”€โ”€ application.properties โ”‚ โ””โ”€โ”€ test/ โ”‚ โ”œโ”€โ”€ java/com/secure/demo/ โ”‚ โ”‚ โ””โ”€โ”€ IntegrationTest.java โ”‚ โ””โ”€โ”€ resources/ โ”‚ โ””โ”€โ”€ application-test.properties โ”‚ โ””โ”€โ”€ README.md ``` --- ## ๐Ÿš€ ๅฟซ้€ŸๅฏๅŠจ ### ๅŽ็ซฏ ```bash cd springboot-server mvn spring-boot:run ``` ### ๆต‹่ฏ• ```bash cd springboot-server mvn test ``` ### Android ็ซฏ้›†ๆˆ ๅฐ† `DeviceCrypto.java` ๅคๅˆถๅˆฐ Android ้กน็›ฎ็š„ๅฏนๅบ”ๅŒ…่ทฏๅพ„ไธ‹๏ผŒ ๅœจ `Application` ๆˆ– `MainActivity` ไธญๅˆๅง‹ๅŒ–๏ผš ```java DeviceCrypto crypto = new DeviceCrypto(context); // ๆณจๅ†Œ String pubKey = crypto.getPublicKeyBase64(); String sn = crypto.getDeviceSN(); // โ†’ POST /api/device/register {sn, publicKeyBase64: pubKey} // ๆ‹็…งๅŠ ๅฏ† byte[] photo = capturePhoto(); DeviceCrypto.EncryptedPayload payload = crypto.encryptData(photo); String signature = crypto.signMetadata(sn + "|" + timestamp + "|" + photoId); // โ†’ POST /api/photo/upload {sn, photoId, ciphertextBase64, ivBase64, // dekBase64: payload.dekBase64, // metadataSignature: signature, metadata} ``` --- ## ๐Ÿ›ก๏ธ ๅฎ‰ๅ…จๅˆ†ๆž ### ๆ”ปๅ‡ปๅœบๆ™ฏ vs ้˜ฒๆŠค | ๆ”ปๅ‡ปๅœบๆ™ฏ | ็ป“ๆžœ | ๅŽŸๅ›  | |---|---|---| | ่ฎพๅค‡่ขซ root | ๆ‹ฟไธๅˆฐ TEE ็ง้’ฅ | Keystore ็กฌไปถไฟๆŠค | | ่ฎพๅค‡่ขซ็›— | ๆ— ๆณ•่งฃๅฏ†ๅކๅฒๆ•ฐๆฎ | ๆ— ็”จๆˆท็™ปๅฝ•ๆ€ | | ๆขๅคๅ‡บๅŽ‚ | ๆ—งๅฏ†้’ฅ้”€ๆฏ | TEE ๅฎ‰ๅ…จๆ“ฆ้™ค | | ๆœๅŠก็ซฏ่ขซๆ‹–ๅบ“ | DEK ่ขซ UK ๅŠ ๅฏ† | ไฟกๅฐๅŠ ๅฏ† | | SN ่ขซไผช้€  | ๆ— ๆณ•็ป‘ๅฎš/ๆขๅค | ้œ€็Ÿญไฟก้ชŒ่ฏ + SN ๅฝ’ๅฑžๆ ก้ชŒ | | ็Ÿญไฟก่ขซๆˆช่Žท | ไป้œ€ SN ๅฝ’ๅฑž | ๅคšๅฑ‚ๆ ก้ชŒ | | ๆ—ง่ฎพๅค‡ๆฎ‹็•™ | ๅทฒๅœ็”จ | ้‡ๆ–ฐๆณจๅ†Œๆ—ถๅœ็”จๆ—ง่ฎพๅค‡ | ### ๅฎ‰ๅ…จๅŽŸๅˆ™ 1. โœ… **่ฎพๅค‡้›ถไฟกไปป** โ€” ่ฎพๅค‡ๅชๆŒๆœ‰็ญพๅๅฏ†้’ฅ๏ผŒไธๆŒๆœ‰่งฃๅฏ†ๅฏ†้’ฅ 2. โœ… **ๅ‰ๅ‘ๅฎ‰ๅ…จ** โ€” ๆฏๆฌกๆขๅค็”Ÿๆˆๆ–ฐๅฏ†้’ฅๅฏน 3. โœ… **ไฟกๅฐๅŠ ๅฏ†** โ€” DEK ๆฐธไธๆ˜Žๆ–‡ๅญ˜ๅบ“ 4. โœ… **็Ÿญไฟก + SN ๅŒๅ› ๅญ** โ€” ๆขๅคๅฟ…้กปไธค่€…ๅŒๆ—ถ้€š่ฟ‡ 5. โœ… **ไธ€ๆฌกๆ€งไปค็‰Œ** โ€” Recovery Token ๅซ nonce + ๆ—ถ้—ด็ช—ๅฃ --- ## โš ๏ธ ็”Ÿไบง็Žฏๅขƒๆณจๆ„ไบ‹้กน | Demo ็ฎ€ๅŒ– | ็”Ÿไบง็Žฏๅขƒๅบ”ๆ”นไธบ | |---|---| | UK ็›ดๆŽฅๅญ˜ DB | KMS ๆ‰˜็ฎก๏ผˆ้˜ฟ้‡Œไบ‘ KMS / AWS KMS๏ผ‰ | | ็Ÿญไฟก็ ้šๆœบ็”Ÿๆˆ | ๅฏนๆŽฅ่…พ่ฎฏไบ‘/้˜ฟ้‡Œไบ‘็ŸญไฟกๆœๅŠก | | ๅ†…ๅญ˜ ConcurrentHashMap | JPA + PostgreSQL/MySQL | | ๆ˜Žๆ–‡ DEK ไผ ่พ“ | ่ฎพๅค‡็ซฏ็”จๆœๅŠก็ซฏๅ…ฌ้’ฅๅŠ ๅฏ† DEK ๅŽไผ ่พ“ | | ๆ— ้ชŒ็ญพๅฎž็Žฐ | ๆœๅŠก็ซฏ็”จ่ฎพๅค‡ๅ…ฌ้’ฅ้ชŒ่ฏ ECDSA ็ญพๅ | | ๆ— ้ข‘็އ้™ๅˆถ | Redis ้™ๆต๏ผˆ็Ÿญไฟก/API๏ผ‰ | | ๆ— ๅฎก่ฎกๆ—ฅๅฟ— | ๆ‰€ๆœ‰ๅฏ†้’ฅๆ“ไฝœๅ†™ๅฎก่ฎก่กจ | | Recovery Token ๆ— ่ฟ‡ๆœŸ | ๅŠ  5 ๅˆ†้’Ÿๆ—ถ้—ด็ช—ๅฃ + Redis ้˜ฒ้‡ๆ”พ | --- ## ๐Ÿ“‹ API ๆŽฅๅฃๆธ…ๅ• | Method | Path | ่ฏดๆ˜Ž | |---|---|---| | POST | `/api/device/register` | ่ฎพๅค‡ๆณจๅ†Œ๏ผˆSN + ๅ…ฌ้’ฅ๏ผ‰ | | POST | `/api/device/bind` | ็”จๆˆท็ป‘ๅฎš่ฎพๅค‡ | | POST | `/api/device/sms/send` | ๅ‘้€็Ÿญไฟก้ชŒ่ฏ็  | | POST | `/api/photo/upload` | ไธŠไผ ๅŠ ๅฏ†็…ง็‰‡ | | POST | `/api/device/recover` | ๆขๅค่ฎพๅค‡๏ผˆ็Ÿญไฟก้ชŒ่ฏ๏ผ‰ | | POST | `/api/photo/recover` | ๆขๅคๅŽ่Žทๅ–็…ง็‰‡ DEK | | GET | `/api/photo/{id}/decrypt` | ็”จๆˆทไธ‹่ฝฝๅนถ่งฃๅฏ†็…ง็‰‡ | | GET | `/api/health` | ๅฅๅบทๆฃ€ๆŸฅ |