2017년 6월 18일 일요일
AES with openssl
앞에서 aes를 https://github.com/kokke/tiny-AES128-C 여기 소스를 이용해서 만들었습니다. http://swlock.blogspot.com/2017/06/aes-128-example-cbc-mode-with-hash-hmac.html
openssl에도 AES가 구현되어 있습니다. 그래서 소스를 검토해보았습니다.
위치는 아래와 같고,
https://github.com/openssl/openssl/tree/master/crypto/aes
핵심 소스는 core에 있습니다.
https://github.com/openssl/openssl/blob/master/crypto/aes/aes_core.c
그래서 openssl을 함께 빌드할때 예제를 찾아보았습니다.
http://www.firmcodes.com/how-do-aes-128-bit-cbc-mode-encryption-c-programming-code-openssl/
소스를 간단하게 정리해 보자면 아래와 같습니다.
AES 128은 128/8=16byte 키를 가집니다.
/* AES key for Encryption and Decryption */
const static unsigned char aes_key[]={0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xAA,0xBB,0xCC,0xDD,0xEE,0xFF};
/* Init vector */ <=CBC 모드에서 사용하는 초기값을 의미합니다.
unsigned char iv[AES_BLOCK_SIZE];
memset(iv, 0x00, AES_BLOCK_SIZE);
AES_KEY enc_key, dec_key; <= 키를 의미합니다.
AES_set_encrypt_key(aes_key, sizeof(aes_key)*8, &enc_key); <=키를 세팅하고
AES_cbc_encrypt(aes_input, enc_out, sizeof(aes_input), &enc_key, iv, AES_ENCRYPT); <= 암호화를 합니다.
/* AES-128 bit CBC Decryption */
memset(iv, 0x00, AES_BLOCK_SIZE); // don't forget to set iv vector again, else you can't decrypt data properly
<= 벡터를 초기값을 넣습니다. 초기 벡터는 입력과 출력을 같은 값으로 넣어야 합니다.
AES_set_decrypt_key(aes_key, sizeof(aes_key)*8, &dec_key); // Size of key is in bits
AES_cbc_encrypt(enc_out, dec_out, sizeof(aes_input), &dec_key, iv, AES_DECRYPT);
이전에 작업했던 부분과 연결하면되는데, 제가 가진 환경이 pc환경이고 openssl을 사용해 빌드할만한 환경이 아니라서 추가 예제를 작성하지 못하고 여기에서 마무리 하도록하겠습니다.
2017년 6월 10일 토요일
AES 128 example CBC mode with hash hmac sha 512
앞에서 AES를 이용한 예제를 만들었습니다.
http://swlock.blogspot.com/2017/05/aes-128-example-cbc-mode.html
하지만 중요한 문제가 있습니다. 암호화가 되어있기 때문에 최종 암호화된 데이터에서 손상이나 변형이 일어나게되면 전혀 알아챌 수 없다는 문제입니다. 그래서 CRC와 같은 HASH함수를 넣을 수 있는데요. 아무렇게나 넣으면 그것의 헛점을 이용해서 빠른 속도로 암호화 해제를 해볼 수가 있습니다.
따라서 그것에 대한 고민은 아래 링크의 주제로 알아보았습니다.
http://swlock.blogspot.com/2017/06/data.html
그중에서도 Encrypt-and-MAC (E&M) 방법을 이용해서 예제로 만들어 보았습니다.
MAC을 암호화된 문서에 첨부하는것인데요. MAC은 Key와 원본 message의 hash로 부터 만들어지게 됩니다. 해제한 원본 message의 MAC값을 계산해서 저장된 값과 일치하는 방법을 사용하면 됩니다.
iv, key 변수는 변경해서 사용하면 됩니다.
컴파일 환경 gcc ( MinGW )
전체 소스
// From https://github.com/kokke/tiny-AES128-C #include <stdlib.h> #include <stdio.h> #include <string.h> #include <stdint.h> #define CBC 1 #define ECB 1 #include "aes.h" #include "hmac_sha2.h" static uint8_t key[] = { 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}; static uint8_t iv[] = { 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}; #define MAX_SIZE 10*1024*1024 #define PADDING_SIZE 16 #include <stdio.h> #include <stdlib.h> #define HMAC_SIZE 512 typedef struct { long int encsize; long int decsize; // HMAC-SHA-512 unsigned char hmac[HMAC_SIZE]; }data_header; void AES128_CBC_encrypt_buffer(uint8_t* output, uint8_t* input, uint32_t length, const uint8_t* key, const uint8_t* iv); long int load_file_with_header(char *filename, char *buf, long int buf_size, char *header, long int header_size) { FILE *fp; long int rwsize1 = 0; long int rwsize2 = 0; fp = fopen(filename,"rb"); if(fp==NULL) return -1; rwsize1 = fread(header,1,header_size,fp); if(rwsize1<=0){ fclose(fp); return -1; } rwsize2 = fread(buf,1,buf_size,fp); if(rwsize2<=0){ fclose(fp); return -1; } fclose(fp); return rwsize1+rwsize2; } long int save_file_with_header(char *filename, char *buf, long int buf_size, char *header, long int header_size) { FILE *fp; long int rwsize1 = 0; long int rwsize2 = 0; fp = fopen(filename,"wb"); if(fp==NULL) return -1; rwsize1 = fwrite(header,1,header_size,fp); if(rwsize1<=0){ fclose(fp); return -1; } rwsize2 = fwrite(buf,1,buf_size,fp); if(rwsize2<=0){ fclose(fp); return -1; } fclose(fp); return rwsize1+rwsize2; } // load filedata in buf. // You should set a filesize in advance. long int load_file(char *filename, char *buf, long int fsize) { FILE *fp; long int rwsize = 0; fp = fopen(filename,"rb"); if(fp==NULL) return -1; rwsize = fread(buf,1,fsize,fp); if(rwsize<=0){ fclose(fp); return -1; } fclose(fp); return rwsize; } // it returns sizes of file. // if have an error, it will return the value minus. long int get_file_length(char *filename) { long int length = 0; FILE *fp; fp = fopen(filename,"rb"); if( fp == NULL ) return -1; fseek(fp,0,2); length = ftell(fp); fclose(fp); return length; } // A size must be PADDING_SIZE times. void AES128_decrypt_cbc(uint8_t *in, uint8_t *out, uint8_t *key, long int size, uint8_t *iv) { AES128_CBC_decrypt_buffer(out+0, in+0, 16, key, iv); if( size > 16 ){ AES128_CBC_decrypt_buffer(out+16, in+16, size-16, 0, 0); } } // A size must be PADDING_SIZE times. long int AES128_encrypt_cbc(uint8_t *in, uint8_t *out, uint8_t *key, long int size, uint8_t *iv) { AES128_CBC_encrypt_buffer(out, in, size, key, iv); return size+(PADDING_SIZE-(size%PADDING_SIZE)); } int aes_encrypt_save_with_header(char *filename, char *encfname) { long int encsize; char *buffer = NULL; char *encbuffer = NULL; data_header header; long int filesize = get_file_length(filename); if( filesize <= 0 ) return -1; buffer = malloc(filesize+PADDING_SIZE); memset(buffer+filesize,0,PADDING_SIZE); // padding load_file(filename, buffer, filesize); encbuffer = malloc(filesize+PADDING_SIZE); encsize = AES128_encrypt_cbc(buffer, encbuffer, key, filesize+(PADDING_SIZE-(filesize%PADDING_SIZE)), iv); header.encsize = encsize; header.decsize = filesize; hmac_sha512(key, sizeof(key), (unsigned char *)buffer, filesize, header.hmac, HMAC_SIZE); save_file_with_header(encfname, encbuffer, encsize, (char *)&header, sizeof(header)); free(encbuffer); free(buffer); return 0; } char * aes_decrypt_load_with_header(char *encfname, data_header *header) { char *buffer = NULL; char *outbuffer = NULL; long int filesize = get_file_length(encfname); unsigned char hmac_calc[HMAC_SIZE]; buffer = malloc(filesize+1-sizeof(data_header)); load_file_with_header(encfname, buffer, filesize-sizeof(data_header), (char *)header, sizeof(data_header)); outbuffer = malloc(filesize+1-sizeof(data_header)); AES128_decrypt_cbc(buffer, outbuffer, key, header->decsize, iv); hmac_sha512(key, sizeof(key), (unsigned char *)outbuffer, header->decsize, hmac_calc, HMAC_SIZE); if( memcmp(header->hmac, hmac_calc, HMAC_SIZE)==0 ){ free(buffer); free(outbuffer); return NULL; } free(buffer); return outbuffer; } int main(int argc, char *argv[]) { data_header loadheader; char nbuf[2048]; char *loadbuf; if( argc <= 1){ printf("input file name\n"); return -1; } strcpy(nbuf,argv[1]); strcat(nbuf,".enc"); aes_encrypt_save_with_header(argv[1],nbuf); printf("saved file from %s to %s\nenter\n",argv[1],nbuf); getchar(); printf("load %s file\nenter\n",nbuf); getchar(); loadbuf = aes_decrypt_load_with_header(nbuf,&loadheader); if(loadbuf==NULL){ printf("loading fail\n"); return -1; }else{ printf("loading sucess\n"); } // compare { long int filesize = get_file_length(argv[1]); char *filebuffer; filebuffer = malloc(filesize+1); load_file(argv[1], filebuffer, filesize); if(loadheader.decsize!=filesize){ printf("file size fail %ld!=%ld\n",loadheader.decsize,filebuffer); return -1; } if(memcmp(filebuffer,loadbuf,filesize)==0){ printf("success\n"); }else{ printf("fail\n"); } free(loadbuf); free(filebuffer); } return 0; }
실행화면
enter라고 표시된것은 해당시점에 enter키를 눌러서 다음단계로 넘어가게 예제를 만들었습니다. (getchar 표준 함수 이용)
E:\blogdata\3>aes_test3 aes.c saved file from aes.c to aes.c.enc enter load aes.c.enc file enter loading sucess success
hex edit로 aes.c.enc 파일은 변경한 경우
"<=====" 표시된 시점에 aes.c.enc 파일을 조작을 하게되면 fail이 발생합니다.
E:\download\blogdata\3>aes_test3 aes.c saved file from aes.c to aes.c.enc enter load aes.c.enc file enter <======================== 이 시점에서 값을 조작함 loading sucess fail
소스 링크 : https://drive.google.com/open?id=0B9vAKDzHthQIeEFveFBGODZ1REU
2017년 6월 3일 토요일
암호화시 data의 유효성을 검증하는 방법
암호화시 data의 유효성을 검증하는 방법
암호화가 필요해서 구현을 하였습니다. 그랬더니, 걱정이 생겼습니다. 암호화를 풀고나서 푼값이 원본과 같은지....
그렇다면 md5, crc, 등의 hash 값을 어떻게 넣으면 될까요? 암호를 풀고나서 원본을 점검해야 하는것이니 원본에 대한 hash가 필요하게 됩니다. 이것을 잘못 구현하면 암호화된 text를 공격할때 사용되질 수 있게 되어 보안상 문제가 발생하게 됩니다. 그래서 여러 자료를 찾아 봤는데요. wiki에 정리가 되어있습니다.
https://en.wikipedia.org/wiki/Authenticated_encryption
해당 링크에서 오른쪽 아래 보시며 그림이 있으며, 그림만 봐도 전체적인 흐름을 알 수 있습니다.
Approaches to Authenticated Encryption
Encrypt-then-MAC (EtM)
The plaintext is first encrypted, then a MAC is produced based on the resulting ciphertext. The ciphertext and its MAC are sent together. Used in, e.g., IPsec. The standard method according to ISO/IEC 19772:2009.[5] This is the only method which can reach the highest definition of security in AE, but this can only be achieved when the MAC used is "Strongly Unforgeable"[10] In November 2014, TLS and DTLS extension for EtM has been published as RFC 7366. Various EtM ciphersuites exist for SSHv2 as well (e.g. hmac-sha1-etm@openssh.com).
Encrypt-and-MAC (E&M)
A MAC is produced based on the plaintext, and the plaintext is encrypted without the MAC. The plaintext's MAC and the ciphertext are sent together. Used in, e.g., SSH. Even though the E&M approach has not been proved to be strongly unforgeable in itself,[10] it is possible to apply some minor modifications to SSH to make it strongly unforgeable despite the approach.[citation needed]
MAC-then-Encrypt (MtE)
A MAC is produced based on the plaintext, then the plaintext and MAC are together encrypted to produce a ciphertext based on both. The ciphertext (containing an encrypted MAC) is sent. Used in, e.g., SSL/TLS. Even though the MtE approach has not been proven to be strongly unforgeable in itself,[10] the SSL/TLS implementation has been proven to be strongly unforgeable by Krawczyk who showed that SSL/TLS was in fact secure because of the encoding used alongside the MtE mechanism.[11][dubious – discuss] Despite the theoretical security, deeper analysis of SSL/TLS modeled the protection as MAC-then-pad-then-encrypt, i.e. the plaintext is first padded to the block size of the encryption function. Padding errors often result in the detectable errors on the recipient's side, which in turn lead to Padding Oracle attacks, such as Lucky Thirteen.
이때. MAC이라는 개념이 나오게되는데요. MAC은 message를 인증하기 위한 작은 코드라고 합니다.
https://en.wikipedia.org/wiki/Message_authentication_code
https://ko.wikipedia.org/wiki/%EB%A9%94%EC%8B%9C%EC%A7%80_%EC%9D%B8%EC%A6%9D_%EC%BD%94%EB%93%9C
결로적으로 hash를 이용해 MAC을 구하는 방법은 아래 링크를 참고하면 됩니다.
https://en.wikipedia.org/wiki/Hash-based_message_authentication_code
MAC은 암호화 할때 사용한 key와 원본 text를 입력으로해 만드는 일종의 hash funtion같은것입니다.
위 링크의 예제를 보면 입력이 key와 원본 text를 이용하면 hash값이 출력됨을 알 수 있습니다.
HMAC_MD5("key", "The quick brown fox jumps over the lazy dog") = 0x80070713463e7749b90c2dc24911e275 HMAC_SHA1("key", "The quick brown fox jumps over the lazy dog") = 0xde7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9 HMAC_SHA256("key", "The quick brown fox jumps over the lazy dog") = 0xf7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8
HMAC에 포함된 예제 : http://swlock.blogspot.com/2017/06/aes-128-example-cbc-mode-with-hash-hmac.html
2017년 5월 29일 월요일
AES 128 example CBC mode
AES 128 example CBC mode
참고 소스 : https://github.com/kokke/tiny-AES128-C
동작되는 예제는 아래와 같습니다.
AES 128bit (16byte) 암호화 입니다.
아래 소스는 AES128 CBC mode로 임의의 파일을 암호화 후 *.enc 파일에 저장한 후 다시 파일을 로딩해서 값이 원본과 같은지 비교하는 예제소스 입니다. cbc mode는 block 단위의 암호화를 할때 현재 결과가 다음 블럭의 초기값으로 사용되어지는 모드입니다. 따라서 중간에 에러가 한번 생기면 에러는 마지막단까지 전파가 됩니다. 그리고 초기 벡터 값을 필요로 합니다. (여기에서는 iv 입니다.)
aes_test2.c 로 파일명을 했으며, 위 참고 소스링크의 aes.c, aes.h 파일이 필요합니다.
빌드시 : gcc aes_test2.c aes.c
아래 2개의 배열은 사용자 맞게 바꾸는 값입니다.
key는 암호화를 위한 키값이 됩니다. 일종의 비밀번호라고 생각하면 되는데 이것이 128bit가 됩니다.
참고 소스 코드에 문제가 있는지 padding이 안된 데이터를 넘기면 메모리쪽에 문제가 발생하여, 패딩을 해서 넣도록 제작하였습니다.
iv는 초기 vector값이 됩니다. 이 값 또한 변경 가능합니다.
static uint8_t key[] = { 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}; static uint8_t iv[] = { 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
예제 코드
// From https://github.com/kokke/tiny-AES128-C #include <stdlib.h> #include <stdio.h> #include <string.h> #include <stdint.h> #define CBC 1 #define ECB 1 #include "aes.h" static uint8_t key[] = { 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}; static uint8_t iv[] = { 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}; #define MAX_SIZE 10*1024*1024 #define PADDING_SIZE 16 #include <stdio.h> #include <stdlib.h> typedef struct { long int encsize; long int decsize; }data_header; void AES128_CBC_encrypt_buffer(uint8_t* output, uint8_t* input, uint32_t length, const uint8_t* key, const uint8_t* iv); long int load_file_with_header(char *filename, char *buf, long int buf_size, char *header, long int header_size) { FILE *fp; long int rwsize1 = 0; long int rwsize2 = 0; fp = fopen(filename,"rb"); if(fp==NULL) return -1; rwsize1 = fread(header,1,header_size,fp); if(rwsize1<=0){ fclose(fp); return -1; } rwsize2 = fread(buf,1,buf_size,fp); if(rwsize2<=0){ fclose(fp); return -1; } fclose(fp); return rwsize1+rwsize2; } long int save_file_with_header(char *filename, char *buf, long int buf_size, char *header, long int header_size) { FILE *fp; long int rwsize1 = 0; long int rwsize2 = 0; fp = fopen(filename,"wb"); if(fp==NULL) return -1; rwsize1 = fwrite(header,1,header_size,fp); if(rwsize1<=0){ fclose(fp); return -1; } rwsize2 = fwrite(buf,1,buf_size,fp); if(rwsize2<=0){ fclose(fp); return -1; } fclose(fp); return rwsize1+rwsize2; } // load filedata in buf. // You should set a filesize in advance. long int load_file(char *filename, char *buf, long int fsize) { FILE *fp; long int rwsize = 0; fp = fopen(filename,"rb"); if(fp==NULL) return -1; rwsize = fread(buf,1,fsize,fp); if(rwsize<=0){ fclose(fp); return -1; } fclose(fp); return rwsize; } // it returns sizes of file. // if have an error, it will return the value minus. long int get_file_length(char *filename) { long int length = 0; FILE *fp; fp = fopen(filename,"rb"); if( fp == NULL ) return -1; fseek(fp,0,2); length = ftell(fp); fclose(fp); return length; } // A size must be PADDING_SIZE times. void AES128_decrypt_cbc(uint8_t *in, uint8_t *out, uint8_t *key, long int size, uint8_t *iv) { AES128_CBC_decrypt_buffer(out+0, in+0, 16, key, iv); if( size > 16 ){ AES128_CBC_decrypt_buffer(out+16, in+16, size-16, 0, 0); } } // A size must be PADDING_SIZE times. long int AES128_encrypt_cbc(uint8_t *in, uint8_t *out, uint8_t *key, long int size, uint8_t *iv) { AES128_CBC_encrypt_buffer(out, in, size, key, iv); return size+(PADDING_SIZE-(size%PADDING_SIZE)); } int aes_encrypt_save_with_header(char *filename, char *encfname) { long int encsize; char *buffer = NULL; char *encbuffer = NULL; data_header header; long int filesize = get_file_length(filename); if( filesize <= 0 ) return -1; buffer = malloc(filesize+PADDING_SIZE); memset(buffer+filesize,0,PADDING_SIZE); // padding load_file(filename, buffer, filesize); encbuffer = malloc(filesize+PADDING_SIZE); encsize = AES128_encrypt_cbc(buffer, encbuffer, key, filesize+(PADDING_SIZE-(filesize%PADDING_SIZE)), iv); header.encsize = encsize; header.decsize = filesize; save_file_with_header(encfname, encbuffer, encsize, (char *)&header, sizeof(header)); free(encbuffer); free(buffer); return 0; } char * aes_decrypt_load_with_header(char *encfname, data_header *header) { char *buffer = NULL; char *outbuffer = NULL; long int filesize = get_file_length(encfname); buffer = malloc(filesize+1-sizeof(data_header)); load_file_with_header(encfname, buffer, filesize-sizeof(data_header), (char *)header, sizeof(data_header)); outbuffer = malloc(filesize+1-sizeof(data_header)); AES128_decrypt_cbc(buffer, outbuffer, key, header->decsize, iv); free(buffer); return outbuffer; } int main(int argc, char *argv[]) { data_header loadheader; char nbuf[2048]; char *loadbuf; if( argc <= 1){ printf("input file name\n"); return -1; } strcpy(nbuf,argv[1]); strcat(nbuf,".enc"); aes_encrypt_save_with_header(argv[1],nbuf); loadbuf = aes_decrypt_load_with_header(nbuf,&loadheader); // compare { long int filesize = get_file_length(argv[1]); char *filebuffer; filebuffer = malloc(filesize+1); load_file(argv[1], filebuffer, filesize); if(loadheader.decsize!=filesize){ printf("file size fail %ld!=%ld\n",loadheader.decsize,filebuffer); return -1; } if(memcmp(filebuffer,loadbuf,filesize)==0){ printf("success\n"); }else{ printf("fail\n"); } free(loadbuf); free(filebuffer); } return 0; }
실행시 AES 변환을 하고자 하는 이름을 뒤에 인자로 적어주면 됩니다.
여기에서는 tiny-AES128-C-master.zip 파일을 선택하였습니다.
같은 폴더에 tiny-AES128-C-master.zip.enc 파일도 같이 생성됩니다.
실행화면
E:\blogdata>gcc aes_test2.c aes.c -o aes_test2 E:\blogdata>aes_test2 tiny-AES128-C-master.zip success E:\blogdata>aes_test2 aes.c success E:\blogdata>aes_test2 aes.h success
소스링크:https://drive.google.com/open?id=0B9vAKDzHthQITmY0bDlfRU1aYjg
HMAC에 포함된 예제 : http://swlock.blogspot.com/2017/06/aes-128-example-cbc-mode-with-hash-hmac.html
2016년 1월 3일 일요일
RSA java 예제(2) - 3개 파트 분리 소스
앞에서 RSA 의 기본 개념을 살펴 봤습니다.
http://swlock.blogspot.kr/2014/11/rsa-java.html
하지만 대부분의 예제들이 암호화 하고 곧장 해제하는 등의 하나의 java 예제로 붙어있어서 무슨말인지 이해하기 어려울것 같아서 좀 더 쉽게 예제를 작성 하였습니다.
그림은 이전에 그린 그림과 같이 동일하나, 이번에 구현할 내용은 3개의 part로 나누어서 구현하였습니다.
http://swlock.blogspot.kr/2014/11/rsa-java.html
하지만 대부분의 예제들이 암호화 하고 곧장 해제하는 등의 하나의 java 예제로 붙어있어서 무슨말인지 이해하기 어려울것 같아서 좀 더 쉽게 예제를 작성 하였습니다.
그림은 이전에 그린 그림과 같이 동일하나, 이번에 구현할 내용은 3개의 part로 나누어서 구현하였습니다.
1. 키생성 파트
이건 암호화나 복호화를 하는게 아니라 미리 개인키와 공개키를 보관하기 위해서 생성해 두는것입니다. 안드로이드 apk signing 시 keystore 파일을 만드는것처럼 개인키와 공개키를 미리 저장하는 개념으로 이해하면 됩니다.2. 암호화 파트
공개키와 평문을 이용하여 암호화를 합니다.3. 복호화 파트
전달된 암호문과 가지고 있는 개인키를 이용하여 복호화 합니다.2014년 11월 18일 화요일
RSA의 문서과 이해
정리가 잘되어 있는 문서가 있다.
http://www.parkjonghyuk.net/lecture/modernCrypto/lecturenote/chap05.pdf
정리를 하면 다음과 같다.
<E,N> 쌍 : 공개키
<D,N> 쌍 : 개인키
암호문 = 평문^E mod N
평문 = 암호문^D mod N
주) ^ 연산은 pow() 함수를 의미한다.
N,D,E 생성 순서
(1) N을 구한다
(2) L을 구한다 (L은 키 쌍을 생성할 때만 등장하는 수이다)
(3) E를 구한다
(4) D를 구한다
구하기
(1)N = p*q (p,q는 소수) : p, q 랜덤
(2)L = lcm(p-1, q-1) : lcm 최소 공배수 ex)lcm(12, 18)=36=> lcm(2*2*3,2*2*3*3)
(3)1 < E < L 조건을 만족하는 E 선택 gcd(E, L) = 1 : gcd 최대 공약수
(4)1 < D < L 조건을 만족하는 D 선택 E*D mod L = 1
예)
(1) N을 구한다
2개의 소수 p=17, q=19 를 고른다.
N = p × q = 17×19 = 323
(2) L을 구한다
L = lcm(p-1, q-1)
= lcm(16,18) (p-1은 16, q-1은 18)
= 144 (16과 18의 최소공배수)
(3) E를 구한다
gcd(E, L) = gcd(E, 144) = 1 이 되는 E를 구한다
이과 같은 E는 많이 있다. 예를 들면, 다음과 같은 수이다.
5, 7, 11, 13, 17, 19, 23, 25, 29, 31, …
여기서는 E로서 5를 골라 보겠다.
여기까지 해서 공개키는 (E,N)=(5, 323)이 되었다.
여기까지 해서 공개키는 (E,N)=(5, 323)이 되었다.
(4) D를 구한다
D는 식 E*D mod L = 1 을 충족시켜야만 된다.
E에 뭔가를 곱해서 mod L을 취하면 1이 되는 수를 찾아보자.
D = 29는, 이 관계식을 충족시킨다. 왜냐 하면,
E*D mod L = 5*29 mod 144
= 145 mod 144
= 1
여기까지 해서 개인키는 (D,N)=(29, 323)이 되었다.
(5) 암호화
평문은 N 미만의 수, 즉 323 미만의 수여야 한다.
평문으로서 123을 암호화해 보자.
평문^E mod N = 123^5 mod 323
= 225
따라서 암호문은 225가 된다.
(6) 복호화
암호문 225를 복호화 하겠다.
복호화에서는 개인 키 D = 29, N = 323을 사용 한다.
암호문^D mod N = 225^29 mod 323
= 123
- RSA에 대한 공격
(1) 수 D를 알면 암호문을 복호화 할 수 있다.
그렇기 때문에 RSA에 대한 공격 방법으로서, D의 후보가 되는 수를 순서대로 시도해서 복호화한다는 전사공격을 생각할 수 있다.
전사공격은 D의 비트 수가 크면 클수록 시도해보아야 할 수도 많아지고 계산도 복잡해지므로 점점 더 시간이 걸리며 어려워진다.
따라서 비트 수가 충분히 크면 전사공격으로 수 D를 찾아내는 것은 현실적으로는 불가능해진다.
(2) p와 q는 소수이기 때문에 N으로부터 p와 q를 구하는 것은 자연수 N을 소인수분해하는 것과 같다.
큰 수의 소인수분해를 고속으로 할 수 있는 방법이 발견되면 RSA는 해독할 수 있다
- Java Key로 저장되는 값
이전 소스를 변형하여 쌍으로 생성된 키를 출력해 보았다.
결과는 아래와 같다.
각각의 의미하는 바를 살펴보기로 하자.
modulus는 N을 의미한다. primeP,primeQ는 각각 P,Q값을 의미하고 P*Q = N이 된다.
이것을 Java로 계산해서 확인해 보았다.
결과가 dff077aaf0d951a074bc40121e3d5b33 딱 맞게 나왔다.
public exponent가 E를 의미하며 따라서 공개키<E,N>은 <public exponent,modulus> 가 된다. 개인키 <D,N> 는 <private exponent,modulus>가 된다.
generator.initialize(128); KeyPair pair = generator.generateKeyPair(); Key pubKey = pair.getPublic(); // Kb(pub) 공개키 Key privKey = pair.getPrivate();// Kb(pri) 개인키 System.out.println("pub key: "+ new String(pubKey.toString())); System.out.println("pri key: "+ new String(privKey.toString()));
결과는 아래와 같다.
pub key: RSA Public Key modulus: dff077aaf0d951a074bc40121e3d5b33 public exponent: 10001 pri key: RSA Private CRT Key modulus: dff077aaf0d951a074bc40121e3d5b33 public exponent: 10001 private exponent: 89bee85d057927735ccc19f3ac010a81 primeP: efc54140ccaef98b primeQ: ef18e3b09d2c49f9 primeExponentP: 31b480ac80421db5 primeExponentQ: d48320cd37c5c7b1 crtCoefficient: e04b791dd2e5a6fc
각각의 의미하는 바를 살펴보기로 하자.
modulus는 N을 의미한다. primeP,primeQ는 각각 P,Q값을 의미하고 P*Q = N이 된다.
이것을 Java로 계산해서 확인해 보았다.
BigInteger bi1= new BigInteger("efc54140ccaef98b",16); BigInteger bi2= new BigInteger("ef18e3b09d2c49f9",16); BigInteger bi3 = bi1.multiply(bi2); System.out.println(bi3.toString(16));
결과가 dff077aaf0d951a074bc40121e3d5b33 딱 맞게 나왔다.
public exponent가 E를 의미하며 따라서 공개키<E,N>은 <public exponent,modulus> 가 된다. 개인키 <D,N> 는 <private exponent,modulus>가 된다.
2014년 11월 16일 일요일
RSA java 예제
RSA는 비대칭키로 많이 사용되는 알고리즘입니다.
일반적인 암호화하면 비밀키가 있고 해당 비밀키로만 해제가 되는게 대칭키 알고리즘입니다.
하지만 RSA 공개키과 개인키 두개의 키가 존재하고 암호를 하는쪽에서는 공개키로 암호화를 하면 암호를 해독 하는 쪽에서는 개인키로만 해제가 가능합니다.
더 자세한 설명은 아래 wiki를 살펴보면 됩니다.
http://ko.wikipedia.org/wiki/RSA_%EC%95%94%ED%98%B8
최초 두개의 키를 만들때 두개의 곱셈을 이용하는 부분이 있어서 하나의 키를 알더라도 곱해지는 숫자를 알기 위해서는 소인수 분해를 해야하는데 키가 하나의 소수로 되어 있으며 상당히 큰 숫자로 되어있어 소인수 분해의 어려움이 있기 때문에 개인키 값을 알아내기 힘들게 됩니다.
아래는 RSA의 일반적인 사용에 대해서 그림으로 도식한 것 입니다. 복호화 하는쪽에서 공개키와 개인키를 만들어 공개키를 배포하게 됩니다.
(새로운 예제:각각 모듈이 나누어진 예제)
http://swlock.blogspot.com/2016/01/rsa-java-2-3.html
자바의 RSA 암호화 복호화 예제
package cmdlineJava; import java.security.InvalidKeyException; import java.security.Key; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.SecureRandom; import java.security.Security; import java.security.spec.InvalidKeySpecException; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; public class RSAexample { public static void main(String[] args) throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, InvalidKeySpecException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException { Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); byte[] input = "abcdefg hijklmn".getBytes(); Cipher cipher = Cipher.getInstance("RSA/None/NoPadding", "BC"); SecureRandom random = new SecureRandom(); KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "BC"); generator.initialize(128, random); // 여기에서는 128 bit 키를 생성하였음 KeyPair pair = generator.generateKeyPair(); Key pubKey = pair.getPublic(); // Kb(pub) 공개키 Key privKey = pair.getPrivate();// Kb(pri) 개인키 // 공개키를 전달하여 암호화 cipher.init(Cipher.ENCRYPT_MODE, pubKey); byte[] cipherText = cipher.doFinal(input); System.out.println("cipher: ("+ cipherText.length +")"+ new String(cipherText)); // 개인키를 가지고있는쪽에서 복호화 cipher.init(Cipher.DECRYPT_MODE, privKey); byte[] plainText = cipher.doFinal(cipherText); System.out.println("plain : " + new String(plainText)); } }
실행결과
cipher 쪽은 random으로 인해 값이 바뀜
cipher: (16)뾡 쁴M%긵w?f? plain : abcdefg hijklmn
위 코드 빌드시 bouncycastle 관련 에러가 난다면 아래 site에서 jar파일을 받아서 library를 추가 하시기 바랍니다.
http://www.bouncycastle.org/latest_releases.html
더 많은 RSA sample은 여기를 참고하시기 바랍니다.
http://www.java2s.com/Tutorial/Java/0490__Security/BasicRSAexample.htm
RSA 계산시 중간에 생성되는 값은 아래 site를 참고하시기 바랍니다.
http://xenon.stanford.edu/~tjw/jsbn/
피드 구독하기:
글 (Atom)