[Keycloak] Keycloak 암호화 적용하기

Posted by 김성철

keycloak - 암호화 적용

[RSA 방식의 암호화]

https://github.com/l10178/keycloak-encryption-provider  

버전 수정

build.gradle 파일에서 아래의 내용 수정  
sourceCompatibility -> 사용하려는 java 버전  
keycloakVersion -> 사용하려는 keycloak 버전, 10 11 12까지 지원된다고 하나 13버전도 되는거같음  
=================================================================================================================  
sourceCompatibility = '11.0'  
tasks.withType(JavaCompile) {  
	options.encoding = 'UTF-8'  
}  
  
ext {  
	keycloakVersion = '12.0.3'  
}  
=================================================================================================================  

JAVA 빌드

cd password-encryption-provider  
./gradlew shadowJar  
  
빌드 후 "KEYCLOAK_HOME/standalone/deployments/" 경로에 복사  

JS 빌드

cd password-encryption-provider-js  
npm ci && npm run build  
  
빌드 후 "KEYCLOAK_HOME/themes/base/login/resources/js/" 경로에 복사  

JS 파일 설정

"KEYCLOAK_HOME/themes/base/login/theme.properties" 파일에 아래의 내용 추가  
=================================================================================================================  
scripts=js/password-encryption-provider.js  
=================================================================================================================  

[ AES 암호화 ]

참고 링크 :  
	https://edwin.baculsoft.com/2021/03/how-to-encrypt-and-decrypt-password-on-keycloak-or-red-hat-sso/  
	https://github.com/edwin/keycloak-password-encryptor  

프로젝트 생성

1. File -> New -> Project -> Maven  
  
2. 정보 입력  
	Group ID : com.sungchul  
	ArtifactId : KeycloakPasswordEncryptor  
	Project name : KeycloakPasswordEncryptor  

Pom.xml 작성

위의 참고링크의 내용  
현재 진행중인 프로젝트 내용  
내가 사용중인 keycloak 의 버전을 참고하여 작성하였음  
https://github.com/kkimsungchul/study/blob/master/Keycloak/KeycloakPasswordEncryptor/pom.xml  

※ 아래의 파일들의 내용은 링크에서 확인이 가능함.
※ 프로젝트 파일URL : https://github.com/kkimsungchul/study/tree/master/Keycloak/KeycloakPasswordEncryptor

CustomKeycloakPasswordEncryptor.java 파일 생성

경로 : src\main\java\com\sungchul\keycloak\spi\  
파일명 : CustomKeycloakPasswordEncryptor.java  
URL : https://github.com/kkimsungchul/study/blob/master/Keycloak/KeycloakPasswordEncryptor/src/main/java/com/sungchul/keycloak/spi/CustomKeycloakPasswordEncryptor.java  

CustomKeycloakPasswordEncryptorFactory.java 파일 생성

경로 : src\main\java\com\sungchul\keycloak\spi\  
파일명 : CustomKeycloakPasswordEncryptorFactory.java  
URL : https://github.com/kkimsungchul/study/blob/master/Keycloak/KeycloakPasswordEncryptor/src/main/java/com/sungchul/keycloak/spi/CustomKeycloakPasswordEncryptorFactory.java  

EncryptionHelper.java 파일 생성

Base64 를 사용하였음,  
테스트를 위해서 Base64를 사용한것이고 실제로 사용할때는 암복호화가 가능한 AES256 을 사용할 예정임  
  
경로 : src\main\java\com\sungchul\keycloak\spi\helper\  
파일명 : EncryptionHelper.java  
URL : https://github.com/kkimsungchul/study/blob/master/Keycloak/KeycloakPasswordEncryptor/src/main/java/com/sungchul/keycloak/spi/helper/EncryptionHelper.java  

org.keycloak.authentication.AuthenticatorFactory 파일 생성

