투케이2K

1034. (Android/Java) AWS WebRTC Signaling 메시지를 주고 받기 위한 WebSocket 웹소켓 URL 생성 API AWS4Signer 헤더 생성 방법 본문

Android

1034. (Android/Java) AWS WebRTC Signaling 메시지를 주고 받기 위한 WebSocket 웹소켓 URL 생성 API AWS4Signer 헤더 생성 방법

투케이2K 2025. 10. 11. 11:37
728x90

[개발 환경 설정]

개발 툴 : AndroidStudio

개발 언어 : Java / Kotlin

 

[소스 코드]

// --------------------------------------------------------------------------------------
[개발 및 테스트 환경]
// --------------------------------------------------------------------------------------

- 언어 : Java / Kotlin


- 개발 툴 : AndroidStudio


- 기술 구분 : AWS / AWS4Signer / WebRTC


- 사전) AWS 의존성 부여 설정

  // --------------------------------------------
  // [Aws build.gradle 라이브러리 정의] : targetSdk 33 ~ 34
  // --------------------------------------------
  /**
    * implementation 'com.amazonaws:aws-android-sdk-kms:2.57.0'
    * implementation 'com.amazonaws:aws-android-sdk-s3:2.57.0'
    * implementation 'com.amazonaws:aws-android-sdk-iot:2.57.0'
    * implementation 'com.amazonaws:aws-android-sdk-mobile-client:2.57.0'
    * implementation 'com.amazonaws:aws-android-sdk-kinesisvideo:2.57.0'
    * */


- 사전) AWS4Signer 설명 : 

  >> AWS SigV4 는 AWS API 요청에 인증 정보를 추가하기 위한 AWS 서명 프로토콜입니다

  >> AWS SigV4 는 HTTP Authorization 헤더에서 또는 URL의 쿼리 문자열로 표현될 수 있습니다

  >> AWS SigV4 요청에 서명하는 이유 : 

    - 요청자의 ID 확인 : 인증된 요청에는 액세스 키(액세스 키 ID, 비밀 액세스 키)를 사용하여 만든 서명이 필요합니다

    - 전송 중인 데이터 보호 : 요청이 전송되는 동안 훼손되는 것을 방지하기 위해 일부 요청 요소를 사용하여 요청의 해시(다이제스트)를 계산하고 결과 해시 값을 요청의 일부로 포함합니다

    - 잠재적 재생 공격으로부터 보호 : 대부분의 경우 요청서의 타임스탬프 시간으로부터 5분 이내에 AWS에 요청이 도착해야 합니다 (그렇지 않으면 AWS가 요청을 거부합니다)

  >> AWS SigV4 서명 요청 프로세스 : 

    - 요청 세부 정보를 기반으로 표준 요청 생성

    - AWS 자격 증명을 사용하여 서명 계산

    - 이 서명을 요청에 Authorization 헤더로 추가


- 사전) WebRTC getSignalingChannelEndpoint HTTPS, WSS 사용 범위 설명 : 

  >> kinesisVideoClient.getSignalingChannelEndpoint 메서드는 Amazon Kinesis Video Streams with WebRTC 를 사용할 때, 시그널링 채널의 엔드포인트 주소를 가져오기 위해 사용됩니다.

  >> 반환되는 주소들은 주로 시그널링 통신 (WebRTC peer 연결 설정을 위한 통신) 에 사용됩니다.

  >> getSignalingChannelEndpoint 를 호출 하면 HTTP(S) 및 WSS(WebSocket Secure) 프로토콜을 사용하는 엔드포인트 주소들이 반환 됩니다.

  >> getSignalingChannelEndpoint 호출 시 반환 되는 형식 예시 : 

    {
    "ResourceEndpointList": [
        {
        "Protocol": "HTTPS",
        "ResourceEndpoint": "https://<endpoint>"
        },
        {
        "Protocol": "WSS",
        "ResourceEndpoint": "wss://<endpoint>"
        }
    ]
    }

// --------------------------------------------------------------------------------------






// --------------------------------------------------------------------------------------
[소스 코드]
// --------------------------------------------------------------------------------------

// --------------------------------------------
// [필요 변수 선언]
// --------------------------------------------
String ACCESS_KEY = "AK...LM"; // IAM 계정 AccessKey
String SECRET_KEY = "mn...si"; // IAM 계정 SecretKey
String REGION = "ap-northeast-1"; // AWS 리전 정보

String WSS_ENDPOINT = "WSS:// ...."; // WSS 엔드포인트

String CHANNEL_NAME = "DEVICE_1"; // 신호채널 명칭
String CLIENT_ID = "TEST_CLIENT"; // 접속 시도 클라이언트 고유 아이디 값


