header_logo

COLDSURF

Browse events

About

Blog

#DRM

#암호화

Written by Paul

1. DRM의 개요

디지털 저작권 관리(DRM, Digital Rights Management)는 디지털 콘텐츠의 불법 복제 및 무단 사용을 방지하는 기술입니다. 주로 영상 스트리밍, 전자책, 게임 등에서 사용됩니다.

1.1 DRM의 핵심 기능

  1. 콘텐츠 암호화 – AES-CTR을 이용하여 콘텐츠 보호
  1. 라이선스 제어 – 인증된 사용자만 콘텐츠 접근 가능
  1. 기기 인증 및 보안 정책 적용 – 승인된 기기에서만 재생 가능
  1. 멀티 DRM 지원 – Widevine(Google), PlayReady(Microsoft), FairPlay(Apple) 등

2. DRM에서 사용되는 암호화 방식

2.1 AES-CTR (Counter Mode)

  • 방법:
    • 각 블록마다 증가하는 카운터 값을 암호화하여 키 스트림을 생성 후 평문과 XOR 연산
    • 랜덤 액세스가 가능하여 스트리밍 서비스에 적합
  • 장점:
    • 빠른 암호화/복호화 속도
    • 병렬 처리 가능
    • 랜덤 액세스 지원
  • 단점:
    • Nonce(일회성 값) 재사용 시 보안 취약
  • 사용 예:
    • 스트리밍 서비스
🔹 AES-CTR 암호화 과정
(Nonce + 1 → 🔒) ⊕ P1 → C1
(Nonce + 2 → 🔒) ⊕ P2 → C2
(Nonce + 3 → 🔒) ⊕ P3 → C3

3. DRM 라이선스 서버와 키 관리

3.1 DRM 라이선스 서버의 역할

  • 암호화된 콘텐츠를 재생하기 위해서는 DRM 라이선스 서버에서 키를 받아야 함
  • 라이선스 서버는 사용자의 기기, 계정, 정책을 검증한 후 대칭키(AES 키)를 제공
  • 대칭키는 공개키(RSA, ECC)로 암호화되어 전달됨

3.2 키 교환 과정

  1. 사용자가 콘텐츠 재생 요청
  1. 클라이언트가 DRM 라이선스 서버에 요청 전송
  1. 서버에서 사용자의 기기 및 계정 검증
  1. 허가된 경우, 암호화된 대칭키 제공 (공개키 암호화 사용)
  1. 클라이언트는 받은 키를 복호화하여 콘텐츠 해독
📌 정리AES(대칭키) → 콘텐츠 암호화
RSA(공개키) → 대칭키 보호 및 전송
최종적으로 콘텐츠 복호화에 사용되는 키는 "대칭키(AES)"

4. DRM 암호화 구현 예제 (Python)

from Crypto.Cipher import AES from Crypto.Util import Counter import base64 def encrypt_content(content, key, nonce): counter = Counter.new(64, prefix=nonce) cipher = AES.new(key, AES.MODE_CTR, counter=counter) encrypted_content = cipher.encrypt(content.encode()) return base64.b64encode(encrypted_content).decode() key = b'16byteslongkey!' nonce = b'unique_iv' original_content = "DRM 보호된 콘텐츠입니다." encrypted_content = encrypt_content(original_content, key, nonce) print("🔹 암호화된 콘텐츠:", encrypted_content)
def decrypt_content(encrypted_data, key, nonce): encrypted_data = base64.b64decode(encrypted_data) counter = Counter.new(64, prefix=nonce) cipher = AES.new(key, AES.MODE_CTR, counter=counter) return cipher.decrypt(encrypted_data).decode() decrypted_content = decrypt_content(encrypted_content, key, nonce) print("🔹 복호화된 콘텐츠:", decrypted_content)

5. FairPlay DRM 구현 예시 (웹)

5.1 웹에서 FairPlay DRM 구현 (JavaScript)

