목차

  1. OpenSSL 세팅
  2. OpenSSL을 통한 개인키/공개키 생성
  3. 개인키/공개키를 활용한 암복호화

OpenSSL 세팅

SSL이란?

SSL(Secure Socket Layer)이란? SSL이란 보안 소켓 계층을 이르는 것으로, 인터넷 상에서 데이터를 안전하게 전송하기 위한 인터넷 암호화 통신 프로토콜이며 데이터 보안을 위해서 개발한 통신 레이어다.

OpenSSL이란?

웹브라우저와 서버 간의 통신을 암호화하는 오픈소스 라이브러리라고 보면 된다.
한 마디로 Openssl을 웹서버(Apache,Nginx)에서 자유롭게 사용할 수 있다.
Openssl 사용현황은 대부분의 사이트가 2/3가 Openssl을 채용했다고 보면된다.

보안이슈

Heart bleed bug 구버전에서 발생된 현상으로 보안을 위해 보안이슈과 최신버전을 수시로 확인해야 할 것 같다.

세팅

설치 프로그램 파일 다운로드 URL : http://slproweb.com/products/Win32OpenSSL.html
(환경에 맞게 설치)

image

(해당 글에서는 Win64 OpenSSL v1.1.1s EXE를 받았음)

설치 <접기/펼치기>

Step 01

image

(약관 동의)

Step 02

image

(경로 설정)

Step 04

image

(Install)

Step 05

image


(기부 선택창)

필수 사항이 아니므로 그냥 닫아주면 된다.

환경변수 <접기/펼치기>

환경 변수 > 시스템 변수 > 새로 만들기 >

image

환경 변수 > 시스템 변수 > Path > 새로 만들기 >

image

CMD 실행 > openssl 커맨드 입력

image


OpenSSL을 통한 개인키공개키 생성

공개키 방식이란?

우선 공개키, 비공개 키의 등장 배경인 대칭키 방식에 대하여 간단히 정리하자면 동일한 키로 암호화, 복호화를 동시에 진행할 수 있는 방식이다. 예르들어 'abcd'라는 키로 암호화를 하면, 복호화 시에 'abcd'를 입력해야 한다. 위 과정에서는 대칭키 전달 과정에서 누군가가 대칭키를 획득한다면 쉽게 암호화된 데이터를 알아낼 수 있다.

위와 같은 단점을 보완하기 위한 것이 공개키 방식이다.

공개키 방식에서는 두 개의 키를 갖는다. 누구에가나 공개가 가능한 공개키(Public Key), 자신만이 갖고 있는 개인키(Private Key) 이 공개키 알고리즘은 공개키로 암호화를 하냐, 개인키로 암호화를 하냐에 따라 사용분야가 달라진다.

공개키로 암호화를 하는것은 데이터 보안에 중점을 두는것이고,
개인키로 암호화를 하면 인증 과정에 중점을 두는 것이다.

이해 하기 쉬운 설명 : https://brunch.co.kr/@artiveloper/24

개인키(Pirvate Key) 생성

CMD 실행 후 순차적으로 커맨드 실행

openssl > genrsa -out private_key.pem 1024

image

공개키(Public Key) 생성

위에서 생성한 개인키를 활용하여 키쌍을 생성한다.

CMD 실행 후 순차적으로 커맨드 실행

openssl > rsa -in private_key.pem -out public_key.pem -pubout

image

키 파일 확인

USER 디렉터리에 물리파일이 생성된걸 확인할 수 있음.
image


개인키공개키를 활용한 암복호화

RSA 알고리즘을 사용하였으며, RSA 알고리즘은 공개키 암호 알고리즘 중의 하나이며, 세계적으로 사용하는 표준적인 알고리즘이다.


package org.qrcode.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.crypto.Cipher;
import javax.inject.Inject;
import javax.servlet.ServletContext;
import javax.xml.bind.DatatypeConverter;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;


@RestController
public class qhdksController {

    @Inject
    private ServletContext context;

    @RequestMapping(value = "/test.do")
    public String test() throws Exception {

        String secretText = "비밀이야"; // 암·복호화를 할 텍스트

        String secretTextEncryption = encryption(secretText); // 암호화

        String decryption = decryption(secretTextEncryption); // 복호화 

        return decryption;
    }

