영혼 없이 Windows용 Tomcat에 Let’s Encrypt SSL 인증서 설치하기

Let’s Encrypt 인증서를 설치하려고 검색을 해보니 리눅스 환경에서 certbot을 활용하는 방법이 주로 나왔습니다. 윈도 환경에서는 IIS에 적용하는 방법은 종종 찾을 수 있었지만 Tomcat에 적용하는 방법은 찾기 어려웠습니다. 아무래도 윈도에서 Tomcat으로 서버를 운영하는 사람이 많지 않아서 그런 것 같습니다. 누군가에게 티끌만큼이라도 도움이 되었으면 하는 마음으로 제가 삽질한 내용을 정리해 보았습니다.

준비하기

우선 필요한 파일을 다운로드하고 설치합니다.

(1) Java와 OpenSSL을 설치하고 Path 환경변수에 추가합니다. Path 환경변수에 꼭 넣을 필요는 없지만 넣어두는 것이 정신 건강에 좋습니다. Windows용 OpenSSL은 아래 링크에서 3MB짜리 Light 버전으로 다운로드 받으면 됩니다.
https://slproweb.com/products/Win32OpenSSL.html

(2) PJAC (Porunov Java ACME Client)를 다운로드 합니다. 참고로, 이 글을 쓰는 현재 최신 버전은 v3.0.0 입니다.
https://github.com/porunov/acme_client/releases

(3) 편의를 위해 C 드라이브 루트에 cert 폴더를 만듭니다. 그리고 다운로드 받은 acme_client.jar 파일을 C:\cert에 복사합니다.

인증서 발급받기

이제 인증서를 발급받기 위해 영혼 없이 아래 명령어를 순서대로 실행하면 됩니다. 아래 명령어 중에서 한글로 적힌 부분은 각자에게 맞는 내용으로 변경하여야 합니다.

(1) OpenSSL을 이용하여 CA user account key를 생성합니다.

openssl genrsa -out "C:\cert\account.key" 2048

(2) private domain key를 생성합니다.

openssl genrsa -out "C:\cert\도메인.key" 2048

(3) CSR (Certificate Signing Request)를 생성합니다.

openssl req -new -key "C:\cert\도메인.key" -sha256 -nodes -subj "/C=KR/ST=Seoul/L=Seoul/O=회사명/OU=부서명/CN=도메인/emailAddress=이메일" -outform PEM -out "C:\cert\도메인.csr"

(4) PJAC를 이용하여 CA user account를 등록합니다. 여기부터는 C:\cert 폴더로 이동 후 실행하여야 합니다.

java -jar acme_client.jar --command register -a "C:\cert\account.key" --with-agreement-update --email 이메일

(5) 인증서를 요청하고 http-01 challenge 파일을 다운로드 받습니다. http-01 challenge가 무엇인지 궁금하시면 구글에서 검색해 보세요.

java -jar acme_client.jar --command order-certificate -a "C:\cert\account.key" -w "C:\cert" -c "C:\cert\도메인.csr" --well-known-dir "C:\cert" --one-dir-for-well-known

(6) C:\cert 폴더를 열어보면 잡다하게 긴 이름의 파일이 하나 만들어져 있을 것입니다. 그게 http-01 challenge 파일입니다. 그 파일을 Tomcat 설치 경로 하위에 있는 webapps\ROOT\.well-known\acme-challenge 폴더에 복사합니다. 윈도 탐색기에서 “.well-known” 폴더를 만들려고 하면 에러 메시지가 뜨면서 만들어지지 않을 것입니다. 그러면 “.well-known.”과 같이 끝에도 점을 하나 더 찍으면 폴더가 만들어 집니다. Tomcat은 80 포트로 HTTP Connector를 설정하여 아래와 같은 URL로 접속 가능해야 합니다.

http://도메인/.well-known/acme-challenge/잡다구리파일명

(7) 다시 C:\cert 폴더로 돌아와서, PJAC를 이용하여 challenge 파일을 검증합니다.