경로 : src\main\resources\META-INF\services\  
파일명 : org.keycloak.authentication.AuthenticatorFactory  
URL : https://github.com/kkimsungchul/study/blob/master/Keycloak/KeycloakPasswordEncryptor/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory  

Build

※ IntelliJ 에서 테스트 및 개발을 진행하였으므로 IntelliJ 기준으로 작성함  
※ 이클립스의 경우에는 maven install 을 사용하면 됨  
Project Structure  
-> Artifacts  
-> + 버튼 클릭  
-> Jar 선택  
-> KeycloakPasswordEncryptor 선택  
-> Create JAR From Mudules 창이 뜨면 모듈에 "KeycloakPasswordEncryptor" 로 선택되어 있는 것을 확인 후 OK 버튼 클릭  

JAR 파일을 keycloak의 모듈배포폴더로 이동

※ 본인은 윈도우에서 테스트를 진행하였으므로 윈도우 기준으로 작성  
  
경로 : keycloak-16.1.1\standalone\deployments  
	ex.실제경로) C:\Users\USER\Desktop\kimsc\개발\keycloak-16.1.1\keycloak-16.1.1\standalone\deployments  
  
해당 위치에 KeycloakPasswordEncryptor.jar 파일을 복사  

keyCloak 실행 및 세팅

1. 설치되어 있는 keycloak 를 실행  
	참고링크 : https://github.com/kkimsungchul/study/blob/master/Keycloak/%5BKeycloak%5D%20%EC%84%A4%EC%B9%98%20%EB%B0%8F%20%EC%84%B8%ED%8C%85.txt  
  
2. http://localhost:8080/auth/ 접속 후 로그인  
  
3. 적용할 realm 을 선택  
	본인은 Demo라는 이름으로 생성한 realm 을 선택함  
  
4. 좌측 Configure 탭의 Authentication 클릭  
  
5. Authentication 화면에서 New 버튼을 클릭  
  
6. Create Top Level Form 화면에서 아래의 정보를 입력  
	Alias : Decrypt Password  
	Description : Decrypt Password  
	Top Level Flow : generic  
  
7. 다시 Authentication 화면으로 돌아와서 셀렉트 박스에서 좀전에 생성한 "Decrypt Password" 선택  
  
8. Add execution 클릭  
  
9. Provider 셀렉트 박스에서 "Simple Password Encryption" 선택 후 save  
	Simple Password Encryption 이름은 "CustomKeycloakPasswordEncryptorFactory.java" 파일에서 지정해준 이름  
	해당이름이 표시되지 않는다면 아래의 내용들을 확인할것  
		- 생성한 파일들의 파일명은 제대로 되어 있는가  
		- 생성한 파일들의 경로들은 제대로 되어 있는가  
		- pom.xml 의 라이브러리들이 제대로 추가 되었는가  
		- 오타는 없는가  
		※ 본인은 경로 틀려서 삽질함  
  
10. 생성이 완료되면  
	Authentication 화면에서 "Decrypt Password" 셀렉트 박스를 선택 하면 "Simple Password Encryption" 가 표시됨  
	Requirement 부분에서 "REQUIRED" 선택  
  
11. 적용할 client 의 setting 페이지의 제일 하단부분의 "Authentication Flow Overrides" 를 아래와 같이 설정  
	Browser Flow : browser  
	Direct Grant Flow : Decrypt password  
  
	메뉴위치 :  
		Configure  
		-> Clients  
		-> my_client (내가 생성 및 적용할 클라이언트)  
		-> settings  
		-> Authentication Flow Overrides  
  
12. 사용자 생성  
	이전에 생성해놓은 My_client 에서 사용자를 생성  
		참고링크 : https://github.com/kkimsungchul/study/blob/master/Keycloak/%5BKeycloak%5D%20%EC%84%A4%EC%B9%98%20%EB%B0%8F%20%EC%84%B8%ED%8C%85.txt  
		※ 이름이랑 비밀번호 설정페이지는 별도임 위의 링크 참고  
  
	username : sungchul  
	password : 1  

