Java에서의 문자열 암호화 방법
2D 바코드(PDF-417)로 표시되는 문자열을 암호화하면 스캔 아이디어가 떠올라도 읽을 수 없습니다.
기타 요건:
- 복잡하지 않다
- RSA, PKI 인프라스트럭처, 키쌍 등으로 구성해서는 안 됩니다.
이 솔루션은 기웃거리는 사람들을 제거할 수 있을 정도로 단순해야 하며, 데이터를 얻는데 관심이 있는 다른 회사도 쉽게 해독할 수 있어야 합니다.그들은 우리에게 전화를 걸어 표준을 알려주거나 해독에 사용할 수 있는 간단한 키를 줍니다.
아마도 그 회사들은 다른 기술을 사용할 수 있기 때문에 특별한 플랫폼이나 기술에 얽매이지 않는 표준을 고수하는 것이 좋을 것이다.
★★★★★★★★★★★★★★★★★?'' 요?encrypt()
&decrypt()
높은 보안 표준을 달성하는 데 큰 어려움 없이 말이죠.
구글을 통해 처음 나타나는 페이지인데, 모든 구현의 보안 취약성으로 인해 움츠러들기 때문에 원래 게시물로부터 7년이 지났기 때문에 다른 사용자를 위한 암호화에 대한 정보를 추가하기 위해 게시합니다.저는 컴퓨터 공학 석사 학위를 가지고 있고 암호학을 공부하고 배우는 데 많은 시간을 보냈기 때문에 인터넷을 더 안전한 곳으로 만들기 위해 의견을 모으고 있습니다.
또한 많은 구현이 특정 상황에서 안전할 수 있다는 점에 유의하십시오. 그러나 이러한 구현이 사용될 경우 실수로 인해 오류가 발생할 수 있는 이유는 무엇입니까?특별한 이유가 없는 한 사용할 수 있는 가장 강력한 도구를 사용하십시오.전반적으로 저는 도서관을 사용하고 가능하면 사소한 세부 사항에서 멀리 떨어지기를 강력히 권장합니다.
업데이트 4/5/18: 알기 쉽게 하기 위해 일부 부분을 다시 작성하고 권장 라이브러리를 Jasypt에서 Google의 새로운 라이브러리 Tink로 변경했습니다.기존 설정에서 Jasypt를 완전히 삭제할 것을 권장합니다.
서문
아래에서는 보안 대칭 암호의 기본을 개략적으로 설명하고 표준 Java 라이브러리에서 사용자가 직접 암호화를 구현할 때 온라인에서 흔히 볼 수 있는 실수를 지적합니다.Google의 새로운 라이브러리에 대한 모든 세부 정보를 건너뛰고 싶다면 Tink를 프로젝트로 가져와 모든 암호화에 AES-GCM 모드를 사용하면 안전합니다.
이제 에서 Java로 암호화하는 방법에 대한 자세한 내용을 알아보겠습니다.
블록 암호
먼저 대칭 키 블록 암호를 선택해야 합니다.블록 암호는 의사 난수를 만드는 데 사용되는 컴퓨터 기능/프로그램입니다.의사 무작위성은 양자 컴퓨터 이외에는 어떤 컴퓨터도 그것과 실제 무작위성의 차이를 구별할 수 없는 가짜 무작위성이다.블록 암호는 암호화에 대한 구성 블록과 같으며, 다른 모드 또는 스킴과 함께 사용하면 암호화를 생성할 수 있습니다.
현재 이용 가능한 블록 암호 알고리즘에 대해서는, 「NEVER」, 「NEVER」, 「NEVER」, 「NEVER use 3DES」라고 말할 수 있습니다.Snowden의 NSA 릴리스에서도 가능한 한 의사 랜덤에 가까운 것을 확인할 수 있는 유일한 블록 암호는 AES 256입니다.AES 128도 존재합니다.다만, AES 256은 256비트 블록으로 동작하고, AES 128은 128 블록으로 동작합니다.전체적으로 AES 128은 몇 가지 약점이 발견되었지만 안전한 것으로 간주되지만 256은 매우 견고합니다.
재미있는 사실 DES는 NSA에 의해 처음 설립되었을 때 깨졌고 실제로 몇 년 동안 비밀에 부쳐졌다.여전히 3DES가 안전하다는 주장도 있지만, 3DES의 약점을 찾아내 분석한 연구논문이 적지 않다.
암호화 모드
암호화는 블록 암호를 사용하여 특정 방식을 사용하면 생성됩니다.이렇게 하면 랜덤성과 키를 조합하여 키를 알고 있는 한 되돌릴 수 있는 것을 만들 수 있습니다.이를 암호화 모드라고 합니다.
다음은 무슨 일이 일어나고 있는지 시각적으로 파악할 수 있도록 암호화 모드와 ECB로 알려진 가장 단순한 모드의 예입니다.
온라인에서 가장 일반적으로 볼 수 있는 암호화 모드는 다음과 같습니다.
ECB CTR, CBC, GCM
열거된 모드 외에 다른 모드가 있으며 연구자들은 항상 기존 문제를 개선하기 위해 새로운 모드를 모색하고 있습니다.
이제 구현 및 보안에 대해 살펴보겠습니다.절대 ECB를 사용하지 마십시오. 이것은 유명한 Linux 펭귄이 보여주듯이 반복 데이터를 숨기는데 서툴러요.
Java 로 실장하는 경우, 다음의 코드를 사용하는 경우는, ECB 모드가 디폴트로 설정되어 있는 것에 주의해 주세요.
Cipher cipher = Cipher.getInstance("AES");
...위험은 취약점입니다ILITY!는 유감스럽게도 StackOverflow의 모든 곳과 온라인 튜토리얼 및 예에서 볼 수 있습니다.
난스 및 IV
ECB 모드 명사는 IV로도 알려져 있는 문제에 대응하여 작성되었습니다.새로운 랜덤 변수를 생성하여 모든 암호화에 첨부하여 동일한 2개의 메시지를 암호화하면 서로 다르게 출력됩니다.이것의 장점은 IV 또는 난스가 공공지식이라는 것이다.즉, 공격자는 이 정보에 액세스할 수 있지만 사용자의 키가 없는 한 해당 지식으로는 아무 것도 수행할 수 없습니다.
일반적인 문제는 IV를 코드 내에서 동일한 고정값으로 설정하는 것입니다.IV를 반복하는 순간 IV의 함정은 암호화의 보안 전체를 손상시키는 것입니다.
랜덤 IV 생성
SecureRandom randomSecureRandom = new SecureRandom();
byte[] iv = new byte[cipher.getBlockSize()];
randomSecureRandom.nextBytes(iv);
IvParameterSpec ivParams = new IvParameterSpec(iv);
주의: SHA1은 고장났지만, 이 사용 사례에 SHA256을 올바르게 실장하는 방법을 찾을 수 없었기 때문에, 이것을 시험해 보고 갱신하고 싶은 사람이 있으면, 최고입니다!또한 SHA1 공격은 거대한 클러스터가 깨지는 데 몇 년이 걸릴 수 있기 때문에 여전히 관례적이지 않습니다.자세한 것은 이쪽을 봐 주세요.
CTR의 실장
CTR 모드에서는 패딩이 필요 없습니다.
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
CBC의 실장
CBC 모드를 실장하는 경우는, 다음과 같이 PKCS7Padding 를 사용해 주세요.
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
CBC 및 CTR의 취약성 및 GCM을 사용해야 하는 이유
CBC나 CTR 등의 다른 모드는 안전하지만 공격자가 암호화된 데이터를 플립하여 복호화 시 값을 변경하는 문제가 발생합니다.예를 들어, "Sell 100"이라는 가상의 은행 메시지를 암호화하면, 암호화된 메시지는 "eu23ng"처럼 보입니다. 공격자가 메시지를 복호화하면 "eu53ng"로 1비트가 변경되고 갑자기 "Sell 900"으로 표시됩니다.
이를 피하기 위해 대부분의 인터넷에서는 GCM이 사용되며 HTTPS가 표시될 때마다 GCM이 사용됩니다.GCM은 암호화된 메시지에 해시를 서명하고 이 시그니처를 사용하여 메시지가 변경되지 않았는지 확인합니다.
GCM의 복잡성 때문에 GCM의 실장은 피하고 싶습니다.여기서도 실수로 IV를 반복하면 GCM과 관련된 키를 손상시킬 수 있기 때문에 구글의 새로운 라이브러리 Tink를 사용하는 것이 좋습니다.이것은 궁극적인 보안상의 결함입니다.새로운 연구진은 IV를 반복해도 키는 위험하지 않지만 아직 주류가 되지 않는 IV 반복 저항형 암호화 모드를 개발 중이다.
GCM을 실장하는 경우는, 다음의 링크를 참조해 주세요.다만, 시큐러티를 확실히 할 수 없거나, 제대로 실장하고 있는지는 알 수 없지만, 베이스는 저하하고 있습니다.또한 GCM에서는 패딩이 없습니다.
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
키와 패스워드
또 하나의 매우 중요한 점은 암호화에 관해서는 키와 패스워드는 동일하지 않다는 것입니다.암호화 키는 어느 정도의 엔트로피와 랜덤성을 가져야 안전하다고 간주됩니다.따라서 적절한 암호화 라이브러리를 사용하여 키를 생성해야 합니다.
여기서 실행할 수 있는 실장은 두 가지가 있습니다.첫 번째는 랜덤키 생성에 이 StackOverflow 스레드에 있는 코드를 사용하는 것입니다.이 솔루션은 보안 난수 생성기를 사용하여 사용할 수 있는 키를 처음부터 생성합니다.
보안이 낮은 다른 옵션은 비밀번호와 같은 사용자 입력을 사용하는 것입니다.앞에서 설명한 바와 같이 패스워드의 엔트로피가 부족하기 때문에 패스워드를 취득하여 강화하는 알고리즘인 PBKDF2를 사용해야 합니다.다음으로 마음에 드는 Stack Overflow 실장을 나타냅니다.하지만 구글 팅크 라이브러리에는 이 모든 것이 내장되어 있기 때문에 당신은 그것을 이용해야 한다.
안드로이드 개발자
여기서 짚고 넘어가야 할 중요한 점은 당신의 안드로이드 코드는 리버스 엔지니어링이 가능하며 대부분의 경우 자바 코드도 마찬가지라는 것을 아는 것입니다.즉, 코드에 일반 텍스트로 암호를 저장하는 경우입니다.해커는 쉽게 복구할 수 있습니다.일반적으로 이러한 유형의 암호화에는 비대칭 암호화 등을 사용합니다.이것은 이 포스트의 범위 밖이기 때문에, 나는 그것을 파고드는 것을 피한다.
2013년의 흥미로운 기사: Android에서의 Crypto 구현의 88%가 부적절하게 수행되었다고 지적합니다.
최종 의견
다시 한번 암호용 자바 라이브러리를 직접 구현하지 말고 Google Tink를 사용하면 모든 알고리즘을 제대로 구현했기 때문에 번거로움을 덜 수 있습니다.그리고 Tink github에서 발생한 문제, 여기저기서 나타나는 취약성 팝업도 확인합니다.
질문이나 피드백이 있으시면 언제든지 코멘트를 주세요!시큐러티는 항상 변화하고 있기 때문에, 시큐러티에 대응하기 위해서 최대한의 노력을 할 필요가 있습니다.
DES, 3DES, AES 등 널리 이용 가능한 표준 대칭 사이퍼를 사용하는 것을 추천합니다.이것이 가장 안전한 알고리즘은 아니지만 구현이 많기 때문에 바코드.javax.crypto의 정보를 해독할 수 있는 키를 누구에게나 주면 됩니다.여기서 일하고 싶은 건 암호야
암호화할 바이트가 있다고 가정합니다.
byte[] input;
다음으로 키와 초기화 벡터 바이트가 필요합니다.
byte[] keyBytes;
byte[] ivBytes;
이제 선택한 알고리즘의 암호를 초기화할 수 있습니다.
// wrap key data in Key/IV specs to pass to cipher
SecretKeySpec key = new SecretKeySpec(keyBytes, "DES");
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
// create the cipher with the algorithm you choose
// see javadoc for Cipher class for more info, e.g.
Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
암호화는 다음과 같습니다.
cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
byte[] encrypted= new byte[cipher.getOutputSize(input.length)];
int enc_len = cipher.update(input, 0, input.length, encrypted, 0);
enc_len += cipher.doFinal(encrypted, enc_len);
복호화는 다음과 같습니다.
cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
byte[] decrypted = new byte[cipher.getOutputSize(enc_len)];
int dec_len = cipher.update(encrypted, 0, enc_len, decrypted, 0);
dec_len += cipher.doFinal(decrypted, dec_len);
감사합니다. 당신의 코드를 사용하여 이 클래스를 만들었습니다.아마 누군가가 그것을 사용자로 가득하다고 생각할 것입니다.
물체 크립터
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class ObjectCrypter {
private Cipher deCipher;
private Cipher enCipher;
private SecretKeySpec key;
private IvParameterSpec ivSpec;
public ObjectCrypter(byte[] keyBytes, byte[] ivBytes) {
// wrap key data in Key/IV specs to pass to cipher
ivSpec = new IvParameterSpec(ivBytes);
// create the cipher with the algorithm you choose
// see javadoc for Cipher class for more info, e.g.
try {
DESKeySpec dkey = new DESKeySpec(keyBytes);
key = new SecretKeySpec(dkey.getKey(), "DES");
deCipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
enCipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvalidKeyException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public byte[] encrypt(Object obj) throws InvalidKeyException, InvalidAlgorithmParameterException, IOException, IllegalBlockSizeException, ShortBufferException, BadPaddingException {
byte[] input = convertToByteArray(obj);
enCipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
return enCipher.doFinal(input);
// cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
// byte[] encypted = new byte[cipher.getOutputSize(input.length)];
// int enc_len = cipher.update(input, 0, input.length, encypted, 0);
// enc_len += cipher.doFinal(encypted, enc_len);
// return encypted;
}
public Object decrypt( byte[] encrypted) throws InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, IOException, ClassNotFoundException {
deCipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
return convertFromByteArray(deCipher.doFinal(encrypted));
}
private Object convertFromByteArray(byte[] byteObject) throws IOException,
ClassNotFoundException {
ByteArrayInputStream bais;
ObjectInputStream in;
bais = new ByteArrayInputStream(byteObject);
in = new ObjectInputStream(bais);
Object o = in.readObject();
in.close();
return o;
}
private byte[] convertToByteArray(Object complexObject) throws IOException {
ByteArrayOutputStream baos;
ObjectOutputStream out;
baos = new ByteArrayOutputStream();
out = new ObjectOutputStream(baos);
out.writeObject(complexObject);
out.close();
return baos.toByteArray();
}
}
경고
이것을 일종의 보안 측정으로 사용하지 마십시오.
이 게시물의 암호화 메커니즘은 One-Time Pad로, 공격자가 2개의 암호화된 메시지를 사용하여 쉽게 개인 키를 복구할 수 있습니다. XOR 2 암호화된 메시지를 사용하면 키를 얻을 수 있습니다.그렇게 간단해!
무사님 지적
Sun의 JRE에 있는 Sun의 Base64 Encoder/Decoder를 사용하여 lib에서 또 다른 JAR을 회피하고 있습니다.OpenJDK 또는 다른 JRE를 사용하는 것은 위험합니다.그 외에 Apache Commons lib를 Encoder/Decoder와 함께 사용하는 것을 고려해야 하는 이유가 있습니까?
public class EncryptUtils {
public static final String DEFAULT_ENCODING = "UTF-8";
static BASE64Encoder enc = new BASE64Encoder();
static BASE64Decoder dec = new BASE64Decoder();
public static String base64encode(String text) {
try {
return enc.encode(text.getBytes(DEFAULT_ENCODING));
} catch (UnsupportedEncodingException e) {
return null;
}
}//base64encode
public static String base64decode(String text) {
try {
return new String(dec.decodeBuffer(text), DEFAULT_ENCODING);
} catch (IOException e) {
return null;
}
}//base64decode
public static void main(String[] args) {
String txt = "some text to be encrypted";
String key = "key phrase used for XOR-ing";
System.out.println(txt + " XOR-ed to: " + (txt = xorMessage(txt, key)));
String encoded = base64encode(txt);
System.out.println(" is encoded to: " + encoded + " and that is decoding to: " + (txt = base64decode(encoded)));
System.out.print("XOR-ing back to original: " + xorMessage(txt, key));
}
public static String xorMessage(String message, String key) {
try {
if (message == null || key == null) return null;
char[] keys = key.toCharArray();
char[] mesg = message.toCharArray();
int ml = mesg.length;
int kl = keys.length;
char[] newmsg = new char[ml];
for (int i = 0; i < ml; i++) {
newmsg[i] = (char)(mesg[i] ^ keys[i % kl]);
}//for i
return new String(newmsg);
} catch (Exception e) {
return null;
}
}//xorMessage
}//class
Jasypt 사용 가능
Jasypt를 사용하면 다음과 같이 간단하게 암호를 암호화하고 확인할 수 있습니다.
StrongTextEncryptor textEncryptor = new StrongTextEncryptor();
textEncryptor.setPassword(myEncryptionPassword);
암호화:
String myEncryptedText = textEncryptor.encrypt(myText);
복호화:
String plainText = textEncryptor.decrypt(myEncryptedText);
그라들:
compile group: 'org.jasypt', name: 'jasypt', version: '1.9.2'
특징:
Jasypt는 간단한 단방향(다이제스트) 및 양방향 암호화 기술을 제공합니다.
기본 Java VM 공급자뿐만 아니라 모든 JCE 공급자와 함께 사용할 수 있도록 API를 엽니다.Jasypt는 Bouncy Castle과 같은 유명 공급자와 쉽게 사용할 수 있습니다.더 배우기.
사용자의 비밀번호 보안 강화.더 배우기.
바이너리 암호화 지원.Jasypt는 바이너리(바이트 배열)의 다이제스트와 암호화를 가능하게 합니다.필요에 따라서(예를 들면, 인터넷을 개입시켜 송신하기 위해서) 오브젝트나 파일을 암호화합니다.
번호 암호화 지원텍스트와 바이너리 외에 숫자 값의 다이제스트와 암호화를 허용합니다(BigInteger 및 BigDecimal, 휴지 상태 지속성을 위해 암호화하는 경우 다른 숫자 유형이 지원됩니다).더 배우기.
스레드 세이프.
멀티프로세서/멀티코어 시스템에서 하이 퍼포먼스를 실현하기 위한 암호화/디지스터 풀링 지원.
모바일 플랫폼 등 크기가 제한된 환경에서의 관리 용이성을 향상시키기 위해 라이브러리의 경량("lite") 버전을 포함합니다.
암호화를 처음 접하는 사용자에게 쉬운 무구성 암호화 도구와 파워 유저에게 높은 구성성을 갖춘 표준 암호화 도구를 제공합니다.
매핑된 엔티티의 필드를 암호화 방식으로 유지하려면 3과 4(옵션)의 통합 기능을 휴지 상태로 만듭니다.필드의 암호화는 휴지 상태 매핑 파일에 정의되며, 나머지 애플리케이션에서는 투명하게 유지됩니다(민감한 개인 데이터, 많은 읽기 지원 사용자가 있는 데이터베이스 등).텍스트, 이진수, 숫자, 부울란, 날짜 암호화...더 배우기.
스프링 어플리케이션에 심리스하게 통합할 수 있으며 스프링 2, 스프링 3.0 및 스프링 3.1 전용 통합 기능을 갖추고 있습니다.jasypt의 모든 디지스터와 암호기는 스프링부터 쉽게 사용할 수 있도록 설계되었습니다(인스턴트, 의존성 주입...).또, 스레드 세이프이기 때문에, Spring과 같은 싱글톤 지향의 환경에서도, 동기 염려 없이 사용할 수 있습니다.자세한 내용은 봄 2, 봄 3.0, 봄 3.1입니다.
Spring Security(구 Acegi Security)는 옵션 통합으로 패스워드 암호화 및 보안 프레임워크에 대한 대조 작업을 수행합니다.또, 보다 안전한 패스워드 암호화 메커니즘을 사용해 유저의 패스워드의 시큐러티를 향상해, 한층 더 고도의 설정과 제어를 실현합니다.더 배우기.
데이터베이스 암호와 같은 중요한 정보를 포함하여 응용 프로그램 구성 파일의 전체 또는 일부를 암호화하는 고급 기능을 제공합니다.암호화된 구성을 플레인, 스프링 기반 및/또는 휴지 상태 대응 애플리케이션에 심리스하게 통합할 수 있습니다.더 배우기.
개발자가 암호화된 데이터를 초기화하고 유지 보수 작업 또는 스크립트에 암호화/복호화/디지스트 작업을 포함할 수 있도록 사용하기 쉬운 CLI(Command Line Interface) 도구를 제공합니다.더 배우기.
Apache Wicket에 통합되어 보안 애플리케이션의 URL을 더욱 강력하게 암호화합니다.
개발자가 데이터에 대해 실제로 무엇을 하고 있는지 더 잘 이해할 수 있도록 지원하는 포괄적인 가이드 및 javadoc 문서.
견고한 문자 집합 지원. 원래 문자 집합이 무엇이든 텍스트를 적절히 암호화하고 요약할 수 있도록 설계되었습니다.일본어, 한국어, 아랍어...부호화 또는 플랫폼의 문제는 없습니다.
매우 고도의 설정 기능:개발자는 예를 들어 암호화에 사용되는 암호를 원격 HTTPS 서버에 요청하도록 "암호화자"에게 지시하는 등의 기술을 구현할 수 있습니다.보안 요구 사항을 충족할 수 있습니다.
2019년 12월 12일 갱신
CBC 등의 다른 모드와 달리 GCM 모드에서는 IV를 예측할 수 없습니다.유일한 요건은 IV가 특정 키를 사용하여 호출할 때마다 고유해야 한다는 것입니다.특정 키에 대해 1회 반복하면 보안이 손상될 수 있습니다.이를 실현하기 위한 간단한 방법은 다음과 같이 강력한 의사 난수 발생기의 난수 IV를 사용하는 것입니다.
시퀀스 또는 타임스탬프를 IV로 사용하는 것도 가능하지만 들리는 것처럼 간단하지는 않을 수 있습니다.예를 들어 시스템이 영속적인 스토어에서 IV로 이미 사용되고 있는 시퀀스를 올바르게 추적하지 않으면 시스템 재부팅 후 IV가 반복될 수 있습니다.마찬가지로 완벽한 시계는 없다.컴퓨터 시계 재조정 등
또, 2^32 의 호출 후에 키를 회전할 필요가 있습니다.IV 요건에 대한 자세한 내용은 이 답변과 NIST 권고사항을 참조하십시오.
이것은 제가 Java 8에서 작성한 암호화 및 복호화 코드입니다.다음 사항을 고려하여 작성했습니다.누군가가 이것을 유용하게 여겨주길 바란다.
암호화 알고리즘: 256비트 키를 가진 블록 암호 AES는 충분히 안전한 것으로 간주됩니다.전체 메시지를 암호화하려면 모드를 선택해야 합니다.인증된 암호화(비밀성과 무결성을 모두 제공)를 권장합니다.GCM, CCM 및 EAX는 가장 일반적으로 사용되는 인증 암호화 모드입니다.GCM은 보통 GCM 전용 명령어를 제공하는 인텔 아키텍처에서 뛰어난 성능을 발휘합니다.이들 3가지 모드는 모두 CTR 기반(카운터 기반) 모드이므로 패딩이 필요하지 않습니다.따라서 패딩 관련 공격에 취약하지 않습니다.
GCM은 IV를 선택합니다.'' 또는 '예측불능'의 됩니다.유일한 요건은 랜덤 또는 예측 불가능이어야 한다는 것입니다.'''가
SecuredRandom
를 생성하는 합니다.class는 암호로 강력한 의사 난수를 생성합니다.은 「」로 할 수 .getInstance()
부터는 Java 8을 사용하는 .getInstanceStrong()
에서 및 되는 가장Provider
NIST는 상호 운용성, 효율성 및 설계의 단순성을 높이기 위해 GCM에 96비트 IV를 권장합니다.
SecureRandom
바이트 2^16 바이트를 한 후 .암호 텍스트를 해독하려면 수신자가 IV를 알아야 합니다.따라서 IV는 암호 텍스트와 함께 전송되어야 합니다.일부 실장에서는 IV가 AD(Associated Data)로서 송신됩니다.이는 인증 태그가 암호 텍스트와 IV 양쪽에서 계산됨을 의미합니다.그러나 그것은 필수가 아니다.IV는 단순히 암호 텍스트로 프리펜딩할 수 있습니다.이는 의도적인 공격이나 네트워크/파일 시스템 오류로 인해 전송 중에 IV가 변경되면 인증 태그 검증이 실패하기 때문입니다.
문자열은 불변하기 때문에 사용 후 클리어 텍스트메시지 또는 키를 유지하기 위해 문자열을 사용하지 마십시오.그런 다음 이러한 삼촌형 문자열은 메모리에 남아 힙 덤프에 표시될 수 있습니다.같은 이유로 클라이언트는 이러한 암호화 또는 복호화 방법을 호출하면 메시지 또는 키를 보유하고 있는 모든 변수 또는 배열을 더 이상 필요하지 않게 된 후 클리어해야 합니다.
일반적인 권장 사항에 따라 코드에 하드 코딩된 공급자가 없습니다.
마지막으로 네트워크 또는 스토리지를 통한 전송에서는 키 또는 암호 텍스트를 Base64 인코딩을 사용하여 인코딩해야 합니다.Base64에 대한 자세한 내용은 여기를 참조하십시오.Java 8 접근 방식을 따라야 합니다.
바이트 배열은 다음을 사용하여 지울 수 있습니다.
Arrays.fill(clearTextMessageByteArray, Byte.MIN_VALUE);
8에서는 8을 클리어할 수 이 없습니다.SecretKeyspec
★★★★★★★★★★★★★★★★★」SecretKey
2개의 에서는, 「2」의 「2」의 「2」의 「2」의 「2」의 「2」의 「2」의 「2」의 「2」의 「2」의 「2」의 「2」의 「2」의 「2」의 「2」의 「2」의 「3」의 「3」의 「2」의 「2」의 「2개의 인터페이스에서는 실장」의 방법이 것 같습니다.destroy()
「」의Destroyable
다음 코드에서, 다른 메서드가 쓰여져 있습니다.SecretKeySpec
★★★★★★★★★★★★★★★★★」SecretKey
반사를 이용하여.
키는 다음 두 가지 방법 중 하나를 사용하여 생성해야 합니다.
키는 비밀번호와 같은 비밀이지만 사람이 사용하는 비밀번호와는 달리 암호화 알고리즘에 의해 사용되는 것이므로 위의 방법만을 사용하여 생성해야 합니다.
package com.sapbasu.javastudy;
import java.lang.reflect.Field;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class Crypto {
private static final int AUTH_TAG_SIZE = 128; // bits
// NIST recommendation: "For IVs, it is recommended that implementations
// restrict support to the length of 96 bits, to
// promote interoperability, efficiency, and simplicity of design."
private static final int IV_LEN = 12; // bytes
// number of random number bytes generated before re-seeding
private static final double PRNG_RESEED_INTERVAL = Math.pow(2, 16);
private static final String ENCRYPT_ALGO = "AES/GCM/NoPadding";
private static final List<Integer> ALLOWED_KEY_SIZES = Arrays
.asList(new Integer[] {128, 192, 256}); // bits
private static SecureRandom prng;
// Used to keep track of random number bytes generated by PRNG
// (for the purpose of re-seeding)
private static int bytesGenerated = 0;
public byte[] encrypt(byte[] input, SecretKeySpec key) throws Exception {
Objects.requireNonNull(input, "Input message cannot be null");
Objects.requireNonNull(key, "key cannot be null");
if (input.length == 0) {
throw new IllegalArgumentException("Length of message cannot be 0");
}
if (!ALLOWED_KEY_SIZES.contains(key.getEncoded().length * 8)) {
throw new IllegalArgumentException("Size of key must be 128, 192 or 256");
}
Cipher cipher = Cipher.getInstance(ENCRYPT_ALGO);
byte[] iv = getIV(IV_LEN);
GCMParameterSpec gcmParamSpec = new GCMParameterSpec(AUTH_TAG_SIZE, iv);
cipher.init(Cipher.ENCRYPT_MODE, key, gcmParamSpec);
byte[] messageCipher = cipher.doFinal(input);
// Prepend the IV with the message cipher
byte[] cipherText = new byte[messageCipher.length + IV_LEN];
System.arraycopy(iv, 0, cipherText, 0, IV_LEN);
System.arraycopy(messageCipher, 0, cipherText, IV_LEN,
messageCipher.length);
return cipherText;
}
public byte[] decrypt(byte[] input, SecretKeySpec key) throws Exception {
Objects.requireNonNull(input, "Input message cannot be null");
Objects.requireNonNull(key, "key cannot be null");
if (input.length == 0) {
throw new IllegalArgumentException("Input array cannot be empty");
}
byte[] iv = new byte[IV_LEN];
System.arraycopy(input, 0, iv, 0, IV_LEN);
byte[] messageCipher = new byte[input.length - IV_LEN];
System.arraycopy(input, IV_LEN, messageCipher, 0, input.length - IV_LEN);
GCMParameterSpec gcmParamSpec = new GCMParameterSpec(AUTH_TAG_SIZE, iv);
Cipher cipher = Cipher.getInstance(ENCRYPT_ALGO);
cipher.init(Cipher.DECRYPT_MODE, key, gcmParamSpec);
return cipher.doFinal(messageCipher);
}
public byte[] getIV(int bytesNum) {
if (bytesNum < 1) throw new IllegalArgumentException(
"Number of bytes must be greater than 0");
byte[] iv = new byte[bytesNum];
prng = Optional.ofNullable(prng).orElseGet(() -> {
try {
prng = SecureRandom.getInstanceStrong();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("Wrong algorithm name", e);
}
return prng;
});
if (bytesGenerated > PRNG_RESEED_INTERVAL || bytesGenerated == 0) {
prng.setSeed(prng.generateSeed(bytesNum));
bytesGenerated = 0;
}
prng.nextBytes(iv);
bytesGenerated = bytesGenerated + bytesNum;
return iv;
}
private static void clearSecret(Destroyable key)
throws IllegalArgumentException, IllegalAccessException,
NoSuchFieldException, SecurityException {
Field keyField = key.getClass().getDeclaredField("key");
keyField.setAccessible(true);
byte[] encodedKey = (byte[]) keyField.get(key);
Arrays.fill(encodedKey, Byte.MIN_VALUE);
}
}
암호화 키는 주로 다음 두 가지 방법으로 생성할 수 있습니다.
패스워드 없음
KeyGenerator keyGen = KeyGenerator.getInstance("AES"); keyGen.init(KEY_LEN, SecureRandom.getInstanceStrong()); SecretKey secretKey = keyGen.generateKey(); SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getEncoded(), "AES"); Crypto.clearSecret(secretKey); // After encryption or decryption with key Crypto.clearSecret(secretKeySpec);
비밀번호 포함
SecureRandom random = SecureRandom.getInstanceStrong(); byte[] salt = new byte[32]; random.nextBytes(salt); PBEKeySpec keySpec = new PBEKeySpec(password, salt, iterations, keyLength); SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); SecretKey secretKey = keyFactory.generateSecret(keySpec); SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getEncoded(), "AES"); Crypto.clearSecret(secretKey); // After encryption or decryption with key Crypto.clearSecret(secretKeySpec);
코멘트에 근거한 갱신
@와 같이, 제은 어떤 @MaartenBodewes도 .String
이치노가 이 궁금하게 여길 해서 그 String
.
와 같이 은 「」으로 .String
않은 생각입니다.왜냐하면, 그것은 좋은 이 아니기 때문이다.왜냐하면String
불변하기 때문에 사용 후 지울 수 없습니다.그리고 우리가 알다시피, 심지어String
가비지 컬렉터는 강력한 참조를 가지고 있지 않기 때문에 즉시 힙에서 제거하지 않습니다.①은String
프로그램이 액세스할 수 없는 경우에도 알 수 없는 시간 동안 메모리 내에 계속 남아 있습니다.문제는 해당 기간 동안 힙 덤프를 실행하면 중요한 정보가 노출된다는 것입니다.따라서 바이트 배열 또는 문자 배열의 모든 중요한 정보를 처리하고 목적에 부합하면 배열에 0을 채우는 것이 좋습니다.
이 할되지 않은 String
해서 '어울리다'를 encrypt
★★★★★★★★★★★★★★★★★」decrypt
(다른 입력 키는 상기의 코드 스니펫을 사용하여 생성할 수 있습니다.)
A String
는 다음과 할 수 .
byte[] inputBytes = inputString.getBytes(StandardCharsets.UTF_8);
의 경우 Java 8의 경우String
는 히프를 사용하여 으로 히프에 저장됩니다.UTF-16
부호화를 실시합니다. 우리는 ,, 는을 사용했습니다.UTF-8
여기에서는 보통 보다 적은 공간을 차지하기 때문에UTF-16
(ASCII ASCII)
마찬가지로 암호화된 바이트 배열도 다음과 같이 String으로 변환할 수 있습니다.
String encryptedString = new String(encryptedBytes, StandardCharsets.UTF_8);
이거 어때:
private static byte[] xor(final byte[] input, final byte[] secret) {
final byte[] output = new byte[input.length];
if (secret.length == 0) {
throw new IllegalArgumentException("empty security key");
}
int spos = 0;
for (int pos = 0; pos < input.length; ++pos) {
output[pos] = (byte) (input[pos] ^ secret[spos]);
++spos;
if (spos >= secret.length) {
spos = 0;
}
}
return output;
}
저는 잘 작동하고 컴팩트한 편입니다.
은 기, 음, 음, 음, 음, 음, 음, 음, 음, 음, here, here, here, here, here, here, here, here, here, here, here, here with, with,java.*
★★★★★★★★★★★★★★★★★」javax.crypto.*
기밀성과 무결성을 제공하는 바이트 암호화에 대한 의존성.킬로바이트 단위의 단문 메시지에 대한 선택 일반 텍스트 공격에서는 구별이 불가능해야 합니다.
it it를 한다.AES
GCM
128비트 키를 합니다.PBKDF2
입력된 비밀번호에서 정적 염분(salt)을 클릭합니다.이것에 의해, 패스워드를 강제하는 것이 어려워져, 키 전체에 엔트로피가 분산됩니다.
랜덤 초기화 벡터(IV)가 생성되어 암호문 앞에 추가됩니다. 바이트 「」, 「」는,0x01
는 첫 번째 바이트로 '버전'으로 부가됩니다.
가 Message Authentication Code(MAC; 메시지 인증 코드)에 의해 Code(MAC; 메시지 인증 코드.AES/GCM
.
외부 의존 관계가 없는 암호화 클래스는 기밀성과 무결성을 제공합니다.
package ch.n1b.tcrypt.utils;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import javax.crypto.*;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
/**
* This class implements AES-GCM symmetric key encryption with a PBKDF2 derived password.
* It provides confidentiality and integrity of the plaintext.
*
* @author Thomas Richner
* @created 2018-12-07
*/
public class AesGcmCryptor {
// https://crypto.stackexchange.com/questions/26783/ciphertext-and-tag-size-and-iv-transmission-with-aes-in-gcm-mode
private static final byte VERSION_BYTE = 0x01;
private static final int VERSION_BYTE_LENGTH = 1;
private static final int AES_KEY_BITS_LENGTH = 128;
// fixed AES-GCM constants
private static final String GCM_CRYPTO_NAME = "AES/GCM/NoPadding";
private static final int GCM_IV_BYTES_LENGTH = 12;
private static final int GCM_TAG_BYTES_LENGTH = 16;
// can be tweaked, more iterations = more compute intensive to brute-force password
private static final int PBKDF2_ITERATIONS = 1024;
// protects against rainbow tables
private static final byte[] PBKDF2_SALT = hexStringToByteArray("4d3fe0d71d2abd2828e7a3196ea450d4");
public String encryptString(char[] password, String plaintext) throws CryptoException {
byte[] encrypted = null;
try {
encrypted = encrypt(password, plaintext.getBytes(StandardCharsets.UTF_8));
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException //
| InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException //
| InvalidKeySpecException e) {
throw new CryptoException(e);
}
return byteArrayToHexString(encrypted);
}
public String decryptString(char[] password, String ciphertext)
throws CryptoException {
byte[] ct = hexStringToByteArray(ciphertext);
byte[] plaintext = null;
try {
plaintext = decrypt(password, ct);
} catch (AEADBadTagException e) {
throw new CryptoException(e);
} catch ( //
NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeySpecException //
| InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException //
| BadPaddingException e) {
throw new CryptoException(e);
}
return new String(plaintext, StandardCharsets.UTF_8);
}
/**
* Decrypts an AES-GCM encrypted ciphertext and is
* the reverse operation of {@link AesGcmCryptor#encrypt(char[], byte[])}
*
* @param password passphrase for decryption
* @param ciphertext encrypted bytes
* @return plaintext bytes
* @throws NoSuchPaddingException
* @throws NoSuchAlgorithmException
* @throws NoSuchProviderException
* @throws InvalidKeySpecException
* @throws InvalidAlgorithmParameterException
* @throws InvalidKeyException
* @throws BadPaddingException
* @throws IllegalBlockSizeException
* @throws IllegalArgumentException if the length or format of the ciphertext is bad
* @throws CryptoException
*/
public byte[] decrypt(char[] password, byte[] ciphertext)
throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeySpecException,
InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
// input validation
if (ciphertext == null) {
throw new IllegalArgumentException("ciphertext cannot be null");
}
if (ciphertext.length <= VERSION_BYTE_LENGTH + GCM_IV_BYTES_LENGTH + GCM_TAG_BYTES_LENGTH) {
throw new IllegalArgumentException("ciphertext too short");
}
// the version must match, we don't decrypt other versions
if (ciphertext[0] != VERSION_BYTE) {
throw new IllegalArgumentException("wrong version: " + ciphertext[0]);
}
// input seems legit, lets decrypt and check integrity
// derive key from password
SecretKey key = deriveAesKey(password, PBKDF2_SALT, AES_KEY_BITS_LENGTH);
// init cipher
Cipher cipher = Cipher.getInstance(GCM_CRYPTO_NAME);
GCMParameterSpec params = new GCMParameterSpec(GCM_TAG_BYTES_LENGTH * 8,
ciphertext,
VERSION_BYTE_LENGTH,
GCM_IV_BYTES_LENGTH
);
cipher.init(Cipher.DECRYPT_MODE, key, params);
final int ciphertextOffset = VERSION_BYTE_LENGTH + GCM_IV_BYTES_LENGTH;
// add version and IV to MAC
cipher.updateAAD(ciphertext, 0, ciphertextOffset);
// decipher and check MAC
return cipher.doFinal(ciphertext, ciphertextOffset, ciphertext.length - ciphertextOffset);
}
/**
* Encrypts a plaintext with a password.
* <p>
* The encryption provides the following security properties:
* Confidentiality + Integrity
* <p>
* This is achieved my using the AES-GCM AEAD blockmode with a randomized IV.
* <p>
* The tag is calculated over the version byte, the IV as well as the ciphertext.
* <p>
* Finally the encrypted bytes have the following structure:
* <pre>
* +-------------------------------------------------------------------+
* | | | | |
* | version | IV bytes | ciphertext bytes | tag |
* | | | | |
* +-------------------------------------------------------------------+
* Length: 1B 12B len(plaintext) bytes 16B
* </pre>
* Note: There is no padding required for AES-GCM, but this also implies that
* the exact plaintext length is revealed.
*
* @param password password to use for encryption
* @param plaintext plaintext to encrypt
* @throws NoSuchAlgorithmException
* @throws NoSuchProviderException
* @throws NoSuchPaddingException
* @throws InvalidAlgorithmParameterException
* @throws InvalidKeyException
* @throws BadPaddingException
* @throws IllegalBlockSizeException
* @throws InvalidKeySpecException
*/
public byte[] encrypt(char[] password, byte[] plaintext)
throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException,
InvalidKeySpecException {
// initialise random and generate IV (initialisation vector)
SecretKey key = deriveAesKey(password, PBKDF2_SALT, AES_KEY_BITS_LENGTH);
final byte[] iv = new byte[GCM_IV_BYTES_LENGTH];
SecureRandom random = SecureRandom.getInstanceStrong();
random.nextBytes(iv);
// encrypt
Cipher cipher = Cipher.getInstance(GCM_CRYPTO_NAME);
GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_BYTES_LENGTH * 8, iv);
cipher.init(Cipher.ENCRYPT_MODE, key, spec);
// add IV to MAC
final byte[] versionBytes = new byte[]{VERSION_BYTE};
cipher.updateAAD(versionBytes);
cipher.updateAAD(iv);
// encrypt and MAC plaintext
byte[] ciphertext = cipher.doFinal(plaintext);
// prepend VERSION and IV to ciphertext
byte[] encrypted = new byte[1 + GCM_IV_BYTES_LENGTH + ciphertext.length];
int pos = 0;
System.arraycopy(versionBytes, 0, encrypted, 0, VERSION_BYTE_LENGTH);
pos += VERSION_BYTE_LENGTH;
System.arraycopy(iv, 0, encrypted, pos, iv.length);
pos += iv.length;
System.arraycopy(ciphertext, 0, encrypted, pos, ciphertext.length);
return encrypted;
}
/**
* We derive a fixed length AES key with uniform entropy from a provided
* passphrase. This is done with PBKDF2/HMAC256 with a fixed count
* of iterations and a provided salt.
*
* @param password passphrase to derive key from
* @param salt salt for PBKDF2 if possible use a per-key salt, alternatively
* a random constant salt is better than no salt.
* @param keyLen number of key bits to output
* @return a SecretKey for AES derived from a passphrase
* @throws NoSuchAlgorithmException
* @throws InvalidKeySpecException
*/
private SecretKey deriveAesKey(char[] password, byte[] salt, int keyLen)
throws NoSuchAlgorithmException, InvalidKeySpecException {
if (password == null || salt == null || keyLen <= 0) {
throw new IllegalArgumentException();
}
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(password, salt, PBKDF2_ITERATIONS, keyLen);
SecretKey pbeKey = factory.generateSecret(spec);
return new SecretKeySpec(pbeKey.getEncoded(), "AES");
}
/**
* Helper to convert hex strings to bytes.
* <p>
* May be used to read bytes from constants.
*/
private static byte[] hexStringToByteArray(String s) {
if (s == null) {
throw new IllegalArgumentException("Provided `null` string.");
}
int len = s.length();
if (len % 2 != 0) {
throw new IllegalArgumentException("Invalid length: " + len);
}
byte[] data = new byte[len / 2];
for (int i = 0; i < len - 1; i += 2) {
byte b = (byte) toHexDigit(s, i);
b <<= 4;
b |= toHexDigit(s, i + 1);
data[i / 2] = b;
}
return data;
}
private static int toHexDigit(String s, int pos) {
int d = Character.digit(s.charAt(pos), 16);
if (d < 0) {
throw new IllegalArgumentException("Cannot parse hex digit: " + s + " at " + pos);
}
return d;
}
private static String byteArrayToHexString(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02X", b));
}
return sb.toString();
}
public class CryptoException extends Exception {
public CryptoException(Throwable cause) {
super(cause);
}
}
}
여기에서는 프로젝트 전체를 멋진 CLI로 소개합니다.https://github.com/trichner/tcrypt
편집: 지금 바로 적용encryptString
★★★★★★★★★★★★★★★★★」decryptString
다음은 Spring Singleton으로서 meta64.com에서 구현한 내용입니다.각 콜에 대해서도 동작하는 ciper 인스턴스를 작성하고 나서, 「동기」콜을 삭제할 수 있습니다만, 「암호」는 스레드 세이프가 아닙니다.
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope("singleton")
public class Encryptor {
@Value("${aeskey}")
private String keyStr;
private Key aesKey = null;
private Cipher cipher = null;
synchronized private void init() throws Exception {
if (keyStr == null || keyStr.length() != 16) {
throw new Exception("bad aes key configured");
}
if (aesKey == null) {
aesKey = new SecretKeySpec(keyStr.getBytes(), "AES");
cipher = Cipher.getInstance("AES");
}
}
synchronized public String encrypt(String text) throws Exception {
init();
cipher.init(Cipher.ENCRYPT_MODE, aesKey);
return toHexString(cipher.doFinal(text.getBytes()));
}
synchronized public String decrypt(String text) throws Exception {
init();
cipher.init(Cipher.DECRYPT_MODE, aesKey);
return new String(cipher.doFinal(toByteArray(text)));
}
public static String toHexString(byte[] array) {
return DatatypeConverter.printHexBinary(array);
}
public static byte[] toByteArray(String s) {
return DatatypeConverter.parseHexBinary(s);
}
/*
* DO NOT DELETE
*
* Use this commented code if you don't like using DatatypeConverter dependency
*/
// public static String toHexStringOld(byte[] bytes) {
// StringBuilder sb = new StringBuilder();
// for (byte b : bytes) {
// sb.append(String.format("%02X", b));
// }
// return sb.toString();
// }
//
// public static byte[] toByteArrayOld(String s) {
// int len = s.length();
// byte[] data = new byte[len / 2];
// for (int i = 0; i < len; i += 2) {
// data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i +
// 1), 16));
// }
// return data;
// }
}
https://www.bouncycastle.org/ 등의 사용을 검토하고 있습니다.이 라이브러리는 스누핑으로부터 보호하고 싶은 것은 여러 가지 암호뿐이라는 것을 알고 있습니다만, Base64를 사용해도 실제로는 보호되지 않습니다.
많은 사람들이 이미 말했듯이 DES나 AES와 같이 과도하게 사용되는 표준 사이퍼를 사용해야 합니다.
AES를 사용하여 Java에서 문자열을 암호화 및 복호화하는 간단한 예.
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class EncryptorDemo {
public static String encrypt(String key, String randomVector, String value) {
try {
IvParameterSpec iv = new IvParameterSpec(randomVector.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] encrypted = cipher.doFinal(value.getBytes());
System.out.println("encrypted text: " + Base64.encodeBase64String(encrypted));
return Base64.encodeBase64String(encrypted);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static String decrypt(String key, String randomVector, String encrypted) {
try {
IvParameterSpec iv = new IvParameterSpec(randomVector.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] originalText = cipher.doFinal(Base64.decodeBase64(encrypted));
System.out.println("decrypted text: " + new String(originalText));
return new String(originalText);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
String key = "JavasEncryptDemo"; // 128 bit key
String randomVector = "RandomJavaVector"; // 16 bytes IV
decrypt(key, randomVector, encrypt(key, randomVector, "Anything you want to encrypt!"));
}
}
다음은 Java가 지원하는 몇 가지 링크입니다.
이 예에서는 대량의 데이터를 암호화(AES, Blowfish, RC2, 3DES 등의 대칭 암호화 알고리즘 사용)하는 방법을 보여 줍니다.데이터는 EncryptBytes, EncryptString, EncryptBytes 중 하나의 암호화 방법으로 청크로 전달됩니다.ENC 또는 EncryptStringENC.(메서드명은 입력 유형(문자열 또는 바이트 배열) 및 반환 유형(인코딩된 문자열 또는 바이트 배열)을 나타냅니다.FirstChunk 및 LastChunk 속성은 청크가 스트림에서 암호화되는 첫 번째, 중간 또는 마지막 중 어느 쪽인지를 나타내기 위해 사용됩니다.기본적으로 FirstChunk와 LastChunk는 모두 true입니다. 즉, 전달된 데이터가 전체 양임을 의미합니다.
다음은 복사/붙여넣기 솔루션입니다.코드를 제공하지 않지만 @Constantino의 답변을 읽고 투표할 것을 권장합니다.초기화 벡터(IV)는 소금과 같기 때문에 비밀로 할 필요가 없습니다.GCM은 처음이고 AAD는 옵션이며 특정 상황에서만 사용됩니다.환경변수에 키를 설정합니다.SECRET_KEY_BASE
KeePass와 같은 것을 사용하여 32글자의 비밀번호를 생성합니다.이 솔루션은 제 루비 솔루션을 본떠 만든 것입니다.
public static String encrypt(String s) {
try {
byte[] input = s.getBytes("UTF-8");
String keyString = System.getProperty("SECRET_KEY_BASE", System.getenv("SECRET_KEY_BASE"));
if (keyString == null || keyString.length() == 0) {
Logger.error(Utils.class, "encrypt()", "$SECRET_KEY_BASE is not set.");
return null;
}
byte[] keyBytes = keyString.getBytes("UTF-8");
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
// generate IV
SecureRandom secureRandom = SecureRandom.getInstanceStrong();
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
byte[] ivBytes = new byte[cipher.getBlockSize()];
secureRandom.nextBytes(ivBytes);
GCMParameterSpec gcmSpec = new GCMParameterSpec(96, ivBytes); // 96 bit tag length
cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmSpec);
// generate AAD
// byte[] aadBytes = new byte[cipher.getBlockSize()];
// secureRandom.nextBytes(aadBytes);
// cipher.updateAAD(aadBytes);
// encrypt
byte[] encrypted = cipher.doFinal(input);
byte[] returnBytes = new byte[ivBytes.length + encrypted.length];
// byte[] returnBytes = new byte[ivBytes.length + aadBytes.length + encrypted.length];
System.arraycopy(ivBytes, 0, returnBytes, 0, ivBytes.length);
// System.arraycopy(aadBytes, 0, returnBytes, ivBytes.length, aadBytes.length);
System.arraycopy(encrypted, 0, returnBytes, ivBytes.length, encrypted.length);
// System.arraycopy(encrypted, 0, returnBytes, ivBytes.length+aadBytes.length, encrypted.length);
String encryptedString = Base64.getEncoder().encodeToString(returnBytes);
return encryptedString;
} catch (UnsupportedEncodingException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException |
InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
Logger.error(Utils.class, "encrypt()", "Could not encrypt string: " + e.getMessage());
return null;
}
}
public static String decrypt(String s) {
if (s == null || s.length() == 0) return "";
try {
byte[] encrypted = Base64.getDecoder().decode(s);
String keyString = System.getProperty("SECRET_KEY_BASE", System.getenv("SECRET_KEY_BASE"));
if (keyString == null || keyString.length() == 0) {
Logger.error(Utils.class, "encrypt()", "$SECRET_KEY_BASE is not set.");
return null;
}
byte[] keyBytes = keyString.getBytes("UTF-8");
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
byte[] ivBytes = new byte[cipher.getBlockSize()];
System.arraycopy(encrypted, 0, ivBytes, 0, ivBytes.length);
GCMParameterSpec gcmSpec = new GCMParameterSpec(96, ivBytes);
cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmSpec);
// cipher.updateAAD(encrypted, ivBytes.length, cipher.getBlockSize());
byte[] decrypted = cipher.doFinal(encrypted, cipher.getBlockSize(), encrypted.length - cipher.getBlockSize());
// byte[] decrypted = cipher.doFinal(encrypted, cipher.getBlockSize()*2, encrypted.length - cipher.getBlockSize()*2);
String decryptedString = new String(decrypted, "UTF-8");
return decryptedString;
} catch (NoSuchAlgorithmException | NoSuchPaddingException | UnsupportedEncodingException | InvalidKeyException |
InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
Logger.error(Utils.class, "decrypt()", "Could not decrypt string: " + e.getMessage());
return null;
}
}
다음은 예를 제시하겠습니다.
String s = "This is a test.";
String enc = Utils.encrypt(s);
System.out.println(enc);
// fQHfYjbD+xAuN5XzH2ojk/EWNeKXUrKRSfx8LU+5dpuKkM/pueCMBjKCZw==
String dec = Utils.decrypt(enc);
System.out.println(dec);
// This is a test.
암호화/복호화 코드 생성을 위한 자동화된 도구를 고려해 보는 것이 좋습니다.https://www.stringencrypt.com/java-encryption/
문자열 또는 파일 암호화에 대해 매번 다른 암호화 및 암호 해독 코드를 생성할 수 있습니다.
RSA, AES 등을 사용하지 않고 고속 문자열 암호화를 수행할 경우 매우 편리합니다.
샘플 결과:
// encrypted with https://www.stringencrypt.com (v1.1.0) [Java]
// szTest = "Encryption in Java!"
String szTest = "\u9E3F\uA60F\uAE07\uB61B\uBE1F\uC62B\uCE2D\uD611" +
"\uDE03\uE5FF\uEEED\uF699\uFE3D\u071C\u0ED2\u1692" +
"\u1E06\u26AE\u2EDC";
for (int iatwS = 0, qUJQG = 0; iatwS < 19; iatwS++)
{
qUJQG = szTest.charAt(iatwS);
qUJQG ++;
qUJQG = ((qUJQG << 5) | ( (qUJQG & 0xFFFF) >> 11)) & 0xFFFF;
qUJQG -= iatwS;
qUJQG = (((qUJQG & 0xFFFF) >> 6) | (qUJQG << 10)) & 0xFFFF;
qUJQG ^= iatwS;
qUJQG -= iatwS;
qUJQG = (((qUJQG & 0xFFFF) >> 3) | (qUJQG << 13)) & 0xFFFF;
qUJQG ^= 0xFFFF;
qUJQG ^= 0xB6EC;
qUJQG = ((qUJQG << 8) | ( (qUJQG & 0xFFFF) >> 8)) & 0xFFFF;
qUJQG --;
qUJQG = (((qUJQG & 0xFFFF) >> 5) | (qUJQG << 11)) & 0xFFFF;
qUJQG ++;
qUJQG ^= 0xFFFF;
qUJQG += iatwS;
szTest = szTest.substring(0, iatwS) + (char)(qUJQG & 0xFFFF) + szTest.substring(iatwS + 1);
}
System.out.println(szTest);
저희 회사에서는 항상 사용하고 있습니다.
public static String encryptParams(String myTextInput) {
String myKey = "40674244454045cb9a70040a30e1c007";
String myVector = "@1B2c3D4e5F6g7H8";
String encData = "";
try{
JavaEncryprtionUtil encUtil = new JavaEncryprtionUtil();
encData = Base64.encodeToString(encUtil.encrypt(myTextInput.getBytes("UTF-8"), myKey.getBytes("UTF-8"), myVector.getBytes("UTF-8")),Base64.DEFAULT);
System.out.println(encData);
}catch(NoSuchAlgorithmException ex){
ex.printStackTrace();
}catch(NoSuchPaddingException ex){
ex.printStackTrace();
}catch(InvalidKeyException ex){
ex.printStackTrace();
}catch(InvalidAlgorithmParameterException ex){
ex.printStackTrace();
}catch(IllegalBlockSizeException ex){
ex.printStackTrace();
}catch(BadPaddingException ex){
ex.printStackTrace();
}catch(UnsupportedEncodingException ex){
ex.printStackTrace();
}
return encData;
}
String s1="arshad";
char[] s2=s1.toCharArray();
int s3= s2.length;
System.out.println(s3);
int i=0;
// for(int j=0;j<s3;j++)
// System.out.println(s2[j]);
for(i=0;i<((s3)/2);i++) {
char z,f=10;
z=(char) (s2[i] * f);
s2[i]=s2[(s3-1)-i];
s2[(s3-1)-i]=z;
String b=new String(s2);
print(b); }
언급URL : https://stackoverflow.com/questions/1205135/how-to-encrypt-string-in-java
'programing' 카테고리의 다른 글
C에서 열거형(enum)을 정의하려면 어떻게 해야 합니까? (0) | 2022.08.11 |
---|---|
컴포넌트가 정의되어 있지만 사용되지 않는 vars는 사용되지 않습니다. (0) | 2022.08.11 |
비동기 데이터 내의 Vuej + SSR + router.push() (0) | 2022.08.11 |
Nuxt.js에서 Vuetify 중단점이 작동하지 않음 (0) | 2022.08.11 |
호출하지 않고 실행 중인 계산 함수 (0) | 2022.08.11 |