it-swarm-korea.com

Java에서 공개 키로 파일을 암호화하는 표준 방법은 무엇입니까?

Java에 암호화 된 데이터를 서버로 다시 보내는 클라이언트 앱을 작성 중입니다. 모든 클라이언트에는 RSA 공개 키가 있습니다. 지정된 파일을 암호화하여 보낼 수있는 표준 방법이 있습니까? 다시 서버로?

this one 와 같은 웹 사이트에서 일반적으로 실제 파일을 암호화하기 위해 대칭 키가 생성 되고이 키는 RSA 공개 키로 암호화되어 파일과 함께 전송됩니다. 암호화 된 대칭 키와 대칭으로 암호화 된 파일을 패키지화하는 Java 또는 표준 파일 형식 (추가 라이브러리 없음))의 표준 방법이 있습니까?

물론 자체 파일 형식을 디자인 할 수도 있지만 가능하면 표준을 고수하고 싶습니다.

15

일반적인 표준 암호화 형식 중 하나는 CMS (Cryptographic Message Syntax)이며 PKCS # 7에서 발전한 것입니다. BouncyCastle이이를 지원합니다. 또한 서명을 허용하며 예를 들어 S/MIME 메일 메시지 보안의 기초입니다. 과잉이지만 광범위하게 지원됩니다.

예제 코드는 Bouncy Castle에서 제공합니다. CMS의 경우 org.bouncycastle.cms.test , 아마도 엔벌 로프 된 데이터 예를 참조하십시오.

다른 옵션은 W3C에 의해 표준화 된 XML 암호화 형식입니다. Java 예제의 경우 XML 암호화 탐색, 1 부 를 참조하거나 XSS4J로 수행하십시오. Java에서 XML 암호화 구현

8
nealmcb

가장 간단하고 확실한 파일 암호화 방법은 gpg 또는 pgp를 셸 아웃하고 호출하여 암호화하는 것입니다. 나는 그것이 우아하지 않다는 것을 알고 있지만, 많은 암호 코드를 검토 한 경험에서 이것은 암호에 대해 많이 알 필요없이 솔루션을 안전하게 유지하는 가장 간단한 방법입니다. 직접 수행하려면 암호화 작동 방식에 대해 자세히 알아야하며 보안 문제의 위험이 증가합니다.

암호화 된 데이터에 대한 진정성 보호가 있는지 확인하십시오 (예 : 암호화 및 서명/MAC) : 기밀성과 진정성 보호가 모두 필요합니다. 매우 일반적인 오류는 암호화하지만 서명에 실패합니다. 이 오류는 미묘한 보안 문제를 유발할 수 있습니다. (만약 당신이 암호학자가 아니라면, 이것이 당신에게 이해가되지 않는다는 것을 알고 있습니다. 나는 이것이 명백한 것이 아니라는 것을 알고 있습니다. 그러나 나는 암호 학자입니다. 만약 당신이 IND-CCA2와 INT-CTXT에 익숙하다면, 당신은 이해할 것입니다 이러한 개념에 익숙하지 않은 경우 원하는 개념에 대해 읽거나 내 말을 받아 들일 수 있습니다. 암호는 까다로운 항목입니다. 귀하가 링크 한 웹 사이트는이 실수를합니다. 안전한 암호화에 대한 조언을 위해 해당 웹 사이트에 의존해서는 안됩니다. 귀하의 질문에 언급 한 strawman 접근 방식 도이 결함으로 인해 어려움을 겪고 있으며 여기에 제안 된 답변 중 일부에도 비슷한 단점이 있습니다.

클라이언트와 서버가 실시간으로 연결된 경우 매우 좋은 대안은 클라이언트와 서버간에 TLS 암호화 채널을 열고 해당 채널을 통해 데이터를 보내는 것입니다. 클라이언트가 서버의 공개 키/인증서를 확인해야합니다. 클라이언트를 인증하려면 클라이언트에 클라이언트 인증서를 제공하고 서버에 클라이언트가 클라이언트 인증서를 제공하고 해당 인증서의 유효성을 검증하도록하십시오.

3
D.W.

@ JPP, 당신 의이 의견은 내가 당신이 잘못된 질문을하고 있다고 생각하게합니다 :

클라이언트 앱은 사용자가 저장된 파일에 대한 읽기/쓰기 액세스 권한이 있더라도 내용이 서버에 게시되지 않은 사용자 활동 로그를 저장합니다. 또한 사용자는 내용을 검사 할 수 없어야합니다 (파일은 퀴즈 응용 프로그램의 연습, 진행률 및 오류 보고서입니다).

클라이언트 앱이 로그 파일을 작성하고 암호화하면 클라이언트 앱은 암호화 키에 액세스 할 수 있습니다.
그러나 클라이언트 앱 사용자가 파일을 읽지 못하게하려고합니다.
다음과 같은 이유로 작동하지 않습니다.

  1. 사용자는 파일 내용 및 암호화 키를 포함하여 클라이언트 앱이 액세스 할 수있는 모든 항목에 액세스 할 수 있습니다.
  2. 사용자는 자신의 프로세스에서 실행되는 모든 것을 제어 할 수 있습니다. 즉, 클라이언트 응용 프로그램에 디버거를 연결할 수도 있습니다. 따라서 똑똑하고 암호화 키를 난독 화하는 방법을 알아 내지 마십시오.

나에게 당신의 문제는 액세스 제어의 하나처럼 들립니다. 사용자가 내용에 액세스하지 않고 어떻게 클라이언트 앱에서 로그 파일을 작성할 수 있습니까?
하나의 방법은 다른 사용자 컨텍스트에서 실행되는 로컬 서비스 (예 : Windows의 Windows 서비스)를 구축하는 것입니다. 클라이언트 응용 프로그램은 서비스와 통신하고 서비스는 일반 사용자가 접근 할 수없는 보호 된 위치에있는 파일에 내용을 씁니다.
다른 대안은 @ D.W입니다. 서버에 직접 전송하고 내용을 파일에 쓰지 마십시오.

이 두 가지 솔루션의 문제점은 이전에 언급 한 바와 같습니다. 사용자는 자신의 프로세스에서 무엇이든 제어 할 수 있으며 서버 또는 로컬 Windows 서비스로 전송 된 모든 요청을 차단하거나 수정할 수 있습니다.

3
AviD

예를 들어 Bouncy Castle에서 이미 사용 가능한 항목을 크게 코딩하지 않고 표준 라이브러리를 사용하여 수행 할 수있는 방법은 없습니다.

여기에 내가 생각해 낸 것이 있습니다. 클라이언트 쪽에서 다음과 같이 파일 plainfile을 (를) 암호화합니다.

// install security provider
if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
    Security.addProvider(new BouncyCastleProvider());
}

