JAVA와 C# 간에 RSA와 AES로 비밀통신 해보기(SSL)
JAVA와 C# 간에 RSA와 AES로 비밀통신을 하는 코드와 개념을 간단히 정리해 둔다.
RSA
비대칭 암호화 알고리즘으로써, 암호화키와 복호화키가 존재 한다.
일반적으로 암호화 키는 공개키, 복호화키는 개인키라 하지만, 반대로도 사용할 수 있다.
반대로 사용하는 대표적인 경우가 CA가 발급한 인증서를 검증할 때 사용한다.
주의 사항
메시지의 길이는 암호키 길이와 같아야 하는데, 기본적으로 PKCS1Padding 을 사용하게 되므로, 11바이트의 해더가 붙는다.
예를 들어 암호키 사이즈가 512bits 라면, 512/8-11로 53bytes의 메시지 길이 제한이 생긴다.
AES
대칭 암호화 알고리즘으로, 암호화키와 복호화키가 동일하다.
대칭키 중 복호화 시키기는 어렵지만 속도가 빨라 많이 사용한다.
비밀 통신
RSA나 AES를 단독으로 사용해 비밀통신을 하기는 어렵다.
RSA는 메시지 제한이 있기도 하지만, 512bits 기준으로 AES 보다 약 5배 가량은 느렸다.
AES는 대칭키라, 키교환 과정에서 해커가 가로채는 문제가 있다.
RSA와 AES를 혼합해 비밀 통신을 해야 한다
비밀 통신 시작
A와 B가 비밀통신한다고 가정
1. A는 RSA 공개키를 B에게 줌
2. B는 AES키를 생성해 A의 공개키로 암호화 시켜 A에게 보냄
3. A는 받은 암호화된 메시지를 A의 개인키로 풀어 AES키를 획득
4. 그 이후 통신은 AES키로 암호화 시켜 전달한다.
이 과정속에서 해커가 메시지를 가로채서 해독하기는 어렵겠지만,
B가 해커라면, 비밀통신하는 의미가 없어진다.
B가 그 B가 맞는지 입증할 수 있어야 한다.
일반적인 B입증 방법
1. 제 3의 인증기관(CA)이 필요하다.
인증서 비용이 매년 발생하게 된다.(물론 무료도 있다.)
B는 사전에 B의 공개키+서버 주소등을 CA에 주고,
CA는 B가 준 정보를 CA의 개인키로 암호화시킨 인증서로 만들어 B에게 준다.
2. A는 CA의 공개키를 갖고 있어야 하며,
B에게서 받은 인증서를 CA 공개키로 복호화하여 서버 주소와 B의 공개키를 얻어낼 수 있다면,
B가 입증되는 것이다.
3. A는 AES키를 생성해 B의 공개키로 암호화하여 전송
4. B는 B의 개인키로 A의 메시지를 복호화하여 AES키 획득
5. 이후 통신부터는 AES로 암호/복호화 한다.
좀더 간단한 B입증 방법
1. B의 공개키를 A가 미리 알고있다.
A가 사용할 클라이언트를 B가 배포한다면 가능하겠지.
2. A는 AES키를 B의 공개키로 암호화 시켜 B에게 전달
3. B는 B의 개인키로 해독하여 AES키 획득
4. 이후 통신은 AES로 암호화
일반적 또는 간단한 입증방법 모두 해커가 A또는 B 혹은 둘 다를 가장한다고 해도,
진짜 B의 개인키를 모르기 때문에, A의 AES 암호키를 얻을 수 없다.
하지만, A가 사용하는 클라이언트가 이미 감염된 상태라면, 소용없다.
사실 클라이언트가 이미 감염된 상태라면, 암호화 방법으로도 막을 수 없거나, 힘들 꺼다.
이래서 공식 사이트에서 클라이언트를 받거나, 정품 클라이언트를 사용해야 한다.
하지만, ISP(인터넷 서비스 제공자)까지 감염되었다면, 이마저도 안되겠지...
C#과 JAVA 간 호환되는 RSA와 AES 코드
using System; | |
using System.Collections.Generic; | |
using System.Text; | |
using System.Security.Cryptography; | |
namespace ConsoleApplication | |
{ | |
public class RSACrypto | |
{ | |
public class PublicKey | |
{ | |
byte[] m_modulus; | |
byte[] m_exponent; | |
public PublicKey(byte[] modulus, byte[] exponent) | |
{ | |
m_modulus = modulus; | |
m_exponent = exponent; | |
} | |
public PublicKey(string modulusBase64, string exponentBase64) | |
{ | |
m_modulus = System.Convert.FromBase64String(modulusBase64); | |
m_exponent = System.Convert.FromBase64String(exponentBase64); | |
} | |
public static implicit operator RSAParameters(PublicKey key) | |
{ | |
RSAParameters rsaParas = new RSAParameters(); | |
rsaParas.Modulus = key.m_modulus; | |
rsaParas.Exponent = key.m_exponent; | |
return rsaParas; | |
} | |
public byte[] Modulus | |
{ | |
get { return m_modulus; } | |
} | |
public byte[] Exponent | |
{ | |
get { return m_exponent; } | |
} | |
} | |
public class PrivateKey | |
{ | |
RSAParameters m_parameters; | |
public PrivateKey(RSAParameters parameters) | |
{ | |
m_parameters = parameters; | |
} | |
public static implicit operator RSAParameters(PrivateKey key) | |
{ | |
return key.m_parameters; | |
} | |
} | |
public class KeyPair | |
{ | |
public PublicKey publicKey; | |
public PrivateKey privateKey; | |
public KeyPair(int KeyBits) | |
{ | |
using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(KeyBits)) | |
{ | |
RSAParameters privateParam = RSA.ExportParameters(true); | |
publicKey = new PublicKey(privateParam.Modulus, privateParam.Exponent); | |
privateKey = new PrivateKey(privateParam); | |
} | |
} | |
} | |
public static KeyPair GenKey(int KeyBits) | |
{ | |
return new KeyPair(KeyBits); | |
} | |
public static byte[] Encrypt(PublicKey key, byte[] data, bool DoOAEPPadding) | |
{ | |
try | |
{ | |
byte[] encryptedData = null; | |
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider()) | |
{ | |
rsa.ImportParameters(key); | |
encryptedData = rsa.Encrypt(data, DoOAEPPadding); | |
} | |
return encryptedData; | |
} | |
catch (CryptographicException e) | |
{ | |
//Debug.WriteLine(e); | |
} | |
return null; | |
} | |
public static byte[] Decrypt(PrivateKey key, byte[] data, bool DoOAEPPadding) | |
{ | |
try | |
{ | |
byte[] decryptedData = null; | |
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider()) | |
{ | |
rsa.ImportParameters(key); | |
decryptedData = rsa.Decrypt(data, DoOAEPPadding); | |
} | |
return decryptedData; | |
} | |
catch (CryptographicException e) | |
{ | |
//Debug.WriteLine(e); | |
} | |
return null; | |
} | |
public static void WritePublicKeyToFile(PublicKey key, string fileName) | |
{ | |
using (System.IO.StreamWriter fw = new System.IO.StreamWriter(fileName)) | |
{ | |
fw.WriteLine(System.Convert.ToBase64String(key.Modulus)); | |
fw.WriteLine(System.Convert.ToBase64String(key.Exponent)); | |
} | |
} | |
public static PublicKey ReadPublicKeyFromFile(string fileName) | |
{ | |
using (System.IO.StreamReader fr = new System.IO.StreamReader(fileName)) | |
{ | |
byte[] modulus = System.Convert.FromBase64String(fr.ReadLine()); | |
byte[] exponent = System.Convert.FromBase64String(fr.ReadLine()); | |
return new RSACrypto.PublicKey(modulus, exponent); | |
} | |
} | |
} | |
} |
using System; | |
using System.Collections.Generic; | |
using System.Text; | |
using System.Security.Cryptography; | |
namespace ConsoleApplication | |
{ | |
public class AESCrypto | |
{ | |
public class AESKey | |
{ | |
public byte[] key; | |
public byte[] ivKey; | |
} | |
static RijndaelManaged GenRijndaelManaged(AESKey aesKey) | |
{ | |
return new RijndaelManaged | |
{ | |
Mode = CipherMode.CBC, | |
Padding = PaddingMode.PKCS7, | |
KeySize = aesKey.key.Length * 8, | |
Key = aesKey.key, | |
IV = aesKey.ivKey | |
}; | |
} | |
static byte[] Encrypt(byte[] plainBytes, RijndaelManaged rijndaelManaged) | |
{ | |
return rijndaelManaged.CreateEncryptor() | |
.TransformFinalBlock(plainBytes, 0, plainBytes.Length); | |
} | |
static byte[] Decrypt(byte[] encryptedData, RijndaelManaged rijndaelManaged) | |
{ | |
return rijndaelManaged.CreateDecryptor() | |
.TransformFinalBlock(encryptedData, 0, encryptedData.Length); | |
} | |
public static AESKey GenKey(int keyBits) | |
{ | |
AESKey aesKey = new AESKey(); | |
using (RijndaelManaged myRijndael = new RijndaelManaged()) | |
{ | |
myRijndael.KeySize = keyBits; | |
myRijndael.GenerateKey(); | |
myRijndael.GenerateIV(); | |
aesKey.key = myRijndael.Key; | |
aesKey.ivKey = myRijndael.IV; | |
} | |
return aesKey; | |
} | |
public static byte[] Encrypt(AESKey key, byte[] plainMsg) | |
{ | |
return Encrypt(plainMsg, GenRijndaelManaged(key)); | |
} | |
public static byte[] Decrypt(AESKey key, byte[] cipherMsg) | |
{ | |
return Decrypt(cipherMsg, GenRijndaelManaged(key)); | |
} | |
} | |
} |
using System; | |
using System.Collections.Generic; | |
using System.Text; | |
using System.Security.Cryptography; | |
using System.Diagnostics; | |
using System.Linq; | |
namespace ConsoleApplication | |
{ | |
class Program | |
{ | |
static byte[] plain = Encoding.UTF8.GetBytes("c#!시샵"); | |
static void Main(string[] args) | |
{ | |
doRSA(); | |
doAES(); | |
} | |
static void doRSA() | |
{ | |
Debug.WriteLine("RSA Basic Enc/Decrypt"); | |
RSACrypto.KeyPair keyPair = RSACrypto.GenKey(512); | |
byte[] enc = RSACrypto.Encrypt(keyPair.publicKey, plain, false); | |
byte[] enc1 = RSACrypto.Encrypt(keyPair.publicKey, plain, false); | |
byte[] dec = RSACrypto.Decrypt(keyPair.privateKey, enc, false); | |
byte[] dec1 = RSACrypto.Decrypt(keyPair.privateKey, enc1, false); | |
Debug.WriteLine("plain:" + toHex(plain) + " " + System.Text.Encoding.UTF8.GetString(plain)); | |
Debug.WriteLine("enc :" + toHex(enc)); | |
Debug.WriteLine("enc1 :" + toHex(enc1)); | |
Debug.WriteLine("dec :" + toHex(dec) + " " + System.Text.Encoding.UTF8.GetString(dec)); | |
Debug.WriteLine("dec1 :" + toHex(dec1) + " " + System.Text.Encoding.UTF8.GetString(dec1)); | |
Debug.Assert(plain.SequenceEqual(dec)); | |
doSaveRSAPublicKey(keyPair.publicKey); | |
Console.WriteLine("Waiting for Java's PublicKey"); | |
Debug.WriteLine("Waiting for Java's PublicKey\n"); | |
System.Console.ReadKey(); | |
doEncryptWithJavaRSAPublicKey(); | |
Console.WriteLine("Waiting for Java's msg encrypted"); | |
Debug.WriteLine("Waiting for Java's msg encrypted\n"); | |
System.Console.ReadKey(); | |
doDecryptWithCSharpPrivateKey(keyPair.privateKey); | |
} | |
static void doSaveRSAPublicKey(RSACrypto.PublicKey publicKey) | |
{ | |
Debug.WriteLine("RSA Send CSharp's PublicKey to Java"); | |
RSACrypto.WritePublicKeyToFile(publicKey, @"g:\csharpRSAPublicKey.txt"); | |
} | |
static void doEncryptWithJavaRSAPublicKey() | |
{ | |
Debug.WriteLine("RSA Encrypt " + System.Text.Encoding.UTF8.GetString(plain) + " with Java's PublicKey"); | |
RSACrypto.PublicKey publicKey = RSACrypto.ReadPublicKeyFromFile(@"g:\javaRSAPublicKey.txt"); | |
byte[] enc = RSACrypto.Encrypt(publicKey, plain, false); | |
using (System.IO.StreamWriter fw = new System.IO.StreamWriter(@"g:\csharpEncWithJavaRSAPublicKey.txt")) | |
{ | |
fw.Write(System.Convert.ToBase64String(enc)); | |
} | |
} | |
static void doDecryptWithCSharpPrivateKey(RSACrypto.PrivateKey privateKey) | |
{ | |
Debug.WriteLine("RSA Decrypt Java's msg with CSharp's PrivateKey"); | |
using (System.IO.StreamReader fr = new System.IO.StreamReader(@"g:\javaEncWithCSharpRSAPublicKey.txt")) | |
{ | |
byte[] dec = RSACrypto.Decrypt(privateKey, System.Convert.FromBase64String(fr.ReadLine()), false); | |
Debug.WriteLine("java's msg :" + toHex(dec) + " " + System.Text.Encoding.UTF8.GetString(dec)); | |
} | |
} | |
static void doAES() | |
{ | |
Debug.WriteLine("AES Basic Enc/Decrypt"); | |
AESCrypto.AESKey aesKey = AESCrypto.GenKey(128); | |
byte[] enc = AESCrypto.Encrypt(aesKey, plain); | |
byte[] dec = AESCrypto.Decrypt(aesKey, enc); | |
Debug.WriteLine("plain:" + toHex(plain)); | |
Debug.WriteLine("enc :" + toHex(enc)); | |
Debug.WriteLine("dec :" + toHex(dec)); | |
} | |
static string toHex(byte[] text) | |
{ | |
return BitConverter.ToString(text).Replace("-", string.Empty); | |
} | |
} | |
} |
RSA Basic Enc/Decrypt | |
plain:632321EC8B9CEC83B5 c#!시샵 | |
enc :AA8B3F6103D5C68654D16F05740631699FC4DC50F76FD851B86E1BDAEC9998CBF7987DC5D1BDDB887F10DB7AA3A9AB9F2CD09FFDF689BEC29D6A46AC8E0DBD7C | |
dec :632321EC8B9CEC83B5 c#!시샵 | |
RSA Send CSharp's PublicKey to Java | |
Waiting for Java's PublicKey | |
RSA Encrypt c#!시샵 with Java's PublicKey | |
Waiting for Java's msg encrypted | |
RSA Decrypt Java's msg with CSharp's PrivateKey | |
java's msg :6A61766121EC9E90EBB094 java!자바 | |
AES Basic Enc/Decrypt | |
plain:632321EC8B9CEC83B5 | |
enc :84B6138E415E3621DD6A095FC1CCAA30 | |
dec :632321EC8B9CEC83B5 |
package cipher.security; | |
import javax.crypto.Cipher; | |
import javax.crypto.KeyGenerator; | |
import javax.crypto.spec.IvParameterSpec; | |
import javax.crypto.spec.SecretKeySpec; | |
public class AESCrypto { | |
private static final String CipherTransformation = "AES/CBC/PKCS5Padding"; | |
private static final String Algorithm = "AES"; | |
private AESCrypto () { | |
} | |
public static AESKey genKey(int keyBits) | |
{ | |
try { | |
KeyGenerator keyGenerator = KeyGenerator.getInstance(Algorithm); | |
keyGenerator.init(keyBits); | |
// IV is only 16 BYTES | |
KeyGenerator ivKeyGenerator = KeyGenerator.getInstance(Algorithm); | |
ivKeyGenerator.init(16*8); | |
AESKey key = new AESKey(); | |
key.key = keyGenerator.generateKey().getEncoded(); | |
key.ivKey = ivKeyGenerator.generateKey().getEncoded(); | |
return key; | |
} catch (Exception e) { | |
// TODO Auto-generated catch block | |
e.printStackTrace(); | |
} | |
return null; | |
} | |
private static byte[] doCipher(AESKey key, int mode, byte[] msg) | |
{ | |
try { | |
Cipher cipher = Cipher.getInstance(CipherTransformation); | |
SecretKeySpec secretKeySpecy = new SecretKeySpec(key.key, Algorithm); | |
IvParameterSpec ivParameterSpec = new IvParameterSpec(key.ivKey); | |
cipher.init(mode, secretKeySpecy, ivParameterSpec); | |
return cipher.doFinal(msg); | |
} catch(Exception e) | |
{ | |
e.printStackTrace(); | |
} | |
return null; | |
} | |
public static byte[] decrypt(AESKey key, byte[] cipherMsg) | |
{ | |
return doCipher(key, Cipher.DECRYPT_MODE, cipherMsg); | |
} | |
public static byte[] encrypt(AESKey key, byte[] plainMsg) | |
{ | |
return doCipher(key, Cipher.ENCRYPT_MODE, plainMsg); | |
} | |
} |
package cipher.security; | |
public class AESKey | |
{ | |
public byte[] key; | |
public byte[] ivKey; | |
} |
package cipher.security; | |
import java.io.BufferedReader; | |
import java.io.BufferedWriter; | |
import java.io.FileReader; | |
import java.io.FileWriter; | |
import java.math.BigInteger; | |
import java.security.Key; | |
import java.security.KeyFactory; | |
import java.security.KeyPair; | |
import java.security.KeyPairGenerator; | |
import java.security.PrivateKey; | |
import java.security.PublicKey; | |
import java.security.spec.PKCS8EncodedKeySpec; | |
import java.security.spec.RSAPublicKeySpec; | |
import javax.crypto.Cipher; | |
import org.apache.commons.codec.binary.Base64; | |
import sun.security.pkcs.PKCS8Key; | |
public class RSACrypto { | |
private static final String Algorithm = "RSA"; | |
private RSACrypto () { | |
} | |
public static KeyPair genKey(int KeyBits) | |
{ | |
try { | |
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(Algorithm); | |
keyPairGenerator.initialize(KeyBits); | |
return keyPairGenerator.genKeyPair(); | |
} catch (Exception e) { | |
// TODO Auto-generated catch block | |
e.printStackTrace(); | |
} | |
return null; | |
} | |
public static RSAPublicKeySpec toPublicKeySpec(PublicKey key) | |
{ | |
try { | |
return (RSAPublicKeySpec) KeyFactory.getInstance(Algorithm).getKeySpec(key, RSAPublicKeySpec.class); | |
} catch (Exception e) { | |
// TODO Auto-generated catch block | |
e.printStackTrace(); | |
} | |
return null; | |
} | |
public static PublicKey toPublicKey(byte[] modulus, byte[] exponent) | |
{ | |
try { | |
BigInteger bigModulus = new BigInteger(1, modulus); | |
BigInteger bigExponent = new BigInteger(1, exponent); | |
RSAPublicKeySpec spec = new RSAPublicKeySpec(bigModulus, bigExponent); | |
return KeyFactory.getInstance(Algorithm).generatePublic(spec); | |
//return KeyFactory.getInstance(Algorithm).generatePublic(new X509EncodedKeySpec(bytes)); | |
} catch (Exception e) { | |
// TODO Auto-generated catch block | |
e.printStackTrace(); | |
} | |
return null; | |
} | |
public static boolean writePrivateKeyToFile(PrivateKey key, String filename) | |
{ | |
try { | |
FileWriter fw = new FileWriter(filename, false); | |
BufferedWriter bw = new BufferedWriter(fw); | |
byte[] keyBytes = key.getEncoded(); | |
PKCS8Key pkcs8= new PKCS8Key(); | |
pkcs8.decode(keyBytes); | |
byte[] b=pkcs8.encode(); | |
bw.write(Base64.encodeBase64String(b)); | |
bw.close(); | |
return true; | |
} catch (Exception e) { | |
// TODO Auto-generated catch block | |
e.printStackTrace(); | |
} | |
return false; | |
} | |
public static PrivateKey genPrivateKeyFromBase64(String privateKeyBase64) | |
{ | |
try { | |
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyBase64)); | |
KeyFactory kf = KeyFactory.getInstance(Algorithm); | |
return kf.generatePrivate(spec); | |
}catch (Exception e) { | |
// TODO Auto-generated catch block | |
e.printStackTrace(); | |
} | |
return null; | |
} | |
public static PrivateKey readPrivateKeyFromFile(String filename) | |
{ | |
try { | |
FileReader fr = new FileReader(filename); | |
BufferedReader br = new BufferedReader(fr); | |
String keyBase64 = br.readLine(); | |
br.close(); | |
return genPrivateKeyFromBase64(keyBase64); | |
} catch (Exception e) { | |
// TODO Auto-generated catch block | |
e.printStackTrace(); | |
} | |
return null; | |
} | |
public static void writePublicKeyToFile(PublicKey key, String fileName) | |
{ | |
try { | |
RSAPublicKeySpec spec = RSACrypto.toPublicKeySpec(key); | |
byte[] modulus = RSACrypto.removeSigPaddingOfBigIntger(spec.getModulus().toByteArray()); | |
byte[] exponent = spec.getPublicExponent().toByteArray(); | |
FileWriter fw = new FileWriter(fileName, false); | |
BufferedWriter bw = new BufferedWriter(fw); | |
bw.write(Base64.encodeBase64String(modulus)); | |
bw.newLine(); | |
bw.write(Base64.encodeBase64String(exponent)); | |
bw.close(); | |
} catch (Exception e) { | |
// TODO Auto-generated catch block | |
e.printStackTrace(); | |
} | |
} | |
public static PublicKey readPublicKeyFromFile(String fileName) | |
{ | |
try { | |
FileReader fr = new FileReader(fileName); | |
BufferedReader br = new BufferedReader(fr); | |
byte[] modulus = Base64.decodeBase64(br.readLine()); | |
byte[] exponent = Base64.decodeBase64(br.readLine()); | |
PublicKey publicKey = RSACrypto.toPublicKey(modulus, exponent); | |
return publicKey; | |
} catch (Exception e) { | |
// TODO Auto-generated catch block | |
e.printStackTrace(); | |
} | |
return null; | |
} | |
private static byte[] doCipher(Key key, int mode, byte[] msg) | |
{ | |
try { | |
Cipher cipher = Cipher.getInstance(Algorithm); | |
cipher.init(mode, key); | |
return cipher.doFinal(msg); | |
} catch (Exception e) { | |
// TODO Auto-generated catch block | |
e.printStackTrace(); | |
} | |
return null; | |
} | |
private static byte[] removeSigPaddingOfBigIntger(byte[] a) | |
{ | |
if (a[0] == 0) { | |
byte[] tmp = new byte[a.length - 1]; | |
System.arraycopy(a, 1, tmp, 0, tmp.length); | |
return tmp; | |
} | |
return a; | |
} | |
public static byte[] encrypt(PublicKey key, byte[] plainMsg) | |
{ | |
return doCipher(key, Cipher.ENCRYPT_MODE, plainMsg); | |
} | |
public static byte[] decrypt(PrivateKey key, byte[] cipherMsg) | |
{ | |
return doCipher(key, Cipher.DECRYPT_MODE, cipherMsg); | |
} | |
public static byte[] encrypt(PrivateKey key, byte[] plainMsg) | |
{ | |
return doCipher(key, Cipher.ENCRYPT_MODE, plainMsg); | |
} | |
public static byte[] decrypt(PublicKey key, byte[] cipherMsg) | |
{ | |
return doCipher(key, Cipher.DECRYPT_MODE, cipherMsg); | |
} | |
} |
package cipher; | |
import java.io.BufferedReader; | |
import java.io.BufferedWriter; | |
import java.io.FileReader; | |
import java.io.FileWriter; | |
import java.math.BigInteger; | |
import java.security.KeyPair; | |
import java.security.PrivateKey; | |
import java.security.PublicKey; | |
import java.util.Scanner; | |
import org.apache.commons.codec.binary.Base64; | |
import cipher.security.AESCrypto; | |
import cipher.security.AESKey; | |
import cipher.security.RSACrypto; | |
public class MainApp { | |
static final String PlainText = "java!자바"; | |
public static void main(String args[]) { | |
doRSA(); | |
doAES(); | |
} | |
static void doRSA() | |
{ | |
try { | |
System.out.println("RSA Basic Enc/Decrypt"); | |
KeyPair keyPair = RSACrypto.genKey(512); | |
byte[] plain = PlainText.getBytes("UTF-8"); | |
byte[] enc = RSACrypto.encrypt(keyPair.getPublic(), plain); | |
byte[] enc1 = RSACrypto.encrypt(keyPair.getPublic(), plain); | |
byte[] dec = RSACrypto.decrypt(keyPair.getPrivate(), enc); | |
RSACrypto.writePrivateKeyToFile(keyPair.getPrivate(), "g:/javaRSAPrivateKey.txt"); | |
PrivateKey pKey = RSACrypto.readPrivateKeyFromFile("g:/javaRSAPrivateKey.txt"); | |
byte[] dec1 = RSACrypto.decrypt(pKey, enc1); | |
byte[] renc = RSACrypto.encrypt(keyPair.getPrivate(), plain); | |
byte[] renc1 = RSACrypto.encrypt(keyPair.getPrivate(), plain); | |
byte[] rdec = RSACrypto.decrypt(keyPair.getPublic(), renc); | |
byte[] rdec1 = RSACrypto.decrypt(keyPair.getPublic(), renc1); | |
System.out.println("plain:" + ToHexStr( plain ) + " " + new String(plain, "UTF-8")); | |
System.out.println("enc :" + ToHexStr( enc )); | |
System.out.println("enc1 :" + ToHexStr( enc1 )); | |
System.out.println("dec :" + ToHexStr( dec ) + " " + new String(dec, "UTF-8")); | |
System.out.println("dec1 :" + ToHexStr( dec1 ) + " " + new String(dec1, "UTF-8")); | |
System.out.println("renc :" + ToHexStr( renc )); | |
System.out.println("renc1:" + ToHexStr( renc1 )); | |
System.out.println("rdec :" + ToHexStr( rdec ) + " " + new String(rdec, "UTF-8")); | |
System.out.println("rdec1:" + ToHexStr( rdec1 ) + " " + new String(rdec1, "UTF-8")); | |
doSaveRSAPublicKey(keyPair); | |
Scanner in = new Scanner(System.in); | |
System.out.println("Waiting for CSharp's PublicKey"); | |
in.nextLine(); | |
doEncryptWithCSharpRSAPublicKey(); | |
System.out.println("Waiting for CSharp's msg encrypted"); | |
in.nextLine(); | |
doDecryptWithJavaPrivateKey(keyPair.getPrivate()); | |
doRSADataLimit(); | |
doBigInteger(); | |
} catch (Exception e) { | |
// TODO Auto-generated catch block | |
e.printStackTrace(); | |
} | |
} | |
static void doSaveRSAPublicKey(KeyPair keyPair) | |
{ | |
System.out.println("RSA Send Java's PublicKey to CSharp"); | |
RSACrypto.writePublicKeyToFile(keyPair.getPublic(), "g:/javaRSAPublicKey.txt"); | |
} | |
static void doEncryptWithCSharpRSAPublicKey() | |
{ | |
System.out.println("RSA Encrypt " + PlainText + " with CSharp's PublicKey"); | |
try { | |
PublicKey publicKey = RSACrypto.readPublicKeyFromFile("g:/csharpRSAPublicKey.txt"); | |
byte[] plain = PlainText.getBytes("UTF-8"); | |
byte[] enc = RSACrypto.encrypt(publicKey, plain); | |
FileWriter fw = new FileWriter("g:/javaEncWithCSharpRSAPublicKey.txt", false); | |
BufferedWriter bw = new BufferedWriter(fw); | |
bw.write(Base64.encodeBase64String(enc)); | |
bw.close(); | |
System.out.println("enc :" + ToHexStr( enc )); | |
} catch (Exception e) { | |
// TODO Auto-generated catch block | |
e.printStackTrace(); | |
} | |
} | |
static void doDecryptWithJavaPrivateKey(PrivateKey privateKey) | |
{ | |
System.out.println("RSA Decrypt CSharp's msg With Java's PrivateKey"); | |
try { | |
FileReader fr = new FileReader("g:/csharpEncWithJavaRSAPublicKey.txt"); | |
BufferedReader br = new BufferedReader(fr); | |
byte[] dec = RSACrypto.decrypt(privateKey, Base64.decodeBase64(br.readLine())); | |
byte[] plain = PlainText.getBytes("UTF-8"); | |
System.out.println("c#'s msg :" + ToHexStr( dec ) + " " + new String(dec, "UTF-8")); | |
} catch (Exception e) { | |
// TODO Auto-generated catch block | |
e.printStackTrace(); | |
} | |
} | |
static void doRSADataLimit() throws Exception | |
{ | |
System.out.println("RSA Data Limit"); | |
KeyPair keyPair = RSACrypto.genKey(512); | |
// max data length = rsa key size / 8 - 11 | |
byte[] plain = repeat("a", 512/8-11+1).getBytes("UTF-8"); | |
RSACrypto.encrypt(keyPair.getPublic(), plain); | |
} | |
static void doAES() | |
{ | |
try { | |
System.out.println("AES Basic Enc/Decrypt"); | |
AESKey key = AESCrypto.genKey(128); | |
byte[] plain = PlainText.getBytes("UTF-8"); | |
byte[] enc = AESCrypto.encrypt(key, plain); | |
byte[] dec = AESCrypto.decrypt(key, enc); | |
System.out.println("plain:" + ToHexStr( plain )); | |
System.out.println("enc :" + ToHexStr( enc )); | |
System.out.println("dec :" + ToHexStr( dec )); | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} | |
} | |
static void doBigInteger() | |
{ | |
System.out.println("BigInteger Test"); | |
{ | |
byte[] test = {0}; | |
System.out.println(ToHexStr(test) + ":" + ToHexStr(new BigInteger(1, test).toByteArray())); | |
System.out.println(ToHexStr(test) + ":" + ToHexStr(new BigInteger(test).toByteArray())); | |
} | |
{ | |
byte[] test = {1}; | |
System.out.println(ToHexStr(test) + ":" + ToHexStr(new BigInteger(1, test).toByteArray())); | |
System.out.println(ToHexStr(test) + ":" + ToHexStr(new BigInteger(test).toByteArray())); | |
} | |
{ | |
byte[] test = {-1}; | |
System.out.println(ToHexStr(test) + ":" + ToHexStr(new BigInteger(1, test).toByteArray())); | |
System.out.println(ToHexStr(test) + ":" + ToHexStr(new BigInteger(test).toByteArray())); | |
} | |
{ | |
byte[] test = {0, -1}; | |
System.out.println(ToHexStr(test) + ":" + ToHexStr(new BigInteger(1, test).toByteArray())); | |
System.out.println(ToHexStr(test) + ":" + ToHexStr(new BigInteger(test).toByteArray())); | |
} | |
{ | |
byte[] test = {-1, -1}; | |
System.out.println(ToHexStr(test) + ":" + ToHexStr(new BigInteger(1, test).toByteArray())); | |
System.out.println(ToHexStr(test) + ":" + ToHexStr(new BigInteger(test).toByteArray())); | |
} | |
{ | |
byte[] test = {1, -1}; | |
System.out.println(ToHexStr(test) + ":" + ToHexStr(new BigInteger(1, test).toByteArray())); | |
System.out.println(ToHexStr(test) + ":" + ToHexStr(new BigInteger(test).toByteArray())); | |
} | |
} | |
public static String repeat(String s, int times) { | |
StringBuilder stringBuilder = new StringBuilder(s.length() * times); | |
for (int i = 0; i < times; ++i) | |
stringBuilder.append(s); | |
return stringBuilder.toString(); | |
} | |
static String ToHexStr(byte[] b) | |
{ | |
return javax.xml.bind.DatatypeConverter.printHexBinary(b); | |
} | |
} |
RSA Basic Enc/Decrypt | |
plain:6A61766121EC9E90EBB094 java!자바 | |
enc :1C86D20ED175FF853245EC21D1299080AED532B351CFEA8DAA4A2FD61AE51ADF3793CB248BF071E14D5E346DBEED27EC92B64CBCEF0EBEF4247E89001F3C05C4 | |
enc1 :98553EFF69097FD1DFC98762FD9D1D4BB625EAD7CBDCA29772498251E1BB39E6ED1F94E8844B6BBA55FC6E4F4FD2E1C7D64B0060EEE28814223A1FF94E728DB9 | |
dec :6A61766121EC9E90EBB094 java!자바 | |
dec1 :6A61766121EC9E90EBB094 java!자바 | |
renc :09A8E444D3CDF20A16F9EF01B359CE9E0937626B98F55CF208075741CEF2AB4CF9C530971D5206C39673DAE2480269BA07CF97B6D8D0B7BC80EC35829F3AB985 | |
renc1:09A8E444D3CDF20A16F9EF01B359CE9E0937626B98F55CF208075741CEF2AB4CF9C530971D5206C39673DAE2480269BA07CF97B6D8D0B7BC80EC35829F3AB985 | |
rdec :6A61766121EC9E90EBB094 java!자바 | |
rdec1:6A61766121EC9E90EBB094 java!자바 | |
RSA Send Java's PublicKey to CSharp | |
Waiting for CSharp's PublicKey | |
RSA Encrypt java!자바 with CSharp's PublicKey | |
enc :7E17AB4E1212537C3A185C95FFB415354AAF462DC9F5837598E81F247CC8C4300D26F450EF586DDD1874F85D38768F3DDC515B96E6B7F4B0281AE71D694D981F | |
Waiting for CSharp's msg encrypted | |
RSA Decrypt CSharp's msg With Java's PrivateKey | |
c#'s msg :632321EC8B9CEC83B5 c#!시샵 | |
RSA Data Limit | |
javax.crypto.IllegalBlockSizeException: Data must not be longer than 53 bytes | |
at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:344) | |
at com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:389) | |
at javax.crypto.Cipher.doFinal(Cipher.java:2121) | |
at cipher.security.RSACrypto.doCipher(RSACrypto.java:164) | |
at cipher.security.RSACrypto.encrypt(RSACrypto.java:186) | |
at cipher.MainApp.doRSADataLimit(MainApp.java:146) | |
at cipher.MainApp.doRSA(MainApp.java:75) | |
at cipher.MainApp.main(MainApp.java:25) | |
BigInteger Test | |
00:00 | |
00:00 | |
01:01 | |
01:01 | |
FF:00FF | |
FF:FF | |
00FF:00FF | |
00FF:00FF | |
FFFF:00FFFF | |
FFFF:FF | |
01FF:01FF | |
01FF:01FF | |
AES Basic Enc/Decrypt | |
plain:6A61766121EC9E90EBB094 | |
enc :F11EC019F727EAA92BF16572A200A65E | |
dec :6A61766121EC9E90EBB094 |
RSA Basic Enc/Decrypt 설명
plain은 암호화 시킬 메시지
enc와 enc1은 PublicKey로 암호화 시킴, 암호화 시킬 때마다 값이 다르다는 것을 알 수 있다.
dec와 dec1은 각 각 enc와 enc1을 PrivateKey로 복호화 시킴, plain 메시지랑 동일하다. 잘 풀렸다.
renc와 renc1은 PrivateKey로 암호화 시킴, 암호화 시킬 때마다 값이 동일하다.
rdec와 rdec1은 각 각 renc와 renc1을 PublicKey로 복호화 시킴, plain 메시지랑 동일하다. 잘 풀렸다.
RSA Decrypt CSharp's msg With Java's PrivateKey 설명
c#에서 준 메시지를 정상적으로 복호화 한 것을 알 수 있을 거다.
RSA Data Limit
RSA로 암호화 시킬 때, 메시지 길이 제한이 있다는 것을 의도적으로 발생시켰다.
BigInteger Test
BigInteger.toArrayByte()함수 사용시, 첫 번째 배열에 음수값이 있을 때를 0번 째 배열에 0값이 들어가는 것을 테스트 한 거다.
PublickKey의 Modulus 값을 byte[] 형태로 얻을 때, BigInteger.toArrayByte()함수를 사용한다.
첫 번째 배열에 음수값이 있는 경우, 0값이 첫 번째 배열에 채워진다.
이 형태 그대로 c#에 전달해 암호화 시키면, 암호화 시켜 나온 데이터 길이가 암호키 길이보다 하나 증가한다, 참고로, 암호키 길이랑 동일해야 한다.
이 암호화된 데이터를 Java에서 복호화 하면, 오류가 발생한다.
c#에 보내기 전에 추가적으로 붙은 0값을 제거해 줘야 한다.
RSA 로 암호화 시킬 때, 메시지 사이즈 크기를 벗어났을 때 예외가 발생하는 것을 보여줘려고 테스트 코드를 넣었다.
PublicKey와 PrivateKey로 암호화 할 때
어떤 키로도 암호화할 수 있다. 하지만 c#에서는 안되더라...
동일한 PublicKey로 동일한 메시지를 매번 암호화 하면, 암호화된 값은 매번 다르다.
하지만 동일한 PrivateKey로 동일한 메시지를 매번 암호화 하면, 암호화된 값은 매번 동일하다.
Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 8을 다운
JCE를 다운받아 압축을 풀면, local_policy.jar과 US_export_policy.jar파일이 있는데, 직접 그 JAR 파일을 자바폴더에 덮어 씌움.
JRE 환경
%JAVA%\lib\security
예) C:\Program Files\Java\jre1.8.0_31\lib\security
JDK 환경
%JAVA%\jre\lib\security
예) C:\Program Files\Java\jdk1.8.0_31\jre\lib\security