ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [spring][react][SSL] 인증서 발급하기
    금융프로젝트 2022. 10. 24. 10:59

    Https와 SSL인증서

    HTTP는 HTML을 전송하기 위한 통신규약이다. HTTPS는 데이터를 전송할 때 암호화를 사용하는 통신규약이다. HTTPS는 SSL프로토콜 위에서 돌아가는 프로토콜이다. SSL인증서는 클라이언트와 서버간의 통신을 제3자가 보증해주는 전자화된 문서다. 공인인증서가 그 예이다. SSL인증서를 통해 HTTPS서버로 접속할 수 있다.

    CA

    인증서의 역할은 클라이언트가 접속한 서버가 클라이언트가 의도한 서버가 맞는지 보장하는 역할을 한다. 이 역할을 하는 민간기업들이 있는데 이런 기업들을 CA라고 한다.

    사설 인증기관

    사설 CA의 인증서를 이용한다면 브라우저는 경고를 출력한다.

    공인된 CA의 인증서를 사용한다면 경고를 출력하지 않는다.

    프로세스

    1. 사용자 데이터 세팅

    2. 사용자 신원 확인

    따로 주민등록번호가 실제 주민등록번호인지 확인할 권한이 없으므로 아이디가 db에 존재한다면 인증서를 발급한다.

    사용자는 id와 주민등록번호를 입력한다.

    crt인증서와 개인키 파일이 C:/cert폴더에 생성된다.

    ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("C:\\\\cert\\\\temisone.key"));
    //비밀키 공개키 쌍 생성		
    KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
    		kpg.initialize(1024);
    		KeyPair mKeyPair = kpg.genKeyPair();
    		
    		os.writeObject(mKeyPair);
    		os.close();
    		
    	//crt인증서 생성 공급자는 temisone이다.	
    		X509V3CertificateGenerator v3CertGen = new X509V3CertificateGenerator();
    		v3CertGen.setSerialNumber(new BigInteger("1234"));
    		v3CertGen.setIssuerDN(new X509Principal("CN=temisone, OU=temisone, O=Company, L=Seoul, C=KR"));
    		v3CertGen.setNotBefore(new Date());
    		v3CertGen.setNotAfter(new Date(System.currentTimeMillis()+1000L*60*60*24*365*10));
    		v3CertGen.setSubjectDN(new X509Principal("CN=temisone, OU=temisone, O=Company, L=Seoul, C=KR"));
    		v3CertGen.setPublicKey(mKeyPair.getPublic());//키쌍의 공개키를 인증서에 저장한다.
    		v3CertGen.setSignatureAlgorithm("SHA1withRSA");
    		//개인키로 인증서를 증명할 수 있도록 한다.
    		X509Certificate pKCertificate = v3CertGen.generate(mKeyPair.getPrivate());
    		//로컬에 인증서와 키쌍을 저장한다.
    		FileOutputStream fos = new FileOutputStream("C:\\\\cert\\\\temisone.crt");
    		fos.write(pKCertificate.getEncoded());
    		fos.close();
    		System.out.println("키저장완료");
    	
    

    3. 에러 처리

    1. 인증서가 이미 존재한다면
      1. 이미 인증서가 존재한다는 알림을 출력한다.
    2. 회원이 아니라면
      1. 아이디가 존재하지 않는다고 알림을 출력한다.

     

    서버에 인증서 등록하기

    현재까지 찾아본 바로는 수동으로 등록하는 방법뿐이었다.

    • java

    jdk나 jre는 keystore을 자체적으로 관리해서 인증서를 keystore에 등록할 수 있다. 경로는 jre/library/security/cacerts다.

    docker 명령어로 키파일을 저장시킨다.

    keytool -import -keystore [키스토어 경로] -file [키파일 경로]
    

    tomcat서버에 keystore를 등록해준다.

    server.xml

    <Connector port="443" protocol="org.apache.coyote.http11.Http11NioProtocol"
               maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
               defaultSSLHostConfigName="localhost">
        <SSLHostConfig hostName="localhost" protocols="TLSv1.2,+TLSv1.1,+TLSv1">
            <Certificate certificateKeystoreFile="C:\\java_hyj\\jre\\bin\\temisoneKeyfile"
                         type="RSA" certificateKeystorePassword="temisone1245"/>
        </SSLHostConfig>
    </Connector>
    

    이렇게 하면 인증서를 가지고 https접속이 된다.

    동적으로 키파일이 keystore에 존재하는지 검증할 수 있다.

    public class HttpsURLConnection {
    	static String urlString= "<https://localhost:443>";
    	static String line = null;
    	static InputStream in = null;
    	static BufferedReader reader = null;
    	static javax.net.ssl.HttpsURLConnection httpsConn = null;
    	public HttpsURLConnection() throws IOException, NoSuchAlgorithmException, KeyManagementException, KeyStoreException, CertificateException, UnrecoverableKeyException {
    		URL url = new URL(urlString);
    		httpsConn = (javax.net.ssl.HttpsURLConnection) url.openConnection();
    		
    		// Set Hostname verification
    		httpsConn.setHostnameVerifier(new HostnameVerifier() {
    			@Override
    			public boolean verify(String hostname, SSLSession session) {
    				// Ignore host name verification. It always returns true.
    				return true;
    			}
    		});
    		
    		// Input setting
    		httpsConn.setDoInput(true);
    		// Output setting
    		//httpsConn.setDoOutput(true);
    		// Caches setting
    		httpsConn.setUseCaches(false);
    		// Read Timeout Setting
    		httpsConn.setReadTimeout(1000);
    		// Connection Timeout setting
    		httpsConn.setConnectTimeout(1000);
    		// Method Setting(GET/POST)
    		httpsConn.setRequestMethod("GET");
    		// Header Setting
    		httpsConn.setRequestProperty("HeaderKey","HeaderValue");
    //로컬의 키파일을 가져온다.
    		KeyStore clientStore = KeyStore.getInstance("jks");
    		clientStore.load(new FileInputStream("C:/cert/temisone.crt"),"temisone1245".toCharArray());
    		
    		KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    		kmf.init(clientStore,"temisone1245".toCharArray());
    		KeyManager[] kms = kmf.getKeyManagers();
    	//서버의 키스토어를 가져온다.	
    		KeyStore trustStore = KeyStore.getInstance("jks");
    		trustStore.load(new FileInputStream("C:/java_hyj/jre/lib/security/cacerts"),"temisone1245".toCharArray() );
    	//사설 인증서를 인증해주지 않아서 무조건 인증을 사용한다.	
    		TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    		tmf.init(trustStore);
    		TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
    			public java.security.cert.X509Certificate[] getAcceptedIssuers() {
    				return null;
    			}
    
    			public void checkClientTrusted(X509Certificate[] certs, String authType){
    			}
    
    			public void checkServerTrusted(X509Certificate[] certs, String authType) {
    			}
    		} };
    
    		
    		SSLContext sslContext = null;
    		sslContext = SSLContext.getInstance("TLS");
    		sslContext.init(kms, trustAllCerts,new SecureRandom());
    		
    		httpsConn.setSSLSocketFactory(sslContext.getSocketFactory());
    		System.out.println("끝");
    		
    		httpsConn.connect();
    		httpsConn.setInstanceFollowRedirects(false);
    	}
    	
    }
    
    • react

    react도 자체적인 서버를 가지기 때문에 https인증을 해주어야 한다.

    mkcert를 사용하여 key를 등록해준다

    mkcert -key-file [개인키 경로] -cert-file [인증서 경로]
    

    package.json에 사용할 인증서를 등록해준다.

    "scripts": {
        "start": "cross-env HTTPS=ture SSL_CRT_FILE=C:/cert/temisone.crt react-scripts start",
        "build": "react-scripts build",
        "test": "react-scripts test",
        "eject": "react-scripts eject"
      },
    

    아쉬운점

    자체적으로 발급한 인증서라 두가지 방법을 사용해도 제대로 인증이 되지 않는다.

    시연 영상

    댓글

Designed by Tistory.