웹 브라우저에서 FairPlay DRM을 구현하는 방법은 Encrypted Media Extensions (EME) API를 사용하여 FairPlay 키를 처리하는 방식입니다. FairPlay는 com.apple.fps.1_0 키 시스템을 사용합니다.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>FairPlay DRM Example</title> </head> <body> <video id="video" controls></video> <script> const videoElement = document.getElementById("video"); // FairPlay 라이선스 URL const licenseUrl = "https://license.example.com/fairplay"; // Encrypted Media Extensions (EME) API 초기화 if (navigator.requestMediaKeySystemAccess) { const config = [{ initDataTypes: ['cenc'], videoCapabilities: [{ contentType: 'video/mp4; codecs="avc1.4d401e"' }] }]; // FairPlay 키 시스템을 사용하여 요청 navigator.requestMediaKeySystemAccess('com.apple.fps.1_0', config) .then(keySystemAccess => { return keySystemAccess.createMediaKeys(); }) .then(mediaKeys => { videoElement.setMediaKeys(mediaKeys); // Play DRM 콘텐츠 const mediaSource = new MediaSource(); videoElement.src = mediaSource; mediaSource.addEventListener('sourceopen', () => { // 라이선스 서버에서 키를 요청 mediaSource.sourceBuffer.addEventListener('encrypted', event => { const session = mediaKeys.createSession(); session.onmessage = (e) => { // 라이선스 서버에서 받은 응답을 통해 DRM 키 전달 const licenseRequest = new XMLHttpRequest(); licenseRequest.open('POST', licenseUrl); licenseRequest.setRequestHeader('Content-Type', 'application/octet-stream'); licenseRequest.send(e.message); }; session.generateRequest(event.initDataType, event.initData); }); }); mediaSource.addSourceBuffer('video/mp4; codecs="avc1.4d401e"'); mediaSource.addSourceBuffer('audio/mp4; codecs="mp4a.40.2"'); }) .catch(error => { console.error("FairPlay DRM 지원 오류:", error); }); } else { console.error("Encrypted Media Extensions (EME) 미지원"); } </script> </body> </html>

6. ExoPlayer + Widevine DRM 구현 예시 (Android)

6.1 ExoPlayer + Widevine DRM 구현 (Android)