// --------------------------------------------
// [AWSCredentials 자격 증명 수행]
// --------------------------------------------
AWSCredentials credentials = new BasicAWSCredentials(ACCESS_KEY, SECRET_KEY); // TODO [IAM 계정 Key 초기화]
//BasicSessionCredentials credentials = new BasicSessionCredentials(ACCESS_KEY, SECRET_KEY, SESSION_TOKEN); // TODO [STS 임시 정보 초기화]


// --------------------------------------------
// [AWSKinesisVideoClient 생성]
// --------------------------------------------
AWSKinesisVideo awsKinesisVideoClient = new AWSKinesisVideoClient(credentials);
awsKinesisVideoClient.setRegion(Region.getRegion(REGION));


// --------------------------------------------
// [신호 채널 ARN 정보 조회 수행]

// --------------------------------------------
DescribeSignalingChannelRequest arn_request = new DescribeSignalingChannelRequest()
        .withChannelName(CHANNEL_NAME);

DescribeSignalingChannelResult arn_result = awsKinesisVideoClient.describeSignalingChannel(arn_request);

ChannelInfo channelInfo = arn_result.getChannelInfo(); // TODO Get ChannelInfo
String CHANNEL_ARN = channelInfo.getChannelARN();


// --------------------------------------------
// [API 요청 서비스 명칭 선언]
// --------------------------------------------
String SERVICE_NAME = "kinesisvideo";


// ---------------------------------------------
// [AWS Signature V4 서명 적용]
// ---------------------------------------------
// import com.amazonaws.DefaultRequest;
// import com.amazonaws.Request;
// ---------------------------------------------
// import okhttp3.Request;
// import okhttp3.RequestBody;
// import okhttp3.Response;
// import okio.BufferedSink;
// ---------------------------------------------
com.amazonaws.Request<?> socket_sign_request = new DefaultRequest<Void>(SERVICE_NAME);
socket_sign_request.setHttpMethod(HttpMethodName.GET);
socket_sign_request.setEndpoint(URI.create(WSS_ENDPOINT)); // TODO Set URL
socket_sign_request.setEncodedResourcePath("/");

// Query Parameter 추가
Map<String, String> queryParams = new HashMap<>();
queryParams.put("X-Amz-ChannelARN", CHANNEL_ARN);
queryParams.put("X-Amz-ClientId", CLIENT_ID);

socket_sign_request.setParameters(queryParams);


// ---------------------------------------------
// [AWS 서명자 설정]
// ---------------------------------------------
AWS4Signer aws_socket_signer = new AWS4Signer();
aws_socket_signer.setServiceName(SERVICE_NAME);
aws_socket_signer.setRegionName(REGION);

Date expiration = new java.util.Date(System.currentTimeMillis() + 60 * 1000); // 1분
aws_socket_signer.presignRequest(socket_sign_request, credentials, expiration);


// ---------------------------------------------
// [실제 소켓 연결 Presigned URL 생성]
// ---------------------------------------------
String presignedUrl = "";

StringBuilder url = new StringBuilder(socket_sign_request.getEndpoint().toString());
url.append("?");
boolean first = true;
for (Map.Entry<String, String> entry : socket_sign_request.getParameters().entrySet()) {
    if (!first) url.append("&");
    first = false;
    url.append(URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8.name()))
            .append("=")
            .append(URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8.name()));
}
presignedUrl = url.toString();


// ---------------------------------------------
// [OKHttp Request.Builder 생성]
// ---------------------------------------------
Request.Builder okSocketReqBuilder = new Request.Builder()
        .url(presignedUrl)
        .get();


// ---------------------------------------------
// [서명 헤더 추가]
// ---------------------------------------------
for (String key : socket_sign_request.getHeaders().keySet()) {
    okSocketReqBuilder.header(key, socket_sign_request.getHeaders().get(key));
}

S_Log._W_("STEP :: Signaling :: WebSocket Connect :: Start", new String[]{"URL :: " + presignedUrl, "HEADER :: " + okSocketReqBuilder.build().headers()});

// --------------------------------------------------------------------------------------





// --------------------------------------------------------------------------------------
[참고 사이트]
// --------------------------------------------------------------------------------------

[Aws SigV4 인증 및 서명 프로토콜 설명]

https://blog.naver.com/kkh0977/223874467995?trackingCode=blog_bloghome_searchlist


[WebRTC GetIceServerConfig TURN 릴레이 서버 IceServerList 설명]

https://blog.naver.com/kkh0977/224035896944


[WebRTC getSignalingChannelEndpoint HTTPS, WSS 사용 범위 정리]

https://blog.naver.com/kkh0977/224035890592


[WebRTC GetSignalingChannelEndpoint 신호 채널 엔드포인트 설명]

https://blog.naver.com/kkh0977/224035894347

// --------------------------------------------------------------------------------------
 
728x90
반응형
Comments