java -jar acme_client.jar --command verify-domains -a "C:\cert\account.key" -w "C:\cert" -c "C:\cert\도메인.csr"

(8) 검증이 완료되면 인증서를 생성 및 다운로드합니다.

java -jar acme_client.jar --command generate-certificate -a "C:\cert\account.key" -w "C:\cert" -c "C:\cert\도메인.csr" --cert-dir "C:\cert"

(9) 이제 C:\cert 폴더에 세 개의 PEM 파일이 생긴 것을 확인할 수 있을 것입니다. (cert.pem, chain.pem, fullchain.pem)

(10) Tomcat에서 사용하기 위해 인증서 파일을 변환합니다.

openssl pkcs12 -export -in "C:\cert\fullchain.pem" -inkey "C:\cert\도메인.key" -out "C:\cert\keystore.p12" -name tomcat -CAfile "C:\cert\chain.pem" -caname root -password pass:패스워드

(11) 마지막으로, Tomcat에 SSL 인증서를 적용하기 위해 Tomcat의 conf 폴더에 있는 server.xml에 아래와 같이 Connector를 설정합니다.

  • Tomcat 8.5 이상
<Connector port="443" protocol="org.apache.coyote.http11.Http11NioProtocol"
	maxThreads="150" SSLEnabled="true">
	<SSLHostConfig>
		<Certificate certificateKeystoreFile="C:\cert\keystore.p12"
			certificateKeystorePassword="패스워드"
			certificateKeystoreType ="pkcs12" type="RSA" />
	</SSLHostConfig>
</Connector>
  • Tomcat 8 이하
<Connector port="443" protocol="org.apache.coyote.http11.Http11NioProtocol"
	maxThreads="150" SSLEnabled="true"
	keystoreFile="C:\cert\keystore.p12" keystorePass="패스워드" keystoreType="pkcs12">

인증서 갱신하기

Let’s Encrypt 인증서의 유효기간은 90일이기 때문에 계속 유지하려면 주기적으로 인증서를 재발급 받아야 합니다. 만료일이 가까워지면 위의 (4)번에서 입력한 이메일 주소로 알림 메일이 옵니다. 그러면 위의 (5)번부터 (10)번까지 순서대로 다시 실행하고 Tomcat을 재시작하면 됩니다.

