2011년 6월 13일 월요일

자바을 이용한 암호학(자바보안 개정판:키 관리) - 6

PLT 3.4 Key 일치
키 일치는 공개 키를 교환함으로써 공유된 비밀 키를 생성할 수 있는 방법을 제공한다. Diffie-Hellman 알고리즘은 키 일치를 하는 표준 알고리즘이다.

javax.crypto.KeyAgreement
주요 Method :
- public static final KeyAgreement getInstance(String algorithm) : 주어진 알고리즘을 사용하여 새로운 KeyAgreement를 생성한다. 이름은 “DH"와 같은 키교환 알고리즘이어야 한다.
- public static final KeyAgreement getInstance(String algorithm, String provider) : 주어진 알고리즘과 프로바이더로 새로운 KeyAgreement를 생성한다.
- public final void init(Key key) : 제공된 키를 사용하는 KeyAgreement를 초기화 한다.
- public final void init(Key key, AlgorithmParameterSpec params) : 주어진 키와 알고리즘 지정 파라미터를 사용하여 KeyAgreement를 초기화 한다.
- public final void init(.Key key, AlgorithmParameterSpec params, SecureRandom random) : 주어진 키와 알고리즘 지정 파라미터 그리고 랜덤 소스를 사용해서 KeyAgreement를 초기화 한다.
- public final void init(Key key, SecureRandom random)
- public final Key doPhase(Key key, boolean lastPhase)
- public final byte[] generateSecret()
- public final int generateSecret(byte[] sharedSecret, int offset)
- public final SecretKey generateSecret(java.lang.String algorithm)
- public final String getAlgorithm()
- public final Provider getProvider()

Sample Program
서버와 클라이언트가 각각 키교환을 통하여 SessionKey을 생성하고 상호 Stream을 SessionKey을 이용한 대칭키 방식으로 암호화 한다.
Session Key는 암호화와 복호화에 같은 키를 사용하는 대칭키(Symmetric Key)를 사용하고 이 세션키를 암호화하고 복호화하는 데는 비대칭키(Asymmetric Key)를 사용한다. 그 이유는 대칭키가 비대칭키보다 보안상 약하지만 암호화 및 복호화 속도는 현저하게 빠르다.


