nginx 란 ?

www.nginx.com/resources/glossary/nginx/

NGINX 는 웹 서비스, 리버스 프록시, 캐싱,로드 밸런싱, 미디어 스트리밍 등을위한 오픈 소스 소프트웨어입니다. 
최고의 성능과 안정성을 위해 설계된 웹 서버로 시작되었습니다. 
HTTP 서버 기능 외에도 NGINX는 이메일 (IMAP, POP3 및 SMTP) 용 프록시 서버와 HTTP, TCP 및 
UDP 서버용 역방향 프록시 및로드 밸런서로도 작동 할 수 있습니다.

 

저는 nginx를 웹 프록시 서버로 접근시 톰켓 서버로 연동 되도록 설정해 보겠습니다.

자세한 내용은 하기 내용들을 참고하면 될 것 같습니다.

https://nginx.org/en/docs/beginners_guide.html

https://docs.nginx.com/nginx/admin-guide/load-balancer/http-load-balancer/#proxying-http-traffic-to-a-group-of-servers

 

1. nginx.conf 설정

nginx설치 후 nginx.conf 파일을 하기와 같이 8080으로 받으면 내부 9090포트로 전달하도록 설정하였습니다.

server {
        listen       8080;
        server_name  localhost;
        #charset koi8-r;
        #access_log  logs/host.access.log  main;

        location / {
            proxy_set_header    Host $http_host;
            proxy_set_header    X-Real-IP $remote_addr;
            proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header    X-Forwarded-Proto $scheme;
            proxy_set_header    X-NginX-Proxy true;

            proxy_pass http://127.0.0.1:9090;
            proxy_redirect      off;
            charset utf-8;
         }
...

2. nginx 재 시작 

위 설정 후 nginx restart 8080으로 접근하면 정상적으로 동작하는 것을 확인하였습니다.

1. 배포시에 war를 톰켓에 올리기 위하여 war를 export하기 위해서  

하기 가이드를 참고하여 적용해 보도록 하겠습니다

https://docs.spring.io/spring-boot/docs/current/reference/html/howto.html#howto-traditional-deployment

 

“How-to” Guides

Spring Boot has no mandatory logging dependency, except for the Commons Logging API, which is typically provided by Spring Framework’s spring-jcl module. To use Logback, you need to include it and spring-jcl on the classpath. The recommended way to do th

docs.spring.io

 

1. SpringBootApplication에 SpringBootServletInitializer 상속 

@SpringBootApplication
public class DemoApplication extends SpringBootServletInitializer{ 

	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}
	
	@Override protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { 
		return builder.sources(DemoApplication.class); 
	}
}

 

2. build.gradle 설정 

1. id 'war' 추가 

plugins {
	id 'org.springframework.boot' version '2.3.2.RELEASE'
	id 'io.spring.dependency-management' version '1.0.9.RELEASE'
	id 'java'
	id "com.ewerk.gradle.plugins.querydsl" version "1.0.10"
	id 'war'
}

 

2. bootwar 적용

docs.spring.io/spring-boot/docs/current/gradle-plugin/api/org/springframework/boot/gradle/tasks/bundling/BootWar.html

bootWar {
	archiveBaseName = 'demoproject'
 	archiveVersion="0.0.1-SNAPSHOT"
}

3. dependencies 추가 

dependencies {
	...
	providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat' 
    ...
}

 

3. 확인

1. gradle bootWar실행 

2. war 확인 libs폴더 하위에 생성

위와 같이 war를 제대로 추출되는것을 확인 하였습니다. 

 

1. AES란

AES는 미국 정부가 채택한 이후 전 세계적으로 널리 사용되고 있다.

1977년 공표된 DES를 대체한 AES는, 암호화와 복호화 과정에서 동일한 키를 사용하는 대칭 키 알고리즘이다.

 

2.키 길이

AES 암호화 키의 길이는

128 bit - 16byte 

192 bit - 24byte 

256 bit - 32byte 

의 세 가지 중 하나가 될 수 있으며,

각각 AES-128, AES-192, AES-256으로 불린다. 

암호화 키의 길이에 따라 실행하는 라운드의 수가 다른데, 각각 10, 12, 14 라운드를 실행한다.

 

3. 운영방식

EBC, CBC차이

전자 코드북(electronic codebook, ECB)은 운용 방식 중 가장 간단한 구조를 가지며, 암호화하려는 메시지를 여러 블록으로 나누어 각각 암호화하는 방식으로 되어 있다.

 

전자 코드북은 모든 블록이 같은 암호화 키를 사용하기 때문에 보안에 취약하다.

 

