Skip to content

Commit

Permalink
Merge branch 'fix/SDKCRYPTJ-60' into 'main'
Browse files Browse the repository at this point in the history
fix: Fix wrong character encoding of key pair password

See merge request dracoon/sdks-public/sdk-crypto-java!10
  • Loading branch information
mkellnhofer committed Jun 10, 2024
2 parents 4c82a75 + f2723c5 commit b440e25
Show file tree
Hide file tree
Showing 41 changed files with 223 additions and 95 deletions.
46 changes: 29 additions & 17 deletions src/main/java/com/dracoon/sdk/crypto/Crypto.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import com.dracoon.sdk.crypto.error.InvalidPasswordException;
import com.dracoon.sdk.crypto.internal.AesGcmFileDecryptionCipher;
import com.dracoon.sdk.crypto.internal.AesGcmFileEncryptionCipher;
import com.dracoon.sdk.crypto.internal.CryptoUtils;
import com.dracoon.sdk.crypto.internal.Validator;
import com.dracoon.sdk.crypto.model.EncryptedFileKey;
import com.dracoon.sdk.crypto.model.PlainFileKey;
Expand Down Expand Up @@ -165,10 +166,11 @@ private static char[] encryptEncodePrivateKey(PrivateKey key, char[] password)
throws InvalidPasswordException, CryptoSystemException {
OutputEncryptor encryptor;
try {
char[] encodedPassword = CryptoUtils.toUtf8CharArray(password);
encryptor = new JceOpenSSLPKCS8EncryptorBuilder(PKCS8Generator.AES_256_CBC)
.setProvider("BC")
.setIterationCount(HASH_ITERATION_COUNT)
.setPassword(password)
.setPassword(encodedPassword)
.build();
} catch (OperatorCreationException e) {
throw new CryptoSystemException("Could not encrypt private key. Creation of PKCS8" +
Expand Down Expand Up @@ -209,23 +211,17 @@ private static PrivateKey decryptDecodePrivateKey(char[] key, char[] password)
}

PrivateKeyInfo pkInfo;
try {
if (obj instanceof PKCS8EncryptedPrivateKeyInfo) {
PKCS8EncryptedPrivateKeyInfo epkInfo = (PKCS8EncryptedPrivateKeyInfo) obj;
InputDecryptorProvider decryptor = new JceOpenSSLPKCS8DecryptorProviderBuilder()
.setProvider("BC")
.build(password);
pkInfo = epkInfo.decryptPrivateKeyInfo(decryptor);
} else {
throw new InvalidKeyPairException("Could not decrypt private key. Provided key " +
"is not a PKCS8 encrypted private key.");
if (obj instanceof PKCS8EncryptedPrivateKeyInfo) {
PKCS8EncryptedPrivateKeyInfo epkInfo = (PKCS8EncryptedPrivateKeyInfo) obj;
try {
char[] encodedPassword = CryptoUtils.toUtf8CharArray(password);
pkInfo = decryptPrivateKey(epkInfo, encodedPassword);
} catch (InvalidPasswordException e) {
pkInfo = decryptPrivateKey(epkInfo, password);
}
} catch (OperatorCreationException e) {
throw new CryptoSystemException("Could not decrypt private key. Creation of PKCS8 " +
"decryptor failed.", e);
} catch (PKCSException e) {
throw new InvalidPasswordException("Could not decrypt private key. Invalid private " +
"key password.", e);
} else {
throw new InvalidKeyPairException("Could not decrypt private key. Provided key " +
"is not a PKCS8 encrypted private key.");
}

try {
Expand All @@ -240,6 +236,22 @@ private static PrivateKey decryptDecodePrivateKey(char[] key, char[] password)
}
}

private static PrivateKeyInfo decryptPrivateKey(PKCS8EncryptedPrivateKeyInfo epkInfo,
char[] password) throws InvalidPasswordException, CryptoSystemException {
try {
InputDecryptorProvider decryptor = new JceOpenSSLPKCS8DecryptorProviderBuilder()
.setProvider("BC")
.build(password);
return epkInfo.decryptPrivateKeyInfo(decryptor);
} catch (OperatorCreationException e) {
throw new CryptoSystemException("Could not decrypt private key. Creation of PKCS8 " +
"decryptor failed.", e);
} catch (PKCSException e) {
throw new InvalidPasswordException("Could not decrypt private key. Invalid private " +
"key password.", e);
}
}

private static char[] encodePublicKey(PublicKey key) throws InvalidKeyPairException {
try {
CharArrayWriter charWriter = new CharArrayWriter();
Expand Down
15 changes: 15 additions & 0 deletions src/main/java/com/dracoon/sdk/crypto/internal/CryptoUtils.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.dracoon.sdk.crypto.internal;

import org.bouncycastle.util.Strings;

public class CryptoUtils {

private CryptoUtils() {
Expand All @@ -19,4 +21,17 @@ public static <T extends CryptoVersion> T findCryptoVersionEnum(T[] enums, Strin
return null;
}

public static char[] toUtf8CharArray(char[] chars) {
byte[] utf8Bytes = Strings.toUTF8ByteArray(chars);
return toCharArray(utf8Bytes);
}

private static char[] toCharArray(byte[] bytes) {
char[] chars = new char[bytes.length];
for (int i = 0; i < bytes.length; i++) {
chars[i] = (char) (bytes[i] & 0xFF);
}
return chars;
}

}
10 changes: 8 additions & 2 deletions src/test/java/com/dracoon/sdk/crypto/CryptoBaseTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,13 @@ protected UserKeyPair generateUserKeyPair(String version, String pw)

// ### KEY PAIR CHECK TESTS ###

protected void testCheckUserKeyPair(String uprkFileName, String upukFileName, String pw,
protected void testCheckUserKeyPair(String uprkFileName, String upukFileName, String pwFileName,
Boolean mustBeOk) throws UnknownVersionException, InvalidKeyPairException,
CryptoSystemException {
UserPrivateKey uprk = readUserPrivateKey(uprkFileName);
UserPublicKey upuk = readUserPublicKey(upukFileName);
UserKeyPair ukp = new UserKeyPair(uprk, upuk);
String pw = readPassword(pwFileName);
boolean testCheck = Crypto.checkUserKeyPair(ukp, toCharArray(pw));

if (Objects.equals(mustBeOk, Boolean.TRUE)) {
Expand Down Expand Up @@ -96,11 +97,12 @@ protected void validatePlainFileKey(PlainFileKey pfk, PlainFileKey testPfk) {
assertEquals("Version is incorrect!", pfk.getVersion(), testPfk.getVersion());
}

protected PlainFileKey decryptFileKey(String efkFileName, String upkFileName, String pw)
protected PlainFileKey decryptFileKey(String efkFileName, String upkFileName, String pwFileName)
throws UnknownVersionException, InvalidFileKeyException, InvalidKeyPairException,
InvalidPasswordException, CryptoSystemException {
EncryptedFileKey efk = readEncryptedFileKey(efkFileName);
UserPrivateKey upk = readUserPrivateKey(upkFileName);
String pw = readPassword(pwFileName);
return Crypto.decryptFileKey(efk, upk, toCharArray(pw));
}

Expand Down Expand Up @@ -159,6 +161,10 @@ private static UserPublicKey readUserPublicKey(String fileName)
return TestUtils.readUserPublicKey(fileName);
}

private static String readPassword(String fileName) {
return TestUtils.readPassword(fileName);
}

protected static EncryptedFileKey readEncryptedFileKey(String fileName)
throws UnknownVersionException {
return TestUtils.readEncryptedFileKey(fileName);
Expand Down
29 changes: 11 additions & 18 deletions src/test/java/com/dracoon/sdk/crypto/TestUtils.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.dracoon.sdk.crypto;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
Expand Down Expand Up @@ -76,26 +75,20 @@ public static PlainFileKey readPlainFileKey(String fileName)
}

private static <T> T readData(Class<? extends T> clazz, String fileName) {
if (fileName == null) {
return null;
}

try {
InputStream is = TestUtils.class.getClassLoader().getResourceAsStream(fileName);
Reader rd = new BufferedReader(new InputStreamReader(is));

T obj = gson.fromJson(rd, clazz);

rd.close();
is.close();
String data = readResourceFile(fileName);
return data != null ? gson.fromJson(data, clazz) : null;
}

return obj;
} catch (IOException e) {
throw new RuntimeException("Reading test resource JSON failed!", e);
}
public static String readPassword(String fileName) {
return readResourceFile(fileName);
}

public static byte[] readFile(String fileName) {
String data = readResourceFile(fileName);
return data != null ? decodeBase64(data) : null;
}

private static String readResourceFile(String fileName) {
if (fileName == null) {
return null;
}
Expand All @@ -115,7 +108,7 @@ public static byte[] readFile(String fileName) {
in.close();
is.close();

return decodeBase64(data);
return data;
} catch (IOException e) {
throw new RuntimeException("Reading test resource file failed!", e);
}
Expand Down
23 changes: 15 additions & 8 deletions src/test/java/com/dracoon/sdk/crypto/integration/CryptoTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,12 @@
import com.dracoon.sdk.crypto.error.UnknownVersionException;
import com.dracoon.sdk.crypto.model.EncryptedFileKey;
import com.dracoon.sdk.crypto.model.PlainFileKey;
import com.dracoon.sdk.crypto.model.UserKeyPair;
import org.junit.Test;
import org.junit.experimental.categories.Category;

@Category(IntegrationTest.class)
public abstract class CryptoTest extends CryptoBaseTest {

public abstract String password(UserKeyPair.Version version);

public abstract String data(String subPath);

public abstract String file(String subPath);
Expand All @@ -29,7 +26,7 @@ public void testCheckUserKeyPair_Rsa2048_Success() throws UnknownVersionExceptio
testCheckUserKeyPair(
data("kp_rsa2048/private_key.json"),
data("kp_rsa2048/public_key.json"),
password(UserKeyPair.Version.RSA2048),
data("kp_rsa2048/pw.txt"),
true);
}

Expand All @@ -39,7 +36,7 @@ public void testCheckUserKeyPair_Rsa4096_Success() throws UnknownVersionExceptio
testCheckUserKeyPair(
data("kp_rsa4096/private_key.json"),
data("kp_rsa4096/public_key.json"),
password(UserKeyPair.Version.RSA4096),
data("kp_rsa4096/pw.txt"),
true);
}

Expand All @@ -49,7 +46,17 @@ public void testCheckUserKeyPair_Rsa4096_KdfV2_Success() throws UnknownVersionEx
testCheckUserKeyPair(
data("kp_rsa4096_kdfv2/private_key.json"),
data("kp_rsa4096_kdfv2/public_key.json"),
password(UserKeyPair.Version.RSA4096),
data("kp_rsa4096_kdfv2/pw.txt"),
true);
}

@Test
public void testCheckUserKeyPair_Rsa4096_PwEncUtf8WithUmlautAndEmoticon_Success()
throws UnknownVersionException, InvalidKeyPairException, CryptoSystemException {
testCheckUserKeyPair(
data("kp_rsa4096_pw_utf8_umlaut_emoticon/private_key.json"),
data("kp_rsa4096_pw_utf8_umlaut_emoticon/public_key.json"),
data("kp_rsa4096_pw_utf8_umlaut_emoticon/pw.txt"),
true);
}

Expand Down Expand Up @@ -90,7 +97,7 @@ public void testDecryptFileKey_Rsa2048_Success() throws UnknownVersionException,
PlainFileKey testPfk = decryptFileKey(
data("fk_rsa2048_aes256gcm/enc_file_key.json"),
data("kp_rsa2048/private_key.json"),
password(UserKeyPair.Version.RSA2048));
data("kp_rsa2048/pw.txt"));

validatePlainFileKey(pfk, testPfk);
}
Expand All @@ -104,7 +111,7 @@ public void testDecryptFileKey_Rsa4096_Success() throws UnknownVersionException,
PlainFileKey testPfk = decryptFileKey(
data("fk_rsa4096_aes256gcm/enc_file_key.json"),
data("kp_rsa4096/private_key.json"),
password(UserKeyPair.Version.RSA4096));
data("kp_rsa4096/pw.txt"));

validatePlainFileKey(pfk, testPfk);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
package com.dracoon.sdk.crypto.integration.csharp;

import com.dracoon.sdk.crypto.model.UserKeyPair;

public class CsharpCryptoTest extends com.dracoon.sdk.crypto.integration.CryptoTest {

@Override
public String password(UserKeyPair.Version version) {
return "acw9q857n(";
}

@Override
public String data(String subPath) {
return CsharpTestHelper.data(subPath);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
package com.dracoon.sdk.crypto.integration.js;

import com.dracoon.sdk.crypto.model.UserKeyPair;
import com.dracoon.sdk.crypto.error.CryptoSystemException;
import com.dracoon.sdk.crypto.error.InvalidKeyPairException;
import com.dracoon.sdk.crypto.error.UnknownVersionException;
import org.junit.Test;

public class JsCryptoTest extends com.dracoon.sdk.crypto.integration.CryptoTest {

@Override
public String password(UserKeyPair.Version version) {
return "Qwer1234!";
}

@Override
public String data(String subPath) {
return JsTestHelper.data(subPath);
Expand All @@ -19,4 +17,14 @@ public String file(String subPath) {
return JsTestHelper.file(subPath);
}

@Test
public void testCheckUserKeyPair_Rsa4096_PwEncIsoWithUmlaut_Success()
throws UnknownVersionException, InvalidKeyPairException, CryptoSystemException {
testCheckUserKeyPair(
data("kp_rsa4096_pw_iso_umlaut/private_key.json"),
data("kp_rsa4096_pw_iso_umlaut/public_key.json"),
data("kp_rsa4096_pw_iso_umlaut/pw.txt"),
true);
}

}
Original file line number Diff line number Diff line change
@@ -1,21 +1,7 @@
package com.dracoon.sdk.crypto.integration.swift;

import com.dracoon.sdk.crypto.model.UserKeyPair;

public class SwiftCryptoTest extends com.dracoon.sdk.crypto.integration.CryptoTest {

@Override
public String password(UserKeyPair.Version version) {
switch (version) {
case RSA2048:
return "Pass1234!";
case RSA4096:
return "ABC123DEFF456";
default:
return "";
}
}

@Override
public String data(String subPath) {
return SwiftTestHelper.data(subPath);
Expand Down
Loading

0 comments on commit b440e25

Please sign in to comment.