package com.eactive.eai.authserver.custom; import java.nio.charset.StandardCharsets; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.Principal; import java.security.PublicKey; import java.security.SecureRandom; import java.security.Signature; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; import java.util.HashMap; import java.util.HashSet; import java.util.Set; import javax.crypto.Cipher; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.common.util.OAuth2Utils; import org.springframework.security.oauth2.provider.ClientDetails; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.OAuth2Request; import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import com.eactive.eai.adapter.http.dynamic.filter.JwtAuthException; import com.eactive.eai.authserver.service.OAuth2Manager; import com.eactive.eai.authserver.util.BeanUtils; import com.eactive.eai.authserver.vo.ClientVO; import com.eactive.eai.common.util.Logger; @Controller public class SnapOAuth2Controller { public static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT); private static final String HEADER_TIMESTAMP = "X-TIMESTAMP"; private static final String HEADER_CLIENT_ID = "X-CLIENT-KEY"; private static final String HEADER_SIGNATURE = "X-SIGNATURE"; private static final String SERVICE_CODE_ACCESS_TOKEN = "00"; // 임시설정 @RequestMapping(value = "/bukopin_snap_api/v1.0/access-token/b2b", method = RequestMethod.POST, produces = "application/json; charset=\"UTF-8\"") @ResponseBody public SnapOAuth2AccessTokenResponse token(@RequestBody SnapOAuth2AccessTokenRequest tokenRequest, HttpServletRequest request, HttpServletResponse response) { if (logger.isDebug()) { logger.debug(tokenRequest.toString()); } SnapOAuth2AccessTokenResponse responseToken = new SnapOAuth2AccessTokenResponse(); try { if (!StringUtils.equals(tokenRequest.getGrantType(), "client_credentials")) { throw new JwtAuthException( String.format("%d%s%s", HttpStatus.BAD_REQUEST.value(), SERVICE_CODE_ACCESS_TOKEN, "02"), "Invalid Mandatory Field {grantType}"); } String timeStamp = request.getHeader(HEADER_TIMESTAMP); String clientId = request.getHeader(HEADER_CLIENT_ID); String signature = request.getHeader(HEADER_SIGNATURE); ClientDetails clientDetails = OAuth2Manager.getInstance().getClientDeatilsStore().get(clientId); verifyClient(clientDetails, timeStamp, clientId, signature); response.setHeader(HEADER_TIMESTAMP, timeStamp); response.setHeader(HEADER_CLIENT_ID, clientId); HashMap authorizationParameters = new HashMap(); authorizationParameters.put(OAuth2Utils.GRANT_TYPE, tokenRequest.getGrantType()); authorizationParameters.put(OAuth2Utils.CLIENT_ID, clientId); authorizationParameters.put("client_secret", clientDetails.getClientSecret()); Set responseType = new HashSet(); responseType.add(tokenRequest.getGrantType()); OAuth2Request authorizationRequest = new OAuth2Request(authorizationParameters, clientId, null, true, null, null, "", responseType, null); Principal principal = new OAuth2Authentication(authorizationRequest, null); ResponseEntity result = tokenEndpoint().postAccessToken(principal, authorizationParameters); OAuth2AccessToken token = result.getBody(); responseToken.setAccessToken(token.getValue()); responseToken.setExpiresIn(String.valueOf(token.getExpiresIn())); responseToken.setTokenType(token.getTokenType()); } catch (JwtAuthException e) { response.setStatus(NumberUtils.toInt(StringUtils.left(e.getCode(), 3), HttpStatus.UNAUTHORIZED.value())); responseToken.setResponseCode(e.getCode()); responseToken.setResponseMessage(e.getMessage()); } catch (Exception e) { logger.error(e.getMessage()); response.setStatus(HttpStatus.UNAUTHORIZED.value()); responseToken.setResponseCode( String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_ACCESS_TOKEN, "00")); responseToken.setResponseMessage("Unauthorized. [Unknown]"); } return responseToken; } private void verifyClient(ClientDetails clientDetails, String timeStamp, String clientId, String signatureStr) throws JwtAuthException { if (StringUtils.isBlank(timeStamp)) { throw new JwtAuthException( String.format("%d%s%s", HttpStatus.BAD_REQUEST.value(), SERVICE_CODE_ACCESS_TOKEN, "02"), "Invalid Mandatory Field {" + HEADER_TIMESTAMP + "}"); } if (StringUtils.isBlank(clientId)) { throw new JwtAuthException( String.format("%d%s%s", HttpStatus.BAD_REQUEST.value(), SERVICE_CODE_ACCESS_TOKEN, "02"), "Invalid Mandatory Field {" + HEADER_CLIENT_ID + "}"); } if (StringUtils.isBlank(signatureStr)) { throw new JwtAuthException( String.format("%d%s%s", HttpStatus.BAD_REQUEST.value(), SERVICE_CODE_ACCESS_TOKEN, "02"), "Invalid Mandatory Field {" + HEADER_SIGNATURE + "}"); } if (clientDetails == null) { throw new JwtAuthException( String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_ACCESS_TOKEN, "00"), "Unauthorized. [client not found]"); } String publicKeyStr = ((ClientVO) clientDetails).getSecurityKey(); if (StringUtils.isBlank(publicKeyStr)) { throw new JwtAuthException( String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_ACCESS_TOKEN, "00"), "Unauthorized. [public key not found]"); } try { String message = clientId + "|" + timeStamp; X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyStr)); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PublicKey publicKey = keyFactory.generatePublic(keySpec); Signature signature = Signature.getInstance("SHA256withRSA"); signature.initVerify(publicKey); signature.update(message.getBytes(StandardCharsets.UTF_8)); if (!signature.verify(Base64.getDecoder().decode(signatureStr))) { throw new JwtAuthException( String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_ACCESS_TOKEN, "00"), "Unauthorized. [Signature not matched]"); } } catch (JwtAuthException e) { throw e; } catch (Exception e) { logger.error(e.getMessage()); throw new JwtAuthException( String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_ACCESS_TOKEN, "00"), "Unauthorized. [Signature fail]"); } } private TokenEndpoint tokenEndpoint() { return BeanUtils.getBean("tokenEndpoint", TokenEndpoint.class); } public static void main(String[] args) throws Exception { // First generate a public/private key pair KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA"); // generator.initialize(512, new SecureRandom()); //generator.initialize(1024, new SecureRandom()); generator.initialize(2048, new SecureRandom()); KeyPair pair = generator.generateKeyPair(); // The private key can be used to sign (not encrypt!) a message. The public key // holder can then verify the message. String message = "sSGJDwlJsel5WeXodzd3J3MQ20KYCZpL|2022-12-21T14:36:19+07:00"; // Let's sign our message Signature privateSignature = Signature.getInstance("SHA256withRSA"); privateSignature.initSign(pair.getPrivate()); privateSignature.update(message.getBytes(StandardCharsets.UTF_8)); System.out.println( "private key=" + Base64.getEncoder().encodeToString(pair.getPrivate().getEncoded())); byte[] signature = privateSignature.sign(); //System.out.println("signature=" + new String(signature, StandardCharsets.UTF_8)); System.out.println("signature=" + Base64.getEncoder().encodeToString(signature)); // Let's check the signature Signature publicSignature = Signature.getInstance("SHA256withRSA"); publicSignature.initVerify(pair.getPublic()); publicSignature.update(message.getBytes(StandardCharsets.UTF_8)); boolean isCorrect = publicSignature.verify(signature); System.out.println("public key=" + Base64.getEncoder().encodeToString(pair.getPublic().getEncoded())); System.out.println("Signature correct: " + isCorrect); // The public key can be used to encrypt a message, the private key can be used // to decrypt it. // Encrypt the message Cipher encryptCipher = Cipher.getInstance("RSA"); encryptCipher.init(Cipher.ENCRYPT_MODE, pair.getPublic()); byte[] cipherText = encryptCipher.doFinal(message.getBytes(StandardCharsets.UTF_8)); // Now decrypt it Cipher decriptCipher = Cipher.getInstance("RSA"); decriptCipher.init(Cipher.DECRYPT_MODE, pair.getPrivate()); String decipheredMessage = new String(decriptCipher.doFinal(cipherText), StandardCharsets.UTF_8); System.out.println(decipheredMessage); } }