    // 암호화 메서드
    private String encryption(String secretText) {
        byte[] secretTextBytes = secretText.getBytes();

        String fullPath = context.getRealPath("/WEB-INF/classes/message/public_key.pem");

        String key = null;

        BufferedInputStream bs = null;
        try {
            bs = new BufferedInputStream(new FileInputStream(fullPath));
            byte[] b = new byte[bs.available()];
            while (bs.read(b) != -1) {
            }

            key = new String(b);
        } catch (Exception e) {
        } finally {
            try {
                bs.close();
            } catch (Exception e) {
                return "key읽기실패";
            }
        }

        String publicKeyPEM = key.replace("-----BEGIN PUBLIC KEY-----", "")
                .replaceAll("\n", "").replace("-----END PUBLIC KEY-----", "");

        byte[] a = DatatypeConverter.parseBase64Binary(publicKeyPEM);
        String encryptedData = null;
        try {
            //평문으로 전달받은 공개키를 공개키객체로 만드는 과정
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(a);
            PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);

            //만들어진 공개키객체를 기반으로 암호화모드로 설정하는 과정
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);

            //평문을 암호화하는 과정
            byte[] byteEncryptedData = cipher.doFinal(secretText.getBytes());
            encryptedData = Base64.getEncoder().encodeToString(byteEncryptedData);

        } catch (Exception e) {
            System.out.println("암호화 실패");
        }

        return encryptedData;
    }

    // 복호화 메서드
    private String decryption(String encryptedText) throws Exception {
        String decryptedText = null;

        String fullPath = context.getRealPath("/WEB-INF/classes/message/private_key.pem");

        String key = null;

        BufferedInputStream bs = null;
        try {
            bs = new BufferedInputStream(new FileInputStream(fullPath));
            byte[] b = new byte[bs.available()];
            while (bs.read(b) != -1) {
            }

            key = new String(b);
        } catch (Exception e) {
        } finally {
            try {
                bs.close();
            } catch (Exception e) {
                return "key읽기실패";
            }
        }

        String privateKeyPEM = key.replace("-----BEGIN PRIVATE KEY-----", "")
                .replaceAll("\n", "").replace("-----END PRIVATE KEY-----", "");

        byte[] a = DatatypeConverter.parseBase64Binary(privateKeyPEM);

        try {
            //평문으로 전달받은 개인키를 개인키객체로 만드는 과정
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(a);
            PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);

            //만들어진 개인키객체를 기반으로 암호화모드로 설정하는 과정
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, privateKey);

            //암호문을 평문화하는 과정
            byte[] byteEncryptedData = Base64.getDecoder().decode(encryptedText.getBytes());
            byte[] byteDecryptedData = cipher.doFinal(byteEncryptedData);
            decryptedText = new String(byteDecryptedData);
        } catch (Exception e) {
            System.out.println("복호화 실패");
        }

        return decryptedText;
    }
}


 

kf.generatePublic(keySpec) 코드에서 에러가 발생하는 경우

HTTP Status 500 - Request processing failed; nested exception is java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: invalid key format

원인은 자바에서 지원하는 RSA PEM 파일 형식이 맞지 않기 때문이다.
자바는 PKCS#1을 지원하지 않고, PKCS#8만 지원한다.

PKCS#1, PKCS#8 형식 차이 <접기/펼치기>
PKCS#1
-----BEGIN RSA PRIVATE KEY-----

.

.

.

-----END RSA PRIVATE KEY-----
  

PKCS#8

-----BEGIN PRIVATE KEY-----

.

.

.

-----END PRIVATE KEY-----

자바에서 사용하는 경우 pkcs#8 형식으로 변환 혹은 재생성을 하도록 한다.

pkcs#8형식으로 생성 <접기/펼치기>

openssl 커맨드

# 개인키 pkcs#1 형식으로 생성

genrsa -out private_key1.pem 2048

# 개인키 pkcs#1에서 pkcs#8 형식으로 변환

pkcs8 -in private_key1.pem -inform PEM -out private_key.pem -outform PEM -topk8 -nocrypt

# 공개키 pkcs#8 형식으로 생성

rsa -in private_key.pem -out public_key.pem -pubout

image

image

(private_key1.pem은 사용하지 않음.)


profile
개인 학습, 프로젝트 기록

0개의 댓글