토큰 발급 테스트

CMD 창에서 아래의 명령어를 통해 토큰이 발급되는지 테스트  
  
1. 암호화 미적용  
	=================================================================================================================   curl -X POST "http://localhost:8080/auth/realms/demo/protocol/openid-connect/token" ^   --header "Content-Type:application/x-www-form-urlencoded" ^   --data-urlencode "grant_type=password" ^   --data-urlencode "client_id=my_client" ^   --data-urlencode "client_secret=JBFLaz1mgZKPwYNqE9Tfyf0c3b2mnkOl" ^   --data-urlencode "username=sungchul" ^   --data-urlencode "password=1"  
	=================================================================================================================  
  
	결과 : {"error":"invalid_request","error_description":"User Not Found"}  
  
2. 암호화 적용  
  
	=================================================================================================================   curl -X POST "http://localhost:8080/auth/realms/demo/protocol/openid-connect/token" ^   --header "Content-Type:application/x-www-form-urlencoded" ^   --data-urlencode "grant_type=password" ^   --data-urlencode "client_id=my_client" ^   --data-urlencode "client_secret=JBFLaz1mgZKPwYNqE9Tfyf0c3b2mnkOl" ^   --data-urlencode "username=sungchul" ^   --data-urlencode "password=MQ=="  
	=================================================================================================================  
  
	결과 :  
	{"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJPdGViQldzSmdhRWozeXVaMGZlbTVWclFmbkZpcDlLczE5aVN6anBybGw0In0.eyJleHAiOjE2NDQ1MjIzMDksImlhdCI6MTY0NDQ4NjMwOSwianRpIjoiYmM4ZGI5ZTAtYjZmYy00OWJlLWI0MjItZjBiZDcxNGQ2YzJiIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL2F1dGgvcmVhbG1zL2RlbW8iLCJhdWQiOiJhY2NvdW50Iiwic3ViIjoiNzNkNWYwOWUtZTY2Ny00YjA2LTgwZDctMTk3ZDM1NzQyNmQ0IiwidHlwIjoiQmVhcmVyIiwiYXpwIjoibXlfY2xpZW50Iiwic2Vzc2lvbl9zdGF0ZSI6ImIwNTVhMTRlLWY3N2QtNGVkYS04MTE1LWE2Y2NmZDFkNzRhMiIsImFjciI6IjEiLCJhbGxvd2VkLW9yaWdpbnMiOlsiaHR0cDovL2xvY2FsaG9zdDo4MDgwIl0sInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIiwiZGVmYXVsdC1yb2xlcy1kZW1vIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsibXlfY2xpZW50Ijp7InJvbGVzIjpbIlJPTEVfVVNFUiJdfSwiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJwcm9maWxlIGVtYWlsIiwic2lkIjoiYjA1NWExNGUtZjc3ZC00ZWRhLTgxMTUtYTZjY2ZkMWQ3NGEyIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJuYW1lIjoia2ltIHN1bmdjaHVsIiwicHJlZmVycmVkX3VzZXJuYW1lIjoic3VuZ2NodWwiLCJnaXZlbl9uYW1lIjoia2ltIiwiZmFtaWx5X25hbWUiOiJzdW5nY2h1bCIsImVtYWlsIjoia2ltc2MxMjE4QGdtYWlsLmNvbSJ9.QacHvhWS7myXfOgXhwtxLiyf7k4s7C_UowNgSxAipUxUBPa-2V5PHOatV68jqi1V9Np-Gbmj8iTlo4-PqTK3hImZRGx19YBM1hQ3uSGOQOg3QUVS9VMHIV8DBUl_bKfN92uQfg8b_Bqf4q9AT4ZNnvAk2yx5RlahDWi_qaH76qbECj04R6qrBJe9EgDJyxZB8xcMdT494TCMlCCyAV3rJ4KALDdBp7kSuQFlQHSLGY_Ti4K8y6TjWAE5jXpYRPUyq-27gymhhdrl9yktvb2LJluKgSjvDJ4tixMPYGOYTJWhqUTfWDfQncraGLd0vzJEev0GHb_aZ4mx67W_Ypz9pQ","expires_in":36000,"refresh_expires_in":1800,"refresh_token":"eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJhZDM2NmVmOS1kMTNmLTQyM2YtOTRhMS04OTI4M2YxN2JmNGUifQ.eyJleHAiOjE2NDQ0ODgxMDksImlhdCI6MTY0NDQ4NjMwOSwianRpIjoiMzdlNzM4OWUtMGYyNC00NTBkLTg2ZTUtYzYwZjNmOTlkN2E2IiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL2F1dGgvcmVhbG1zL2RlbW8iLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvZGVtbyIsInN1YiI6IjczZDVmMDllLWU2NjctNGIwNi04MGQ3LTE5N2QzNTc0MjZkNCIsInR5cCI6IlJlZnJlc2giLCJhenAiOiJteV9jbGllbnQiLCJzZXNzaW9uX3N0YXRlIjoiYjA1NWExNGUtZjc3ZC00ZWRhLTgxMTUtYTZjY2ZkMWQ3NGEyIiwic2NvcGUiOiJwcm9maWxlIGVtYWlsIiwic2lkIjoiYjA1NWExNGUtZjc3ZC00ZWRhLTgxMTUtYTZjY2ZkMWQ3NGEyIn0.RLVYh5a4yUpYtRmpfF-TTMUkJMQK2wK28MFU1gsaYLU","token_type":"Bearer","not-before-policy":0,"session_state":"b055a14e-f77d-4eda-8115-a6ccfd1d74a2","scope":"profile email"}  
  
	결과 확인  
		1. https://jwt.io/ 사이트 접속  
		2. access_token 의 내용을 붙여넣기  
		-결과값  
		=================================================================================================================  
		{  
			  "exp": 1644522309,  
			  "iat": 1644486309,  
			  "jti": "bc8db9e0-b6fc-49be-b422-f0bd714d6c2b",  
			  "iss": "http://localhost:8080/auth/realms/demo",  
			  "aud": "account",  
			  "sub": "73d5f09e-e667-4b06-80d7-197d357426d4",  
			  "typ": "Bearer",  
			  "azp": "my_client",  
			  "session_state": "b055a14e-f77d-4eda-8115-a6ccfd1d74a2",  
			  "acr": "1",  
			  "allowed-origins": [  
				"http://localhost:8080"  
			  ],  
			  "realm_access": {  
				"roles": [  
				  "offline_access",  
				  "uma_authorization",  
				  "default-roles-demo"  
				]  
			  },  
			  "resource_access": {  
				"my_client": {  
				  "roles": [  
					"ROLE_USER"  
				  ]  
				},  
				"account": {  
				  "roles": [  
					"manage-account",  
					"manage-account-links",  
					"view-profile"  
				  ]  
				}  
			  },  
			  "scope": "profile email",  
			  "sid": "b055a14e-f77d-4eda-8115-a6ccfd1d74a2",  
			  "email_verified": false,  
			  "name": "kim sungchul",  
			  "preferred_username": "sungchul",  
			  "given_name": "kim",  
			  "family_name": "sungchul",  
			  "email": "kimsc1218@gmail.com"  
			}  
  
		=================================================================================================================  

curl -X POST “http://localhost:8080/auth/realms/demo/protocol/openid-connect/token” ^
–header “Content-Type:application/x-www-form-urlencoded” ^
–data-urlencode “grant_type=password” ^
–data-urlencode “client_id=my_client” ^
–data-urlencode “client_secret=JBFLaz1mgZKPwYNqE9Tfyf0c3b2mnkOl” ^
–data-urlencode “username=sungchul” ^
–data-urlencode “password=MQ==”