암호 블록 체인 (cipher-block chaining, CBC) 방식은 1976년 IBM에 의해 개발되었다.[4] 각 블록은 암호화되기 전에 이전 블록의 암호화 결과와 XOR되며, 첫 블록의 경우에는 초기화 벡터가 사용된다. 초기화 벡터가 같은 경우 출력 결과가 항상 같기 때문에, 매 암호화마다 다른 초기화 벡터를 사용해야 한다.

 

 

CBC 방식은 현재 널리 사용되는 운용 방식 중 하나이다

 

4. AES256 CBC방식 소스 구현해보기

https://developer.android.com/reference/javax/crypto/Cipher

위 Cipher클래스에서 Algorithm Modes Paddings를 하기와 같이 설정하여 확인해 보도록 하겠습니다.

AES/CBC/PKCS5Padding으로 확인해보겠습니다.

PKCS5Padding은

AES는 블록사이즈가 16바이트 크기로 16바이트의 배수가 아니라면

마지막 블록의 빈 부분을 채워주는 패딩옵션으로 PKCS5Padding로 적용해보겠습니다.

 

* 참고할만한 java 소스 코드 확인시

AES 블록은 16byte이고 AES키 사이즈는 16, 24, 32이며 

라운드 횟수는 키사이즈에 의해서 라운드 횟수가 정해지고

IV사이즈는 블록사이즈와 동일하게 가져야 하는 것을 확인할 수 있습니다.

 

AESConstants.java

interface AESConstants {
    // AES block size in bytes.
    int AES_BLOCK_SIZE = 16;
    // Valid AES key sizes in bytes.
    // NOTE: The values need to be listed in an *increasing* order
    // since DHKeyAgreement depends on this fact.
    int[] AES_KEYSIZES = { 16, 24, 32 };
}

 

AESCrypt.java

키사이즈에 의해서 라운드 횟수가 정해지는것을 확인할 수 있습니다.

/**
  * Return The number of rounds for a given Rijndael keysize.
  *
  * @param keySize  The size of the user key material in bytes.
  *                 MUST be one of (16, 24, 32).
  * @return         The number of rounds.
  */
private static int getRounds(int keySize) {
    return (keySize >> 2) + 6;
}

 

CipherCore.java

블록사이즈와 initial vector사이즈가 동일한 것을 확인할 수 있습니다.

if (params instanceof IvParameterSpec) {
    ivBytes = ((IvParameterSpec)params).getIV();
    if ((ivBytes == null) || (ivBytes.length != blockSize)) {
        throw new InvalidAlgorithmParameterException
            ("Wrong IV length: must be " + blockSize +
              " bytes long");
    }
}

 

Cipher클래스를 이용해서 하기와 같이 구성을 해보았습니다.

//키길이 AES 256
private val KEY_SIZE = 32
private val SECRET_KEY = "0".repeat(KEY_SIZE)
//모드 CBC
private val AES_TRANSFORMATION = "AES/CBC/PKCS5PADDING"
//AES initial Vector == 블록사이즈 16고정
private val IV = "1234567890123456"
private val ivParameterSpec = IvParameterSpec(IV.toByteArray())
private fun secretKeySpec(): SecretKeySpec {
    return SecretKeySpec(SECRET_KEY.toByteArray(), "AES")
}

private fun encrypt(plainText: String): ByteArray  {
    val cipher = Cipher.getInstance(AES_TRANSFORMATION)
    cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec(), ivParameterSpec)
    return cipher.doFinal(plainText.toByteArray())
}

private fun decrypt(cipherText: ByteArray?): String {
    val cipher = Cipher.getInstance(AES_TRANSFORMATION)
    cipher.init(Cipher.DECRYPT_MODE, secretKeySpec(), ivParameterSpec)
    return String(cipher.doFinal(cipherText))
}

private fun encode(byteArray: ByteArray): ByteArray {
    val decodedBytes = Base64.encode(byteArray, Base64.DEFAULT)
    return decodedBytes
}

private fun decode(encodedString: String): ByteArray {
    val decodedBytes = Base64.decode(encodedString, Base64.DEFAULT)
    return decodedBytes
}

 

실행

val plainText = "|" + REQUEST_DATE
Timber.tag(TAG).i("plainText: " + plainText)
val encryptText = encode(encrypt(plainText))
Timber.tag(TAG).i("encrypt : " + encryptText)

var resultDecrypt = decrypt( decode(resultStr))
Timber.tag(TAG).i("result decrypt : " + resultDecrypt)

 

결과

plainText: |1604809558
encrypt : [B@162f532
decrypt : |1604809558

 

+ Recent posts