ExoPlayer는 Android에서 DRM 콘텐츠를 재생할 때 가장 많이 사용되는 라이브러리입니다. Widevine DRM을 사용하여 DRM 보호된 비디오 콘텐츠를 재생할 수 있습니다. 아래는 ExoPlayerWidevine DRM을 활용한 예제입니다.
import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import com.google.android.exoplayer2.ExoPlayer import com.google.android.exoplayer2.MediaItem import com.google.android.exoplayer2.source.dash.DashMediaSource import com.google.android.exoplayer2.upstream.DefaultHttpDataSource import com.google.android.exoplayer2.upstream.HttpDataSource import com.google.android.exoplayer2.drm.DrmSessionManager import com.google.android.exoplayer2.drm.DefaultDrmSessionManager import com.google.android.exoplayer2.drm.DefaultDrmProvider import com.google.android.exoplayer2.drm.WidevineUtil import com.google.android.exoplayer2.util.MimeTypes import com.google.android.exoplayer2.upstream.DataSource import android.net.Uri class DRMPlayerActivity : AppCompatActivity() { private lateinit var exoPlayer: ExoPlayer override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // DRM 콘텐츠 URL val contentUrl = "https://example.com/drm/protected_video.mpd" // Widevine 라이선스 URL val licenseUrl = "https://license.example.com/widevine" // 기본 HTTP 데이터 소스 val dataSourceFactory: HttpDataSource.Factory = DefaultHttpDataSource.Factory() // Widevine DRM 세션 관리 설정 val drmCallback: DrmSessionManager.DrmSessionCallback = DefaultDrmProvider(licenseUrl) val drmSessionManager = DefaultDrmSessionManager(drmCallback) // Dash MediaSource 설정 val mediaItem = MediaItem.fromUri(Uri.parse(contentUrl)) val mediaSource = DashMediaSource.Factory(dataSourceFactory).createMediaSource(mediaItem) // ExoPlayer 초기화 exoPlayer = ExoPlayer.Builder(this).build() exoPlayer.setMediaSource(mediaSource) exoPlayer.prepare() exoPlayer.play() } override fun onDestroy() { super.onDestroy() exoPlayer.release() } }

7. iOS FairPlay DRM 콘텐츠 복호화 예시

7.1 FairPlay DRM을 활용한 콘텐츠 복호화

import AVFoundation import UIKit class DRMPlayerViewController: UIViewController { var player: AVPlayer? override func viewDidLoad() { super.viewDidLoad() // DRM이 적용된 콘텐츠 URL let contentURL = URL(string: "https://example.com/encrypted_video.m3u8")! // DRM 라이선스 서버 URL let licenseServerURL = URL(string: "https://license.example.com/fairplay")! let asset = AVURLAsset(url: contentURL) asset.resourceLoader.setDelegate(self, queue: DispatchQueue.main) let playerItem = AVPlayerItem(asset: asset) player = AVPlayer(playerItem: playerItem) let playerLayer = AVPlayerLayer(player: player) playerLayer.frame = self.view.bounds self.view.layer.addSublayer(playerLayer) player?.play() } } // AVAssetResourceLoaderDelegate 구현 (라이선스 요청 처리) extension DRMPlayerViewController: AVAssetResourceLoaderDelegate { func resourceLoader(_ resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest) -> Bool { guard let url = loadingRequest.request.url else { return false } // DRM 키 요청 처리 (FairPlay 라이선스 서버와 통신) let licenseRequest = URLRequest(url: URL(string: "https://license.example.com/fairplay")!) let task = URLSession.shared.dataTask(with: licenseRequest) { data, response, error in guard let data = data, error == nil else { loadingRequest.finishLoading(with: error) return } // DRM 라이선스 서버에서 받은 데이터로 복호화된 콘텐츠를 응답 loadingRequest.dataRequest?.respond(with: data) loadingRequest.finishLoading() } task.resume() return true } }

7.2 복호화 과정 설명

  1. 컨텐츠 요청: AVPlayerAVURLAsset을 사용하여 DRM이 적용된 콘텐츠를 요청합니다.
  1. 라이선스 서버와의 통신: 콘텐츠가 암호화되어 있으면, AVAssetResourceLoaderDelegateshouldWaitForLoadingOfRequestedResource 메소드가 호출되고, 여기서 라이선스 요청을 FairPlay DRM 서버에 전송합니다.
  1. 복호화 키 수신: 서버는 복호화에 필요한 를 응답으로 보내며, 이 키를 사용하여 콘텐츠를 복호화합니다.
  1. 콘텐츠 복호화 및 응답: 서버로부터 받은 데이터는 loadingRequest.dataRequest?.respond(with:)를 통해 응답되어 복호화된 콘텐츠가 AVPlayer로 전달됩니다.

7.3 FairPlay DRM 키 처리 및 콘텐츠 복호화

이 코드에서는 FairPlay DRM 라이선스 서버와의 통신을 통해 를 받아 콘텐츠를 복호화하는 과정을 보여주고 있습니다. 복호화된 콘텐츠는 AVPlayer에서 재생할 수 있도록 처리됩니다.
이 코드는 iOS 앱에서 FairPlay DRM을 적용한 콘텐츠를 안전하게 복호화하고 재생하는 방법을 다루며, 라이선스 요청 및 처리 과정을 간단히 구현한 예제입니다.

8. Web Browser에서 Widevine DRM 콘텐츠 복호화 예시 (JavaScript)

8.1 Widevine DRM을 활용한 웹 브라우저에서 콘텐츠 복호화

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Widevine DRM Video</title> </head> <body> <video id="video" controls width="640" height="360"></video> <script> // DRM이 적용된 비디오 URL const videoUrl = 'https://example.com/path/to/your/encrypted/video.mpd'; // Widevine 라이선스 서버 URL const licenseServerUrl = 'https://license.example.com/widevine'; // video 엘리먼트 가져오기 const video = document.getElementById('video'); // Encrypted Media Extensions (EME) API 사용 if ('requestMediaKeySystemAccess' in navigator) { navigator.requestMediaKeySystemAccess('com.widevine.alpha', [{ initDataTypes: ['cenc'], videoCapabilities: [{ contentType: 'video/mp4; codecs="avc1.42E01E"' }] }]) .then(keySystemAccess => { // 미디어 키 세션 생성 return keySystemAccess.createMediaKeys(); }) .then(mediaKeys => { // 비디오 요소에 MediaKeys 설정 video.setMediaKeys(mediaKeys); // 요청할 라이선스 서버 URL const licenseRequest = { type: 'license', url: licenseServerUrl, initData: new Uint8Array() }; // 라이선스 서버와 통신하여 키를 받아옴 return fetch(licenseRequest.url, { method: 'POST', body: JSON.stringify(licenseRequest), headers: { 'Content-Type': 'application/json' } }).then(response => response.arrayBuffer()) .then(licenseData => { // 받은 라이선스 데이터로 키를 설정 mediaKeys.setServerCertificate(new Uint8Array(licenseData)); return mediaKeys; }); }) .then(mediaKeys => { // 복호화된 콘텐츠 재생 const mediaSource = new MediaSource(); video.src = URL.createObjectURL(mediaSource); // 추가적인 로직을 통해 DRM을 통해 암호화된 콘텐츠를 재생하는 과정 // (예: MediaSource API와 연결된 MediaPlayer 객체를 이용해 실제 비디오 데이터 처리) }) .catch(error => { console.error('Error setting up Widevine DRM:', error); }); } else { console.error('EME not supported on this browser.'); } </script> </body> </html>

8.2 복호화 과정 설명

  1. 미디어 키 시스템 접근 요청:
      • navigator.requestMediaKeySystemAccess 메소드를 사용하여 브라우저에 Widevine 키 시스템을 요청합니다. 이때 필요한 코덱미디어 데이터 타입을 정의합니다.
  1. MediaKeys 생성:
      • Widevine DRM 키를 관리하는 객체인 MediaKeys를 생성하고, 이를 <video> 엘리먼트에 설정하여 복호화 작업을 준비합니다.
  1. 라이선스 서버와 통신:
      • 라이선스 서버로 HTTP 요청을 보내어 콘텐츠 복호화에 필요한 키를 받습니다. 이때, Widevine 라이선스 서버는 POST 요청을 통해 콘텐츠를 해독하는 데 필요한 라이선스를 반환합니다.
  1. 복호화 및 콘텐츠 재생:
      • 받은 라이선스를 통해 복호화된 콘텐츠가 HTML5 <video> 엘리먼트에서 재생됩니다. DRM 보호가 적용된 콘텐츠는 이 과정을 통해 복호화되고 재생 가능합니다.

8.3 Widevine DRM 복호화 프로세스

  • WidevineEncrypted Media Extensions (EME) API와 함께 작동하여 브라우저에서 DRM 보호된 콘텐츠를 복호화합니다.
  • 웹에서 DRM 콘텐츠를 재생하려면, MediaKeySystemAccess, MediaKeys, MediaKeySession 등의 API를 활용하여 라이선스 요청, 복호화 및 콘텐츠 재생을 처리할 수 있습니다.
  • 이 예제는 Widevine DRM을 사용하여 콘텐츠를 보호하고 복호화하는 과정입니다.

9. 결론

🔹 DRM 솔루션은 AES-CTR을 활용하여 콘텐츠 보호
🔹 라이선스 서버를 통해 대칭키를 안전하게 전달하여 보안 강화
🔹 FairPlay, Widevine, PlayReady DRM을 통해 다양한 플랫폼에서 안전한 콘텐츠 보호 가능
🔹 스트리밍 서비스에서 빠르고 효율적인 DRM 구현 가능
스트리밍 서비스 등은 AES-CTR 기반 DRM 구조를 사용하며, 각 플랫폼에 맞는 FairPlay, Widevine, PlayReady DRM 솔루션을 통해 콘텐츠 보호를 강화하고 있습니다. 🚀

10. DRM 구현 예제 종합

위에서 다룬 FairPlayWidevine DRM을 사용하는 실제 코드 예제들을 통해, 다양한 플랫폼에서 DRM 콘텐츠 보호를 구현할 수 있음을 알 수 있습니다.

10.1. Widevine DRM 구현 예시 (Android)

import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import com.google.android.exoplayer2.ExoPlayer import com.google.android.exoplayer2.MediaItem import com.google.android.exoplayer2.source.dash.DashMediaSource import com.google.android.exoplayer2.upstream.DefaultHttpDataSource import com.google.android.exoplayer2.drm.DefaultDrmSessionManager import com.google.android.exoplayer2.drm.DrmSessionManager import com.google.android.exoplayer2.drm.WidevineUtil import android.net.Uri class DRMPlayerActivity : AppCompatActivity() { private lateinit var exoPlayer: ExoPlayer override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // DRM 콘텐츠 URL val contentUrl = "https://example.com/drm/protected_video.mpd" // Widevine 라이선스 URL val licenseUrl = "https://license.example.com/widevine" // 기본 HTTP 데이터 소스 val dataSourceFactory: HttpDataSource.Factory = DefaultHttpDataSource.Factory() // Widevine DRM 세션 관리 설정 val drmCallback: DrmSessionManager.DrmSessionCallback = DefaultDrmProvider(licenseUrl) val drmSessionManager = DefaultDrmSessionManager(drmCallback) // Dash MediaSource 설정 val mediaItem = MediaItem.fromUri(Uri.parse(contentUrl)) val mediaSource = DashMediaSource.Factory(dataSourceFactory).createMediaSource(mediaItem) // ExoPlayer 초기화 exoPlayer = ExoPlayer.Builder(this).build() exoPlayer.setMediaSource(mediaSource) exoPlayer.prepare() exoPlayer.play() } override fun onDestroy() { super.onDestroy() exoPlayer.release() } }

10.2. iOS에서 FairPlay DRM 구현 예시 (Swift)

import AVFoundation import UIKit class DRMPlayerViewController: UIViewController { var player: AVPlayer? override func viewDidLoad() { super.viewDidLoad() // DRM이 적용된 콘텐츠 URL let contentURL = URL(string: "https://example.com/encrypted_video.m3u8")! // DRM 라이선스 서버 URL let licenseServerURL = URL(string: "https://license.example.com/fairplay")! let asset = AVURLAsset(url: contentURL) asset.resourceLoader.setDelegate(self, queue: DispatchQueue.main) let playerItem = AVPlayerItem(asset: asset) player = AVPlayer(playerItem: playerItem) let playerLayer = AVPlayerLayer(player: player) playerLayer.frame = self.view.bounds self.view.layer.addSublayer(playerLayer) player?.play() } } // AVAssetResourceLoaderDelegate 구현 (라이선스 요청 처리) extension DRMPlayerViewController: AVAssetResourceLoaderDelegate { func resourceLoader(_ resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest) -> Bool { guard let url = loadingRequest.request.url else { return false } // DRM 키 요청 처리 (FairPlay 라이선스 서버와 통신) let licenseRequest = URLRequest(url: URL(string: "https://license.example.com/fairplay")!) let task = URLSession.shared.dataTask(with: licenseRequest) { data, response, error in guard let data = data, error == nil else { loadingRequest.finishLoading(with: error) return } loadingRequest.dataRequest?.respond(with: data) loadingRequest.finishLoading() } task.resume() return true } }

10.3. 웹 브라우저에서 Widevine DRM 구현 예시 (JavaScript)

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Widevine DRM Video</title> </head> <body> <video id="video" controls width="640" height="360"></video> <script> // DRM이 적용된 비디오 URL const videoUrl = 'https://example.com/path/to/your/encrypted/video.mpd'; // Widevine 라이선스 서버 URL const licenseServerUrl = 'https://license.example.com/widevine'; // video 엘리먼트 가져오기 const video = document.getElementById('video'); // Encrypted Media Extensions (EME) API 사용 if ('requestMediaKeySystemAccess' in navigator) { navigator.requestMediaKeySystemAccess('com.widevine.alpha', [{ initDataTypes: ['cenc'], videoCapabilities: [{ contentType: 'video/mp4; codecs="avc1.42E01E"' }] }]) .then(keySystemAccess => { // 미디어 키 세션 생성 return keySystemAccess.createMediaKeys(); }) .then(mediaKeys => { // 비디오 요소에 MediaKeys 설정 video.setMediaKeys(mediaKeys); // 요청할 라이선스 서버 URL const licenseRequest = { type: 'license', url: licenseServerUrl, initData: new Uint8Array() }; // 라이선스 서버와 통신하여 키를 받아옴 return fetch(licenseRequest.url, { method: 'POST', body: JSON.stringify(licenseRequest), headers: { 'Content-Type': 'application/json' } }).then(response => response.arrayBuffer()) .then(licenseData => { // 받은 라이선스 데이터로 키를 설정 mediaKeys.setServerCertificate(new Uint8Array(licenseData)); return mediaKeys; }); }) .then(mediaKeys => { // 복호화된 콘텐츠 재생 const mediaSource = new MediaSource(); video.src = URL.createObjectURL(mediaSource); // 추가적인 로직을 통해 DRM을 통해 암호화된 콘텐츠를 재생하는 과정 }) .catch(error => { console.error('Error setting up Widevine DRM:', error); }); } else { console.error('EME not supported on this browser.'); } </script> </body> </html>
 

© 2024 COLDSURF, Inc.

Privacy Policy

Terms of Service