com.crypto.KeyAgreementServer.java
package com.crypto;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.KeyAgreement;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;
public class KeyAgreementServer {
private KeyPair keyPair;
private ServerSocket server;
private Socket socket;
private DataOutputStream out;
private DataInputStream in;
private PublicKey clientPublicKey;
private byte[] iv = null;
private SecretKey sessionKey;
public static void main (String[] args)
throws Exception {
if (args.length != 1) {
System.err.println("Usage: java com.crypto.KeyAgreementServer port");
System.exit(1);
}
KeyAgreementServer server = new KeyAgreementServer();
server.openServer(Integer.parseInt(args[0]));
server.exec();
server.closeServer();
}
/**
* Open a port and wait for a connection
* @param port 포트
* @throws IOException
*/
public void openServer(int port)
throws IOException {
server = new ServerSocket (port);
System.out.println("Listening on port "+port+"...");
socket = server.accept();
out = new DataOutputStream(socket.getOutputStream());
in = new DataInputStream(socket.getInputStream());
}
/**
* server Close
* @throws IOException
*/
public void closeServer()
throws IOException {
in.close();
out.close();
socket.close();
}
public void exec()
throws NoSuchAlgorithmException,
InvalidAlgorithmParameterException,
IOException,
InvalidKeySpecException,
InvalidKeyException,
NoSuchPaddingException {
genServerKey();
sendServerKey();
receiveClientKey();
sendIVParameterSpec();
createSessionKey();
cipherStreamIn();
}
/**
* @throws NoSuchAlgorithmException
* @throws NoSuchPaddingException
* @throws InvalidKeyException
* @throws InvalidAlgorithmParameterException
* @throws IOException
*/
private void cipherStreamIn()
throws NoSuchAlgorithmException,
NoSuchPaddingException,
InvalidKeyException,
InvalidAlgorithmParameterException,
IOException {
System.out.println("Creating the CipherStream...");
System.out.println("Instance : TripleDES/CFB8/NoPadding");
Cipher decrypter = Cipher.getInstance("TripleDES/CFB8/NoPadding");
IvParameterSpec spec = new IvParameterSpec(iv);
decrypter.init(Cipher.DECRYPT_MODE, sessionKey, spec);
CipherInputStream cipherIn =
new CipherInputStream(socket.getInputStream(), decrypter);
int theCharacter=0;
theCharacter = cipherIn.read();
while (theCharacter != -1) {
System.out.print((char)theCharacter);
theCharacter = cipherIn.read();
}
cipherIn.close();
}
/**
* Create the session key
* @throws NoSuchAlgorithmException
* @throws InvalidKeyException
* @throws InvalidKeySpecException
*/
private void createSessionKey()
throws NoSuchAlgorithmException,
InvalidKeyException,
InvalidKeySpecException {
System.out.println("Performing the KeyAgreement...");
KeyAgreement ka = KeyAgreement.getInstance("DH");
ka.init(keyPair.getPrivate());
ka.doPhase(clientPublicKey,true);
byte[] sessionKeyBytes = ka.generateSecret();
SecretKeyFactory skf = SecretKeyFactory.getInstance("TripleDES");
DESedeKeySpec tripleDesSpec = new DESedeKeySpec(sessionKeyBytes);
sessionKey = skf.generateSecret(tripleDesSpec);
}
/**
* Create and send the IVParameterSpec
* @return
* @throws IOException
*/
private void sendIVParameterSpec()
throws IOException {
iv = new byte[8];
SecureRandom sr = new SecureRandom();
sr.nextBytes(iv);
out.write(iv);
}
/**
* Receive the client's public key
* @return
* @throws IOException
* @throws NoSuchAlgorithmException
* @throws InvalidKeySpecException
*/
private void receiveClientKey()
throws IOException,
NoSuchAlgorithmException,
InvalidKeySpecException {
System.out.println("Receiving client's public key...");
byte[] keyBytes = new byte[in.readInt()];
in.readFully(keyBytes);
KeyFactory kf = KeyFactory.getInstance("DH");
X509EncodedKeySpec x509Spec = new X509EncodedKeySpec(keyBytes);
clientPublicKey = kf.generatePublic(x509Spec);
}
/**
* Send my public key
* @throws IOException
*/
private void sendServerKey()
throws IOException {
System.out.println("Sending Server public key.");
byte[] keyBytes = keyPair.getPublic().getEncoded();
out.writeInt(keyBytes.length);
out.write(keyBytes);
}
/**
* Server Key Pair 생성
* @throws NoSuchAlgorithmException
* @throws InvalidAlgorithmParameterException
*/
private void genServerKey()
throws NoSuchAlgorithmException,
InvalidAlgorithmParameterException {
System.out.println("Generating a Diffie-Hellman KeyPair...");
KeyPairGenerator kpg = KeyPairGenerator.getInstance("DH");
kpg.initialize(ServiceLocator.PARAMETER_SPEC);
keyPair = kpg.genKeyPair();
}
}


