Unity Android에서 self signed SSL 인증서 사용하기

Unity로 WWW 클래스를 이용해 통신 중인데 Editor에서는 문제없이 통신이 되다가 Android 폰으로 옮겼더니 통신이 안되는군요.
역시 SSL 인증서를 인식하지 못하는 문제가 발생했습니다.
self signed SSL 인증서는 기본 인증서 서비스가 알아볼 일이 없겠죠.
해결하는 방법으로는 크게 3가지 정도 생각해 볼수 있습니다.



  1. self signed SSL 인증서를 핸드폰에 설치하도록 한다.

  2. Unity의 WWW 을 사용하지 않고 self signed SSL을 지원할 수 있도록 직접 구현한다.

  3. SSL 인증서 인증 과정에 관여하여 조작한다.

그중 3번 방법을 소개합니다.
.NET Framework의 ServicePointManager.ServerCertificateValidationCallback 를 이용하는 방법은 Android + WWW 사용이라는 환경에서는 안되더군요. 그래서 Java에서 바꿔보았습니다.

import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

class X509Man implements X509TrustManager {

private X509TrustManager standardTrustManager = null; // 기본 TrustManager 인스턴스

public X509Man() throws KeyStoreException, NoSuchAlgorithmException {

// 기본 KeyStore
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());

// X509 TrustManagerFactory로 기본 TrustManager를 구함
TrustManagerFactory factory = TrustManagerFactory.getInstance(“X509”);
factory.init(keystore);
TrustManager[] trustmanagers = factory.getTrustManagers();
if (trustmanagers.length == 0) {
throw new NoSuchAlgorithmException(“X509 trust manager not supported”);
}
this.standardTrustManager = (X509TrustManager) trustmanagers[0];
}

@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
// 클라이언트 인증서는 기본 TrustManager가 처리
this.standardTrustManager.checkClientTrusted(chain, authType);
}

@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
// 서버 인증서는 필요한 규칙에 따라 처리
// … 생략

// 필요한 규칙 이외의 인증서는 기본 TrustManager가 처리
this.standardTrustManager.checkServerTrusted(chain, authType);
}

@Override
public X509Certificate[] getAcceptedIssuers() {
// 허용된 Issuer 목록도 기본 TrustManager 처리
return this.standardTrustManager.getAcceptedIssuers();
}
}


이어서 위의 클래스를 사용하는 함수 예제

private void trustOurHost() {

try {

// 기본 Hostname 검증기
final HostnameVerifier origHostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier();

// 커스텀 Hostname 검증기로 바꾸기
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {

// 필요한 규칙에 따라 hostname과 session으로 검증하여 true 반환
// … 생략

// 그 외의 경우는 기본 Hostname 검증기 사용
return origHostnameVerifier == null || origHostnameVerifier.verify(hostname, session);
}
});

// TLS 통신에서는 위에서 만든 클래스를 사용하는 Socket Factory를 지정
SSLContext context = SSLContext.getInstance(“TLS”);
context.init(null, new X509TrustManager[]{new X509Man()}, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory());

} catch (Exception e) {
// 여기 오면 안되는데…
}
}


참고 자료
[How do I get a hold of Java’s default SSL Trust Manager]
[Java example – EasyX509TrustManager.java]
[Accessing Google APIs in Unity]

글쓴이

BS

BS == Programmer