34 Replies to “영혼 없이 Windows용 Tomcat에 Let’s Encrypt SSL 인증서 설치하기”

  1. 안녕하세요. 초보개발자입니다.
    정리해주신 자료 잘 보고 따라해봤는데요.
    일단 잡다구리한걸 root에 넣고 확인까지 했는데요. 다만 80포트가 아닌 다른 포트를 써야되서요.
    혹시 다른포트로 지정할땐 해결방법이 없는건가해서요.

    1. Let’s Encrypt 서버에서 인증서 발급 전에 정상 도메인인지 여부를 확인하기 위해서 해당 도메인으로 연결을 시도합니다. 이 때 80포트로만 연결을 시도하기 때문에 다른 포트를 사용할 수는 없습니다. 80포트를 열어서 인증서를 발급하신 후 원하시는 포트로 변경하는 것을 추천드립니다.

  2. 안녕하세요
    블로그가 많이 도움이 되고있는데ㅛ

    지금 keystore.p12 파일까지 생성이 완료되어
    server.xml에서 설정까지 해주었는데

    https://로 접근시 아이디와 비밀번호를 입력하라고 웹상에서 뜨는데
    이건 어떤이유에서 인지 알수 있을까요?

    좋은자료 감사합니다.

    1. HTTPS로 접근했다고 해서 아이디와 비밀번호를 추가로 요구하지는 않습니다. 아마 그러한 기능을 구현하셨거나 사용하시는 프레임워크에서 기본적으로 제공되는 기능이 아닐까 생각됩니다. 혹시 HTTP로 접근했을 때에는 동일한 증상이 나타나지 않나요?

  3. 안녕하세요!! 자료 감사합니당~
    tomcat root가 지금 WEB-INF 이라 여기 ./well-known/acme-challenge/잡다구리파일 넣었습니다. 그런데 (7)에서 계속 {“failed_domains”:[“도메인”],”status”:”error”} 이 계속 뜨네요 ㅠ
    80포트로 열어놓고 NAT로 연결시켜서 외부에서 사이트 접속은 되는거까지 확인 했습니다…
    어디가 혹시 원인일까여ㅠㅠ;;
    감사합니다!

    1. 아 다시보시 파일을 잘못넣어서 WEB-INF가 아니고 D:\apache-tomcat-7.0.96\webapps\ROOT\.well-known\acme-challenge\잡다구리 넣었습니다.그래도 안되네영 ㅠㅠ에고…

    1. ROOT 폴더에 일반 텍스트 파일을 넣고 URL로 접근 가능한지 확인해 보시기 바랍니다. coo.kr 홈페이지에는 단순 포워딩 서비스만 지원한다고 적혀 있어서 URL 제약이 있을 수 있습니다.

      1. 네, 그냥txt파일만들어서 해봤는데 coo.kr로 가는군요 … ㅠㅠ
        그럼 coo.kr에서 호스팅 받은건 안된다는 말씀이시져?

        1. 지금까지 말씀하신 내용을 보면 안타깝게도 coo.kr 도메인으로는 Let’s encrypt 인증서를 사용하실 수 없을 것 같습니다.

          1. 앗!감사합니다.그래서 유료호스팅으로 결제해서 해보니 한발짝? 은 나간거같은데 바로 다른에러가 떳습니다. 일반txt문서를 만들어서도 해보니 아래와 같은… 경로가 안맞는거같은데 아무리 해도 안되네요 …
            Not Found
            The requested URL /.well-known/acme-challenge/khk.txt was not found on this server.
            Apache/1.3.33 Server at ?? Port 80

            아래는 server.xml 에 내용인데요 ㅠ 톰캣Root/에 넣어도 안되고, webapps/에 넣어도 없다고하고 … 흐엉… 왜이럴까여 …

  4. 안녕하세요 좋은 글을 보고 잘 설치해서 잘 쓰고 있었습니다. 곧 90일이 되서 갱신을 하려고 하는데로 글 맨 아래 5번 부터 10번까지 재실행하는데 7번 실행 후
    Exception in thread “main”org.shredzone.acme4j.exception.AcmeLazyLoadingException: Order https://acme-v02.api.letsencrypt.org/acme/order/84571349/3148922544
    at org.shredzone.acme4j.AcmeJsonResource.getJSON(AcmeJsonResource.java:68)
    at org.shredzone.acme4j.Order.getAuthorizations(Order.java:107)
    at com.jblur.acme_client.command.certificate.VerifyDomainsCommand.commandExecution(VerifyDomainsCommand.java:51)
    at com.jblur.acme_client.command.ACMECommand.execute(ACMECommand.java:47)
    at com.jblur.acme_client.CommandExecutor.executeACMECommand(CommandExecutor.java:123)
    at com.jblur.acme_client.CommandExecutor.execute(CommandExecutor.java:74)
    at com.jblur.acme_client.Application.main(Application.java:65)
    Caused by: org.shredzone.acme4j.exception.AcmeServerException: No order for ID 3148922544
    at org.shredzone.acme4j.connector.DefaultConnection.throwAcmeException(DefaultConnection.java:490)
    at org.shredzone.acme4j.connector.DefaultConnection.performRequest(DefaultConnection.java:417)
    at org.shredzone.acme4j.connector.DefaultConnection.sendSignedRequest(DefaultConnection.java:346)
    at org.shredzone.acme4j.connector.DefaultConnection.sendSignedPostAsGetRequest(DefaultConnection.java:147)
    at org.shredzone.acme4j.AcmeJsonResource.update(AcmeJsonResource.java:119)
    at org.shredzone.acme4j.AcmeJsonResource.getJSON(AcmeJsonResource.java:63)
    … 6 more

    액셉션이 발생하면서 진행이 안되네요. 도움 좀 부탁 드립니다.

    1. 저도 비슷한 경험을 겪은 적이 있습니다.
      폴더에서 certificate_uri_list, order_uri_list 파일을 삭제하고 5번부터 다시 해보시면 에러없이 동작할 겁니다.

  5. 안녕하세요 글 덕분에 많은 도움 받았습니다.
    다름이 아니라 인증서 기간이 만료 되어서 재발급 받으려는데 6번 이용해서 URL로 접근까지 잘 되는데 7번에서 계속 {“failed_domains”:[“도메인”],”status”:”error”} 이런 에러가 뜨면서 진행이 안되네요 혹시 원인을 알 수 있을까요?

    1. 7번 과정에서 에러가 난다면 윗 분과 동일한 원인이 아닐까 생각됩니다.
      폴더에서 certificate_uri_list, order_uri_list 파일을 삭제하고 5번부터 다시 해보시기 바랍니다.

  6. 안녕하세요. 1~5번까지 진행하던도중
    5번에서 아래와 같이 입력하니
    java -jar acme_client.jar –command order-certificate -a “C:\cert\account.key” -w “C:\cert” -c “C:\cert\192.168.0.35.csr” –well-known-dir “C:\cert” –one-dir-for-well-known
    {“status”:”error”}
    라고 나타납니다. 처리방법이 있을까요 ?

    전체 커맨드는 아래와 같습니다
    cmd 관리자모드로 킨 후

    cd C:\Program Files\OpenSSL-Win64\bin

    openssl genrsa -out “C:\cert\account.key” 2048

    openssl genrsa -out “C:\cert\192.168.0.35.key” 2048

    openssl req -new -key “C:\cert\192.168.0.35.key” -sha256 -nodes -subj “/C=KR/ST=Seoul/L=Seoul/O=samocns/OU=develop/CN=192.168.0.35/emailAddress=이메일” -outform PEM -out “C:\cert\192.168.0.35.csr”

    cd C:\cert

    java -jar acme_client.jar –command register -a “C:\cert\account.key” –with-agreement-update –email 이메일

    java -jar acme_client.jar –command order-certificate -a “C:\cert\account.key” -w “C:\cert” -c “C:\cert\192.168.0.35.csr” –well-known-dir “C:\cert” –one-dir-for-well-known

    답변 부탁드립니다 ㅠㅠ

    1. 인증서는 도메인이 있어야 발급 받을 수 있습니다.
      아무래도 IP로 인증서 발급을 시도하셔서 에러가 발생한 것으로 생각됩니다.

  7. 안녕하세요.

    인증서까지 전부 성공하고 server.xml 까지 설정한 후
    톰캣 재시작 했는데 https 접속이 되지 않습니다..

    윈도우 서버를 사용중이고,
    혹시 도메인 사이트에서 추가적으로 작업해야할 것이 있나요?

    1. 웹 브라우저에서 에러가 보이는 것이 아니라 접속 자체가 되지 않는다는 말씀이시죠?
      그렇다면 방화벽에서 HTTPS(443) 포트가 열려있는지 확인해 보시는 게 좋겠습니다.

  8. 현재 iis+톰캣으로 운영중입니다.
    하나의 톰캣에 15개정도의 도메인으로 운영중이며 이중 1개의 사이트에만 ssl인증서를 설치하였습니다.
    iis용 ssl인증서를 구입하고 설치한 상태입니다.

    하여
    https://offbyone.tistory.com/322
    이 사이트를 보고 시도 해 보았고

    기존

    ==>

    게 해보고…하였으나 되지가 않습니다.

    1. 말씀하신 내용으로는 무엇이 안되는지, 어떤 부분을 도와드릴 수 있는지 파악하기 어렵습니다. 좀 더 상세히 설명해 주시면 제가 알고 있는 범위에서 최대한 답변드리도록 하겠습니다.

  9. 인증서 설치방법 저세히 적어주셔서 감사합니다

    그런데 이게 특정브라우저(네이버앱이나 몇몇 브라우저)에선 신뢰하지않는다고 나오는데
    이부분은 해결방법이 있을까 싶어 문의드립니다

    1. Let’s Encrypt 인증서는 최신 브라우저에서 대부분 정상적으로 동작합니다. 제가 사용하고 있는 다른 사이트에 네이버앱으로 접속하였을 때에도 문제없이 표시되었습니다. 인증서가 정상적으로 설치되었는지 다시 한번 확인해 보시고 혹시 웹페이지 내에서 http로 통신하는 부분은 없는지 확인해 보시면 좋겠습니다.

      1. 찾아보면 캐쉬삭제후 재접속해보면 된다고 하여
        저도 이렇게 해보니 다시 자물쇠표시가 정상적으로 나옵니다만
        고객들한테 저렇게 할순없어서.. 고민이네요
        일단은 최신브라우저로 업데이트 하라고 해봐야겠네요
        감사합니다!

  10. 메뉴얼 보니 직접 경로를 지정 해도 되네요. (5)번에서
    –well-known-dir
    Directory to save challenge files to. All challenge files must be
    accessible from internet via link:
    http://${domain}/.well-known/acme-challenge/${token}, where ${token} is
    the name of the challenge file and ${domain} is the domain name the
    challenge file corresponds to.
    Default: /var/acme_work_dir/well_known/

  11. 2022-05-27 08:06:19 ERROR com.jblur.acme_client.command.certificate.OrderCertificateCommand:63 – Cannot authorize domains
    org.shredzone.acme4j.exception.AcmeRateLimitedException: Error creating new order :: too many certificates (5) already issued for this exact set of domains in the last 168 hours: vocalevel.com: see https://letsencrypt.org/docs/rate-limits/
    ….
    컴맨드로 인증절차를 실행하다가 인증이 막혔네요. 168시간 전에 이미 발급된 사이트이며 5번을 인증을 요청 했기에 한 시간 대기 타야 되네요. 참고가 되시면 좋겠습니다.

  12. 안녕하세요 문의드릴게 있어서요
    윈도우 서버를 사내에서 쓰고 있어서 도움받고 있숩니다

    (4) PJAC를 이용하여 CA user account를 등록합니다. 여기부터는 C:\cert 폴더로 이동 후 실행하여야 합니다. > 해당 부분에서 아래와 같은 에러를 만났는데요
    같은 경험이 있으신가요??

    c:\certi>java -jar acme_client.jar –command register -a “c:\certi\account.key” –with-agreement-update –email (사내 이메일 작성함)

    Exception in thread “main” java.lang.ClassCastException: org.bouncycastle.asn1.pkcs.PrivateKeyInfo cannot be cast to org.bouncycastle.openssl.PEMKeyPair
    at org.shredzone.acme4j.util.KeyPairUtils.readKeyPair(KeyPairUtils.java:96)
    at com.jblur.acme_client.IOManager.readKeyPairFromPrivateKey(IOManager.java:30)
    at com.jblur.acme_client.command.AuthorizedCommand.getAccountKey(AuthorizedCommand.java:28)
    at com.jblur.acme_client.command.AuthorizedCommand.(AuthorizedCommand.java:23)
    at com.jblur.acme_client.command.registration.RegistrationCommand.(RegistrationCommand.java:16)
    at com.jblur.acme_client.CommandExecutor.getAccountManager(CommandExecutor.java:108)
    at com.jblur.acme_client.CommandExecutor.execute(CommandExecutor.java:46)
    at com.jblur.acme_client.Application.main(Application.java:65)

    1. 에러 내용을 보면 (1)번 단계에서 만든 키 파일의 문제로 생각됩니다. 다만, 이 글이 3년 전에 작성된 것이어서 openssl이나 java 버전의 변화로 발생한 에러일 수도 있습니다.
      (1)번 단계부터 찬찬히 다시 해보시고 동일한 에러가 발생하면 구글에서 다른 최신 글을 찾아보실 것을 권장드립니다.

Leave a Reply

Your email address will not be published. Required fields are marked *