// load public certificate for signing
KeyStore publicKs = KeyStore.getInstance(KeyStore.getDefaultType());
publicKs.load(new FileInputStream("/path/to/publicKeystore.ks"), null);
TrustedCertificateEntry entry = (TrustedCertificateEntry) publicKs.getEntry("public", null);
X509Certificate cert = (X509Certificate) entry.getTrustedCertificate();

// create CMS envelope data;
// check http://www.ietf.org/rfc/rfc3852.txt pages 15-16 for details
CMSEnvelopedDataGenerator envelopedDataGen = new CMSEnvelopedDataGenerator();

// specify that generated symmetric key will be encrypted by with this public key
envelopedDataGen.addKeyTransRecipient(cert);

// automatically generate the AES key, encrypt the data, and encrypt the key
CMSEnvelopedData data = envelopedDataGen.generate(new CMSProcessableFile(plainfile),
        CMSEnvelopedDataGenerator.AES256_CBC, 256, BouncyCastleProvider.PROVIDER_NAME);

byte[] encryptedData = data.getEncoded();

그런 다음 서버에서 다음과 같이이 데이터를 해독 할 수 있습니다.

// load private key
KeyStore privateKs = KeyStore.getInstance(KeyStore.getDefaultType());
privateKs.load(new FileInputStream("/path/to/privateKeystore.ks"), null);
PrivateKeyEntry privateKeyEntry = (PrivateKeyEntry) privateKs.getEntry("privatekey",
        new KeyStore.PasswordProtection("password".toCharArray()));
PrivateKey privateKey = privateKeyEntry.getPrivateKey();

byte[] encryptedData = ...

// parse CMS envelope data
CMSEnvelopedDataParser envelopedDataParser = new CMSEnvelopedDataParser(new ByteArrayInputStream(encryptedData));

// expect exactly one recipient
Collection<?> recipients = envelopedDataParser.getRecipientInfos().getRecipients();
if (recipients.size() != 1)
    throw new IllegalArgumentException();

// retrieve recipient and decode it
RecipientInformation recipient = (RecipientInformation) recipients.iterator().next();
byte[] decryptedData = recipient.getContent(privateKey, BouncyCastleProvider.PROVIDER_NAME);

중요한 내용이 누락되었거나 여기에 실수가있는 경우 의견을 남겨주세요 ... 감사합니다!

1