Skip to content

Commit 6bf12d9

Browse files
committed
JCE: return SecretKeySpec of appropriate size from KeyAgreement.engineGenerateSecret()
1 parent 930f28c commit 6bf12d9

File tree

2 files changed

+292
-7
lines changed

2 files changed

+292
-7
lines changed

src/main/java/com/wolfssl/provider/jce/WolfCryptKeyAgreement.java

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -330,19 +330,66 @@ protected SecretKey engineGenerateSecret(String algorithm)
330330
InvalidKeyException {
331331

332332
byte secret[] = engineGenerateSecret();
333+
byte[] keyMaterial = null;
334+
SecretKey ret = null;
333335

334336
log("generating SecretKey for " + algorithm);
335337

336-
if (algorithm.equals("DES")) {
337-
return (SecretKey)new DESKeySpec(secret);
338+
try {
339+
if (algorithm.equals("DES")) {
340+
/* DES requires 8 bytes */
341+
if (secret.length < DESKeySpec.DES_KEY_LEN) {
342+
throw new InvalidKeyException(
343+
"Shared secret is too short for DES key");
344+
}
345+
keyMaterial = new byte[DESKeySpec.DES_KEY_LEN];
346+
System.arraycopy(secret, 0, keyMaterial, 0,
347+
DESKeySpec.DES_KEY_LEN);
348+
ret = new SecretKeySpec(keyMaterial, algorithm);
349+
350+
} else if (algorithm.equals("DESede")) {
351+
/* DESede requires 24 bytes (3-key) */
352+
if (secret.length < DESedeKeySpec.DES_EDE_KEY_LEN) {
353+
throw new InvalidKeyException(
354+
"Shared secret is too short for DESede key");
355+
}
356+
keyMaterial = new byte[DESedeKeySpec.DES_EDE_KEY_LEN];
357+
System.arraycopy(secret, 0, keyMaterial, 0,
358+
DESedeKeySpec.DES_EDE_KEY_LEN);
359+
ret = new SecretKeySpec(keyMaterial, algorithm);
360+
361+
} else if (algorithm.equals("AES")) {
362+
/* AES requires specific key sizes: 128, 192, or 256 bits.
363+
* Use first 16 bytes (128-bit) by default, or 32 bytes
364+
* (256-bit) if shared secret is >= 32 bytes. */
365+
int aesKeyLen = 16; /* default to AES-128 */
366+
if (secret.length >= 32) {
367+
aesKeyLen = 32; /* use AES-256 if possible */
368+
} else if (secret.length >= 24) {
369+
aesKeyLen = 24; /* use AES-192 if >= 24 bytes */
370+
}
371+
372+
if (secret.length < aesKeyLen) {
373+
throw new InvalidKeyException(
374+
"Shared secret is too short for AES key " +
375+
"(need at least " + aesKeyLen + " bytes)");
376+
}
338377

339-
} else if (algorithm.equals("DESede")) {
340-
return (SecretKey)new DESedeKeySpec(secret);
378+
keyMaterial = new byte[aesKeyLen];
379+
System.arraycopy(secret, 0, keyMaterial, 0, aesKeyLen);
380+
ret = new SecretKeySpec(keyMaterial, algorithm);
341381

342-
} else {
343-
/* AES and default */
344-
return new SecretKeySpec(secret, algorithm);
382+
} else {
383+
/* Other algorithms: use full shared secret */
384+
ret = new SecretKeySpec(secret, algorithm);
385+
}
386+
387+
} finally {
388+
zeroArray(secret);
389+
zeroArray(keyMaterial);
345390
}
391+
392+
return ret;
346393
}
347394

348395
/**

src/test/java/com/wolfssl/provider/jce/test/WolfCryptKeyAgreementTest.java

Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939

4040
import javax.crypto.KeyAgreement;
4141
import javax.crypto.ShortBufferException;
42+
import javax.crypto.SecretKey;
43+
import javax.crypto.Cipher;
4244
import javax.crypto.spec.DHParameterSpec;
4345

4446
import java.security.Security;
@@ -773,6 +775,242 @@ private void threadRunnerKeyAgreeTest(String algo)
773775
}
774776
}
775777

778+
@Test
779+
public void testDHGenerateSecretKeyForDES()
780+
throws NoSuchProviderException, NoSuchAlgorithmException,
781+
InvalidParameterSpecException, InvalidKeyException,
782+
InvalidAlgorithmParameterException {
783+
784+
/* Skip 512-bit DH params in FIPS mode. FIPS 186-4 only allows
785+
* 1024, 2048, and 3072-bit DH parameter generation */
786+
if (Fips.enabled) {
787+
return;
788+
}
789+
790+
/* create DH params */
791+
AlgorithmParameterGenerator paramGen =
792+
AlgorithmParameterGenerator.getInstance("DH");
793+
paramGen.init(512);
794+
795+
AlgorithmParameters params;
796+
try {
797+
params = paramGen.generateParameters();
798+
}
799+
catch (RuntimeException e) {
800+
/* 512-bit DH parameter generation may not be supported due to
801+
* wolfSSL enforcing minimum parameter sizes. Skip test if
802+
* generation fails. */
803+
if (e.getMessage() != null && e.getMessage().contains(
804+
"Bad function argument")) {
805+
return;
806+
}
807+
throw e;
808+
}
809+
810+
DHParameterSpec dhParams =
811+
(DHParameterSpec)params.getParameterSpec(DHParameterSpec.class);
812+
813+
/* initialize key pair generator */
814+
KeyPairGenerator keyGen =
815+
KeyPairGenerator.getInstance("DH", "wolfJCE");
816+
keyGen.initialize(dhParams, secureRandom);
817+
818+
KeyAgreement aKeyAgree = KeyAgreement.getInstance("DH", "wolfJCE");
819+
KeyAgreement bKeyAgree = KeyAgreement.getInstance("DH", "wolfJCE");
820+
821+
KeyPair aPair = keyGen.generateKeyPair();
822+
KeyPair bPair = keyGen.generateKeyPair();
823+
824+
aKeyAgree.init(aPair.getPrivate());
825+
bKeyAgree.init(bPair.getPrivate());
826+
827+
aKeyAgree.doPhase(bPair.getPublic(), true);
828+
bKeyAgree.doPhase(aPair.getPublic(), true);
829+
830+
/* Test generateSecret("DES") returns SecretKey, not DESKeySpec */
831+
SecretKey desKeyA = null;
832+
SecretKey desKeyB = null;
833+
try {
834+
desKeyA = aKeyAgree.generateSecret("DES");
835+
assertNotNull(desKeyA);
836+
assertTrue(desKeyA instanceof SecretKey);
837+
assertEquals("DES", desKeyA.getAlgorithm());
838+
839+
/* Verify key can be used with Cipher */
840+
Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
841+
cipher.init(Cipher.ENCRYPT_MODE, desKeyA);
842+
843+
} catch (ClassCastException e) {
844+
fail("generateSecret(\"DES\") should return SecretKey, " +
845+
"not DESKeySpec: " + e.getMessage());
846+
847+
} catch (Exception e) {
848+
fail("Unexpected exception during DES key generation: " +
849+
e.getMessage());
850+
}
851+
852+
/* Test generateSecret("DESede") returns SecretKey */
853+
try {
854+
/* bKeyAgree already has doPhase() completed, just generate
855+
* secret with different algorithm */
856+
SecretKey desedeKey = bKeyAgree.generateSecret("DESede");
857+
assertNotNull(desedeKey);
858+
assertTrue(desedeKey instanceof SecretKey);
859+
assertEquals("DESede", desedeKey.getAlgorithm());
860+
861+
/* Verify key can be used with Cipher */
862+
Cipher cipher =
863+
Cipher.getInstance("DESede/ECB/PKCS5Padding");
864+
cipher.init(Cipher.ENCRYPT_MODE, desedeKey);
865+
866+
} catch (ClassCastException e) {
867+
fail("generateSecret(\"DESede\") should return SecretKey, " +
868+
"not DESedeKeySpec: " + e.getMessage());
869+
870+
} catch (Exception e) {
871+
fail("Unexpected exception during DESede key generation: " +
872+
e.getMessage());
873+
}
874+
875+
/* Test generateSecret("AES") returns SecretKey with proper size */
876+
KeyAgreement cKeyAgree = KeyAgreement.getInstance("DH", "wolfJCE");
877+
KeyPair cPair = keyGen.generateKeyPair();
878+
cKeyAgree.init(cPair.getPrivate());
879+
cKeyAgree.doPhase(aPair.getPublic(), true);
880+
881+
try {
882+
SecretKey aesKey = cKeyAgree.generateSecret("AES");
883+
assertNotNull(aesKey);
884+
assertTrue(aesKey instanceof SecretKey);
885+
assertEquals("AES", aesKey.getAlgorithm());
886+
887+
/* AES key should be 16, 24, or 32 bytes */
888+
byte[] encoded = aesKey.getEncoded();
889+
assertTrue("AES key length should be 16, 24, or 32 bytes",
890+
encoded.length == 16 || encoded.length == 24 ||
891+
encoded.length == 32);
892+
893+
/* Verify key can be used with Cipher */
894+
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
895+
cipher.init(Cipher.ENCRYPT_MODE, aesKey);
896+
897+
/* Test encryption/decryption */
898+
byte[] plaintext = "Test AES encryption".getBytes();
899+
byte[] ciphertext = cipher.doFinal(plaintext);
900+
cipher.init(Cipher.DECRYPT_MODE, aesKey,
901+
cipher.getParameters());
902+
byte[] decrypted = cipher.doFinal(ciphertext);
903+
assertArrayEquals(plaintext, decrypted);
904+
905+
} catch (Exception e) {
906+
fail("Unexpected exception during AES key generation: " +
907+
e.getMessage());
908+
}
909+
}
910+
911+
@Test
912+
public void testECDHGenerateSecretKeyForDES()
913+
throws NoSuchProviderException, NoSuchAlgorithmException,
914+
InvalidParameterSpecException, InvalidKeyException,
915+
InvalidAlgorithmParameterException {
916+
917+
/* initialize key pair generator */
918+
KeyPairGenerator keyGen =
919+
KeyPairGenerator.getInstance("EC", "wolfJCE");
920+
ECGenParameterSpec ecsp = new ECGenParameterSpec("secp256r1");
921+
keyGen.initialize(ecsp);
922+
923+
KeyAgreement aKeyAgree = KeyAgreement.getInstance("ECDH", "wolfJCE");
924+
KeyAgreement bKeyAgree = KeyAgreement.getInstance("ECDH", "wolfJCE");
925+
926+
KeyPair aPair = keyGen.generateKeyPair();
927+
KeyPair bPair = keyGen.generateKeyPair();
928+
929+
aKeyAgree.init(aPair.getPrivate());
930+
bKeyAgree.init(bPair.getPrivate());
931+
932+
aKeyAgree.doPhase(bPair.getPublic(), true);
933+
bKeyAgree.doPhase(aPair.getPublic(), true);
934+
935+
/* Test generateSecret("DES") returns SecretKey, not DESKeySpec */
936+
try {
937+
SecretKey desKey = aKeyAgree.generateSecret("DES");
938+
assertNotNull(desKey);
939+
assertTrue(desKey instanceof SecretKey);
940+
assertEquals("DES", desKey.getAlgorithm());
941+
942+
/* Verify key can be used with Cipher */
943+
Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
944+
cipher.init(Cipher.ENCRYPT_MODE, desKey);
945+
946+
} catch (ClassCastException e) {
947+
fail("generateSecret(\"DES\") should return SecretKey, " +
948+
"not DESKeySpec: " + e.getMessage());
949+
950+
} catch (Exception e) {
951+
fail("Unexpected exception during DES key generation: " +
952+
e.getMessage());
953+
}
954+
955+
/* Test generateSecret("DESede") returns SecretKey */
956+
try {
957+
/* bKeyAgree already has doPhase() completed, just generate
958+
* secret with different algorithm */
959+
SecretKey desedeKey = bKeyAgree.generateSecret("DESede");
960+
assertNotNull(desedeKey);
961+
assertTrue(desedeKey instanceof SecretKey);
962+
assertEquals("DESede", desedeKey.getAlgorithm());
963+
964+
/* Verify key can be used with Cipher */
965+
Cipher cipher =
966+
Cipher.getInstance("DESede/ECB/PKCS5Padding");
967+
cipher.init(Cipher.ENCRYPT_MODE, desedeKey);
968+
969+
} catch (ClassCastException e) {
970+
fail("generateSecret(\"DESede\") should return SecretKey, " +
971+
"not DESedeKeySpec: " + e.getMessage());
972+
973+
} catch (Exception e) {
974+
fail("Unexpected exception during DESede key generation: " +
975+
e.getMessage());
976+
}
977+
978+
/* Test generateSecret("AES") returns SecretKey with proper size */
979+
KeyAgreement cKeyAgree = KeyAgreement.getInstance("ECDH", "wolfJCE");
980+
KeyPair cPair = keyGen.generateKeyPair();
981+
cKeyAgree.init(cPair.getPrivate());
982+
cKeyAgree.doPhase(aPair.getPublic(), true);
983+
984+
try {
985+
SecretKey aesKey = cKeyAgree.generateSecret("AES");
986+
assertNotNull(aesKey);
987+
assertTrue(aesKey instanceof SecretKey);
988+
assertEquals("AES", aesKey.getAlgorithm());
989+
990+
/* AES key should be 16, 24, or 32 bytes */
991+
byte[] encoded = aesKey.getEncoded();
992+
assertTrue("AES key length should be 16, 24, or 32 bytes",
993+
encoded.length == 16 || encoded.length == 24 ||
994+
encoded.length == 32);
995+
996+
/* Verify key can be used with Cipher */
997+
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
998+
cipher.init(Cipher.ENCRYPT_MODE, aesKey);
999+
1000+
/* Test encryption/decryption */
1001+
byte[] plaintext = "Test AES encryption".getBytes();
1002+
byte[] ciphertext = cipher.doFinal(plaintext);
1003+
cipher.init(Cipher.DECRYPT_MODE, aesKey,
1004+
cipher.getParameters());
1005+
byte[] decrypted = cipher.doFinal(ciphertext);
1006+
assertArrayEquals(plaintext, decrypted);
1007+
1008+
} catch (Exception e) {
1009+
fail("Unexpected exception during AES key generation: " +
1010+
e.getMessage());
1011+
}
1012+
}
1013+
7761014
@Test
7771015
public void testThreadedKeyAgreement()
7781016
throws InterruptedException, NoSuchAlgorithmException {

0 commit comments

Comments
 (0)