l genServerKey() : 서버 Diffie-Hellman KeyPair을 생성한다.
l sendServerKey() : 서버에서 생성된 KeyPair에서 public key을 클라이언트에 전송한다.
l receiveClientKey() : 클라이언트의 public Key을 받는다.
l sendIVParameterSpec() : 서버에서 생성된 IVParameterSpec을 클라이언트에 전송한다.
l createSessionKey() : 서버의 public key, 클라이언트의 public key을 TripleDES알고리즘으로 SessionKey을 생성한다.
l cipherStreamIn() : 생성된 SessionKey을 이용하여 암호화된 Stream을 생성한다.
com.crypto.KeyAgreementClient.java
package com.crypto;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.Cipher;
import javax.crypto.CipherOutputStream;
import javax.crypto.KeyAgreement;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;
public class KeyAgreementClient {
private DataOutputStream out;
private DataInputStream in;
private Socket socket;
private KeyPair keyPair;
private PublicKey serverPublicKey;
private byte[] iv = new byte[8];
private SecretKey sessionKey;
public static void main (String[] args)
throws Exception {
if (args.length != 2) {
System.err.println("Usage: java KeyAgreementClient host port");
System.exit(1);
}
KeyAgreementClient client = new KeyAgreementClient();
client.connectionServer(args[0],Integer.parseInt(args[1]));
client.exec();
client.closeConnection();
}
/**
* Open a connection
* @param host Server IP/Domain
* @param port Server Port
* @throws IOException
*/
public void connectionServer(String host, int port)
throws IOException {
System.out.println("Trying to connect to "+host+", port "+port+".");
socket = new Socket (host,port);
out = new DataOutputStream(socket.getOutputStream());
in = new DataInputStream(socket.getInputStream());
}
public void closeConnection()
throws IOException {
in.close();
out.close();
socket.close();
}
public void exec()
throws NoSuchAlgorithmException,
InvalidAlgorithmParameterException,
UnknownHostException,
IOException,
InvalidKeySpecException,
InvalidKeyException,
NoSuchPaddingException {
genKey();
receiveServerKey();
sendClientKey();
receiveIVParameterSpec();
createSessionKey();
cipherStreamOut();
}
/**
* Create the CipherStream to be used
* @throws NoSuchAlgorithmException
* @throws NoSuchPaddingException
* @throws InvalidKeyException
* @throws InvalidAlgorithmParameterException
* @throws IOException
*/
private void cipherStreamOut()
throws NoSuchAlgorithmException,
NoSuchPaddingException,
InvalidKeyException,
InvalidAlgorithmParameterException,
IOException {
System.out.println("Creating the CipherStream...");
System.out.println("Instance : TripleDES/CFB8/NoPadding");
Cipher encrypter = Cipher.getInstance("TripleDES/CFB8/NoPadding");
IvParameterSpec spec = new IvParameterSpec(iv);
encrypter.init(Cipher.ENCRYPT_MODE, sessionKey, spec);
CipherOutputStream cipherOut =
new CipherOutputStream(socket.getOutputStream(), encrypter);
String testString = "Established Connection.nn";
byte[] byteArray = testString.getBytes();
cipherOut.write(byteArray);
System.out.println("Established Connection.");
System.out.println("The '~' is an escape character to exit");
int theCharacter=0;
theCharacter = System.in.read();
while (theCharacter != '~') {
cipherOut.write(theCharacter);
theCharacter = System.in.read();
}
cipherOut.close();
}
/**
* Perform the KeyAgreement
* @throws NoSuchAlgorithmException
* @throws InvalidKeyException
* @throws InvalidKeySpecException
*/
private void createSessionKey()
throws NoSuchAlgorithmException,
InvalidKeyException,
InvalidKeySpecException {
System.out.println("Performing the KeyAgreement...");
KeyAgreement ka = KeyAgreement.getInstance("DH");
ka.init(keyPair.getPrivate());
ka.doPhase(serverPublicKey,true);
byte[] sessionKeyBytes = ka.generateSecret();
SecretKeyFactory skf = SecretKeyFactory.getInstance("TripleDES");
DESedeKeySpec tripleDesSpec = new DESedeKeySpec(sessionKeyBytes);
sessionKey = skf.generateSecret(tripleDesSpec);
}
/**
* Receive the initialization vector
* @throws IOException
*/
private void receiveIVParameterSpec()
throws IOException {
in.readFully(iv);
}
/**
* Send our public key
* @throws IOException
*/
private void sendClientKey()
throws IOException {
System.out.println("Sending my public key.");
byte[] keyBytes = keyPair.getPublic().getEncoded();
out.writeInt(keyBytes.length);
out.write(keyBytes);
}
/**
* Receive the server's public key
* @throws IOException
* @throws NoSuchAlgorithmException
* @throws InvalidKeySpecException
*/
private void receiveServerKey()
throws IOException,
NoSuchAlgorithmException,
InvalidKeySpecException {
System.out.println("Receiving the server's public key.");
byte[] keyBytes = new byte[in.readInt()];
in.readFully(keyBytes);
KeyFactory kf = KeyFactory.getInstance("DH");
X509EncodedKeySpec x509Spec = new X509EncodedKeySpec(keyBytes);
serverPublicKey = kf.generatePublic(x509Spec);
}
/**
* Generate a key pair
* @throws NoSuchAlgorithmException
* @throws InvalidAlgorithmParameterException
*/
private void genKey()
throws NoSuchAlgorithmException,
InvalidAlgorithmParameterException {
System.out.println("Generating a Diffie-Hellman key pair...");
KeyPairGenerator kpg = KeyPairGenerator.getInstance("DH");
kpg.initialize(ServiceLocator.PARAMETER_SPEC);
keyPair = kpg.generateKeyPair();
}
}

  • genKey() : 클라이언트 DH알고리즘 KeyPair을 생성한다.
  • receiveServerKey() : 서버로부터 서버 public Key을 받는다.
  • sendClientKey() : 서버에게 클라이언트의 public Key을 전송한다.
  • receiveIVParameterSpec() : 서버에서 생성된 IVParametreSpec을 받는다.
  • createSessionKey() : Session key을 생성한다.
  • cipherStreamOut() : 암호화된 Stream을 생성한다.

댓글 없음:

댓글 쓰기

블록체인 개요 및 오픈소스 동향

블록체인(block chain) 블록체인은 공공 거래장부이며 가상 화폐로 거래할때 발생할때 발생할 수 있는 해킹을 막는 기술. 분산 데이터베이스의 한 형태로, 지속적으로 성장하는 데이터 기록 리스트로서 분산 노드의 운영자에 의한 임의 조작이 불가...