diff --git a/JenkinsfilePerformance b/JenkinsfilePerformance index 2c7d40f2a..65963634c 100644 --- a/JenkinsfilePerformance +++ b/JenkinsfilePerformance @@ -353,6 +353,7 @@ pipeline { 'ibm.jceplus.jmh.MessageDigestInstanceBenchmark', \ 'ibm.jceplus.jmh.MLDSABenchmark', \ 'ibm.jceplus.jmh.MLKEMBenchmark', \ + 'ibm.jceplus.jmh.PBEBenchmark', \ 'ibm.jceplus.jmh.PBKDF2Benchmark', \ 'ibm.jceplus.jmh.RandomBenchmark', \ 'ibm.jceplus.jmh.RSAKeyGeneratorBenchmark', \ diff --git a/README.md b/README.md index 9d707a673..f84eec894 100644 --- a/README.md +++ b/README.md @@ -268,170 +268,212 @@ take effect. The following algorithms are registered by the OpenJCEPlus and OpenJCEPlusFIPS providers. -| Algorithm Type | Algorithm Name | OpenJCEPlusFIPS | OpenJCEPlus | Notes | -| --------------------------|----------------------------|-----------------|--------------|--------------| -AlgorithmParameterGenerator | CCM |X |X | | -AlgorithmParameterGenerator | DSA | |X | | -AlgorithmParameterGenerator | DiffieHellman |X |X | | -AlgorithmParameterGenerator | EC |X |X | | -AlgorithmParameterGenerator | GCM |X |X | | -AlgorithmParameters | AES |X |X | | -AlgorithmParameters | CCM |X |X | | -AlgorithmParameters | ChaCha20-Poly1305 | |X | | -AlgorithmParameters | DESede | |X | | -AlgorithmParameters | DSA |X |X | | -AlgorithmParameters | DiffieHellman |X |X | | -AlgorithmParameters | EC |X |X | | -AlgorithmParameters | GCM |X |X | | -AlgorithmParameters | OAEP |X |X | | -AlgorithmParameters | RSAPSS |X |X | | -Cipher | AES |X |X | | -Cipher | AES/CCM/NoPadding |X |X | | -Cipher | AES/GCM/NoPadding |X |X | | -Cipher | ChaCha20 | |X | | -Cipher | ChaCha20-Poly1305 | |X | | -Cipher | DESede | |X | | -Cipher | RSA |X |X | | -KeyAgreement | DiffieHellman |X |X | | -KeyAgreement | ECDH |X |X | | -KeyAgreement | X25519 | |X | | -KeyAgreement | X448 | |X | | -KeyAgreement | XDH | |X | | -KeyEncapsulationMechanism | ML-KEM-512 | |X |[ML-KEM](#ml-kem)| -KeyEncapsulationMechanism | ML-KEM-768 | |X |[ML-KEM](#ml-kem)| -KeyEncapsulationMechanism | ML-KEM-1024 | |X |[ML-KEM](#ml-kem)| -KeyFactory | DSA |X |X | | -KeyFactory | DiffieHellman |X |X | | -KeyFactory | EC |X |X | | -KeyFactory | Ed25519 | |X | | -KeyFactory | Ed448 | |X | | -KeyFactory | EdDSA | |X | | -KeyFactory | ML-DSA-44 | |X |[ML-DSA](#ml-dsa)| -KeyFactory | ML-DSA-65 | |X |[ML-DSA](#ml-dsa)| -KeyFactory | ML-DSA-87 | |X |[ML-DSA](#ml-dsa)| -KeyFactory | ML-KEM-512 | |X |[ML-KEM](#ml-kem)| -KeyFactory | ML-KEM-768 | |X |[ML-KEM](#ml-kem)| -KeyFactory | ML-KEM-1024 | |X |[ML-KEM](#ml-kem)| -KeyFactory | RSA |X |X | | -KeyFactory | RSAPSS |X |X | | -KeyFactory | X25519 | |X | | -KeyFactory | X448 | |X | | -KeyFactory | XDH | |X | | -KeyGenerator | AES |X |X | | -KeyGenerator | ChaCha20 | |X | | -KeyGenerator | DESede | |X | | -KeyGenerator | HmacMD5 | |X | | -KeyGenerator | HmacSHA1 | |X | | -KeyGenerator | HmacSHA224 |X |X | | -KeyGenerator | HmacSHA256 |X |X | | -KeyGenerator | HmacSHA3-224 |X |X | | -KeyGenerator | HmacSHA3-256 |X |X | | -KeyGenerator | HmacSHA3-384 |X |X | | -KeyGenerator | HmacSHA3-512 |X |X | | -KeyGenerator | HmacSHA384 |X |X | | -KeyGenerator | HmacSHA512 |X |X | | -KeyGenerator | SunTls12KeyMaterial |X |X | | -KeyGenerator | SunTls12MasterSecret |X |X | | -KeyGenerator | SunTls12Prf |X |X | | -KeyGenerator | SunTls12RsaPremasterSecret |X |X | | -KeyGenerator | SunTlsKeyMaterial |X |X | | -KeyGenerator | SunTlsMasterSecret |X |X | | -KeyGenerator | SunTlsPrf |X |X | | -KeyGenerator | SunTlsRsaPremasterSecret |X |X | | -KeyGenerator | kda-hkdf-with-sha1 | |X | | -KeyGenerator | kda-hkdf-with-sha224 |X |X | | -KeyGenerator | kda-hkdf-with-sha256 |X |X | | -KeyGenerator | kda-hkdf-with-sha384 |X |X | | -KeyGenerator | kda-hkdf-with-sha512 |X |X | | -KeyPairGenerator | DSA | |X | | -KeyPairGenerator | DiffieHellman |X |X | | -KeyPairGenerator | EC |X |X |[ECKeyPairGenerator incorrect keysize](#eckeypairgenerator-incorrect-keysize)| -KeyPairGenerator | Ed25519 | |X | | -KeyPairGenerator | Ed448 | |X | | -KeyPairGenerator | EdDSA | |X | | -KeyPairGenerator | ML-DSA-44 | |X | | -KeyPairGenerator | ML-DSA-65 | |X | | -KeyPairGenerator | ML-DSA-87 | |X | | -KeyPairGenerator | ML-KEM-512 | |X |[ML-KEM](#ml-kem)| -KeyPairGenerator | ML-KEM-768 | |X |[ML-KEM](#ml-kem)| -KeyPairGenerator | ML-KEM-1024 | |X |[ML-KEM](#ml-kem)| -KeyPairGenerator | RSA |X |X | | -KeyPairGenerator | RSAPSS |X |X | | -KeyPairGenerator | X25519 | |X | | -KeyPairGenerator | X448 | |X | | -KeyPairGenerator | XDH | |X | | -KeyWrap | AES/KW/NoPadding |X |X |[AESKW](#AESKW)| -KeyWrap | AES/KWP/NoPadding |X |X |[AESKW](#AESKW)| -Mac | HmacMD5 | |X | | -Mac | HmacSHA1 | |X | | -Mac | HmacSHA224 |X |X | | -Mac | HmacSHA256 |X |X | | -Mac | HmacSHA3-224 |X |X | | -Mac | HmacSHA3-256 |X |X | | -Mac | HmacSHA3-384 |X |X | | -Mac | HmacSHA3-512 |X |X | | -Mac | HmacSHA384 |X |X | | -Mac | HmacSHA512 |X |X | | -MessageDigest | MD5 |X |X | | -MessageDigest | SHA-1 |X |X | | -MessageDigest | SHA-224 |X |X | | -MessageDigest | SHA-256 |X |X | | -MessageDigest | SHA-384 |X |X | | -MessageDigest | SHA-512 |X |X | | -MessageDigest | SHA-512/224 |X |X | | -MessageDigest | SHA-512/256 |X |X | | -MessageDigest | SHA3-224 |X |X | | -MessageDigest | SHA3-256 |X |X | | -MessageDigest | SHA3-384 |X |X | | -MessageDigest | SHA3-512 |X |X | | -SecretKeyFactory | AES |X |X | | -SecretKeyFactory | ChaCha20 | |X | | -SecretKeyFactory | DESede | |X | | -SecretKeyFactory | PBKDF2WithHmacSHA1 | |X | | -SecretKeyFactory | PBKDF2WithHmacSHA224 |X |X | | -SecretKeyFactory | PBKDF2WithHmacSHA256 |X |X | | -SecretKeyFactory | PBKDF2WithHmacSHA384 |X |X | | -SecretKeyFactory | PBKDF2WithHmacSHA512 |X |X | | -SecretKeyFactory | PBKDF2WithHmacSHA512/224 | |X | | -SecretKeyFactory | PBKDF2WithHmacSHA512/256 | |X | | -SecureRandom | SHA256DRBG |X |X | | -SecureRandom | SHA512DRBG |X |X | | -Signature | Ed25519 | |X | | -Signature | Ed448 | |X | | -Signature | EdDSA |X |X | | -Signature | ML-DSA-44 | |X |[ML-DSA](#ml-dsa)| -Signature | ML-DSA-65 | |X |[ML-DSA](#ml-dsa)| -Signature | ML-DSA-87 | |X |[ML-DSA](#ml-dsa)| -Signature | NONEwithDSA |X |X | | -Signature | NONEwithECDSA |X |X | | -Signature | NONEwithRSA |X |X | | -Signature | RSAPSS |X |X | | -Signature | RSAforSSL |X |X | | -Signature | SHA1withDSA | |X | | -Signature | SHA1withECDSA | |X | | -Signature | SHA1withRSA |X |X | | -Signature | SHA224withDSA |X |X | | -Signature | SHA224withECDSA |X |X | | -Signature | SHA224withRSA |X |X | | -Signature | SHA256withDSA |X |X | | -Signature | SHA256withECDSA |X |X | | -Signature | SHA256withRSA |X |X | | -Signature | SHA3-224withDSA | |X | | -Signature | SHA3-224withECDSA | |X | | -Signature | SHA3-224withRSA | |X | | -Signature | SHA3-256withDSA | |X | | -Signature | SHA3-256withECDSA | |X | | -Signature | SHA3-256withRSA | |X | | -Signature | SHA3-384withDSA | |X | | -Signature | SHA3-384withECDSA | |X | | -Signature | SHA3-384withRSA | |X | | -Signature | SHA3-512withDSA | |X | | -Signature | SHA3-512withECDSA | |X | | -Signature | SHA3-512withRSA | |X | | -Signature | SHA384withECDSA |X |X | | -Signature | SHA384withRSA |X |X | | -Signature | SHA512withECDSA |X |X | | -Signature | SHA512withRSA |X |X | | +| Algorithm Type | Algorithm Name | OpenJCEPlusFIPS | OpenJCEPlus | Notes | +| --------------------------|---------------------------------|-----------------|--------------|--------------| +AlgorithmParameterGenerator | CCM |X |X | | +AlgorithmParameterGenerator | DSA | |X | | +AlgorithmParameterGenerator | DiffieHellman |X |X | | +AlgorithmParameterGenerator | EC |X |X | | +AlgorithmParameterGenerator | GCM |X |X | | +AlgorithmParameters | AES |X |X | | +AlgorithmParameters | CCM |X |X | | +AlgorithmParameters | ChaCha20-Poly1305 | |X | | +AlgorithmParameters | DESede | |X | | +AlgorithmParameters | DSA |X |X | | +AlgorithmParameters | DiffieHellman |X |X | | +AlgorithmParameters | EC |X |X | | +AlgorithmParameters | GCM |X |X | | +AlgorithmParameters | OAEP |X |X | | +AlgorithmParameters | RSAPSS |X |X | | +AlgorithmParameters | PBEWithHmacSHA1AndAES_128 | |X | | +AlgorithmParameters | PBEWithHmacSHA1AndAES_256 | |X | | +AlgorithmParameters | PBEWithHmacSHA224AndAES_128 | |X | | +AlgorithmParameters | PBEWithHmacSHA224AndAES_256 | |X | | +AlgorithmParameters | PBEWithHmacSHA256AndAES_128 | |X | | +AlgorithmParameters | PBEWithHmacSHA256AndAES_256 | |X | | +AlgorithmParameters | PBEWithHmacSHA384AndAES_128 | |X | | +AlgorithmParameters | PBEWithHmacSHA384AndAES_256 | |X | | +AlgorithmParameters | PBEWithHmacSHA512AndAES_128 | |X | | +AlgorithmParameters | PBEWithHmacSHA512AndAES_256 | |X | | +AlgorithmParameters | PBEWithHmacSHA512/224AndAES_128 | |X | | +AlgorithmParameters | PBEWithHmacSHA512/224AndAES_256 | |X | | +AlgorithmParameters | PBEWithHmacSHA512/256AndAES_128 | |X | | +AlgorithmParameters | PBEWithHmacSHA512/256AndAES_256 | |X | | +Cipher | AES |X |X | | +Cipher | AES/CCM/NoPadding |X |X | | +Cipher | AES/GCM/NoPadding |X |X | | +Cipher | ChaCha20 | |X | | +Cipher | ChaCha20-Poly1305 | |X | | +Cipher | DESede | |X | | +Cipher | RSA |X |X | | +Cipher | PBEWithHmacSHA1AndAES_128 | |X | | +Cipher | PBEWithHmacSHA1AndAES_256 | |X | | +Cipher | PBEWithHmacSHA224AndAES_128 | |X | | +Cipher | PBEWithHmacSHA224AndAES_256 | |X | | +Cipher | PBEWithHmacSHA256AndAES_128 | |X | | +Cipher | PBEWithHmacSHA256AndAES_256 | |X | | +Cipher | PBEWithHmacSHA384AndAES_128 | |X | | +Cipher | PBEWithHmacSHA384AndAES_256 | |X | | +Cipher | PBEWithHmacSHA512AndAES_128 | |X | | +Cipher | PBEWithHmacSHA512AndAES_256 | |X | | +Cipher | PBEWithHmacSHA512/224AndAES_128 | |X | | +Cipher | PBEWithHmacSHA512/224AndAES_256 | |X | | +Cipher | PBEWithHmacSHA512/256AndAES_128 | |X | | +Cipher | PBEWithHmacSHA512/256AndAES_256 | |X | | +KeyAgreement | DiffieHellman |X |X | | +KeyAgreement | ECDH |X |X | | +KeyAgreement | X25519 | |X | | +KeyAgreement | X448 | |X | | +KeyAgreement | XDH | |X | | +KeyEncapsulationMechanism | ML-KEM-512 | |X |[ML-KEM](#ml-kem)| +KeyEncapsulationMechanism | ML-KEM-768 | |X |[ML-KEM](#ml-kem)| +KeyEncapsulationMechanism | ML-KEM-1024 | |X |[ML-KEM](#ml-kem)| +KeyFactory | DSA |X |X | | +KeyFactory | DiffieHellman |X |X | | +KeyFactory | EC |X |X | | +KeyFactory | Ed25519 | |X | | +KeyFactory | Ed448 | |X | | +KeyFactory | EdDSA | |X | | +KeyFactory | ML-DSA-44 | |X |[ML-DSA](#ml-dsa)| +KeyFactory | ML-DSA-65 | |X |[ML-DSA](#ml-dsa)| +KeyFactory | ML-DSA-87 | |X |[ML-DSA](#ml-dsa)| +KeyFactory | ML-KEM-512 | |X |[ML-KEM](#ml-kem)| +KeyFactory | ML-KEM-768 | |X |[ML-KEM](#ml-kem)| +KeyFactory | ML-KEM-1024 | |X |[ML-KEM](#ml-kem)| +KeyFactory | RSA |X |X | | +KeyFactory | RSAPSS |X |X | | +KeyFactory | X25519 | |X | | +KeyFactory | X448 | |X | | +KeyFactory | XDH | |X | | +KeyGenerator | AES |X |X | | +KeyGenerator | ChaCha20 | |X | | +KeyGenerator | DESede | |X | | +KeyGenerator | HmacMD5 | |X | | +KeyGenerator | HmacSHA1 | |X | | +KeyGenerator | HmacSHA224 |X |X | | +KeyGenerator | HmacSHA256 |X |X | | +KeyGenerator | HmacSHA3-224 |X |X | | +KeyGenerator | HmacSHA3-256 |X |X | | +KeyGenerator | HmacSHA3-384 |X |X | | +KeyGenerator | HmacSHA3-512 |X |X | | +KeyGenerator | HmacSHA384 |X |X | | +KeyGenerator | HmacSHA512 |X |X | | +KeyGenerator | SunTls12KeyMaterial |X |X | | +KeyGenerator | SunTls12MasterSecret |X |X | | +KeyGenerator | SunTls12Prf |X |X | | +KeyGenerator | SunTls12RsaPremasterSecret |X |X | | +KeyGenerator | SunTlsKeyMaterial |X |X | | +KeyGenerator | SunTlsMasterSecret |X |X | | +KeyGenerator | SunTlsPrf |X |X | | +KeyGenerator | SunTlsRsaPremasterSecret |X |X | | +KeyGenerator | kda-hkdf-with-sha1 | |X | | +KeyGenerator | kda-hkdf-with-sha224 |X |X | | +KeyGenerator | kda-hkdf-with-sha256 |X |X | | +KeyGenerator | kda-hkdf-with-sha384 |X |X | | +KeyGenerator | kda-hkdf-with-sha512 |X |X | | +KeyPairGenerator | DSA | |X | | +KeyPairGenerator | DiffieHellman |X |X | | +KeyPairGenerator | EC |X |X |[ECKeyPairGenerator incorrect keysize](#eckeypairgenerator-incorrect-keysize)| +KeyPairGenerator | Ed25519 | |X | | +KeyPairGenerator | Ed448 | |X | | +KeyPairGenerator | EdDSA | |X | | +KeyPairGenerator | ML-DSA-44 | |X | | +KeyPairGenerator | ML-DSA-65 | |X | | +KeyPairGenerator | ML-DSA-87 | |X | | +KeyPairGenerator | ML-KEM-512 | |X |[ML-KEM](#ml-kem)| +KeyPairGenerator | ML-KEM-768 | |X |[ML-KEM](#ml-kem)| +KeyPairGenerator | ML-KEM-1024 | |X |[ML-KEM](#ml-kem)| +KeyPairGenerator | RSA |X |X | | +KeyPairGenerator | RSAPSS |X |X | | +KeyPairGenerator | X25519 | |X | | +KeyPairGenerator | X448 | |X | | +KeyPairGenerator | XDH | |X | | +KeyWrap | AES/KW/NoPadding |X |X |[AESKW](#AESKW)| +KeyWrap | AES/KWP/NoPadding |X |X |[AESKW](#AESKW)| +Mac | HmacMD5 | |X | | +Mac | HmacSHA1 | |X | | +Mac | HmacSHA224 |X |X | | +Mac | HmacSHA256 |X |X | | +Mac | HmacSHA3-224 |X |X | | +Mac | HmacSHA3-256 |X |X | | +Mac | HmacSHA3-384 |X |X | | +Mac | HmacSHA3-512 |X |X | | +Mac | HmacSHA384 |X |X | | +Mac | HmacSHA512 |X |X | | +MessageDigest | MD5 |X |X | | +MessageDigest | SHA-1 |X |X | | +MessageDigest | SHA-224 |X |X | | +MessageDigest | SHA-256 |X |X | | +MessageDigest | SHA-384 |X |X | | +MessageDigest | SHA-512 |X |X | | +MessageDigest | SHA-512/224 |X |X | | +MessageDigest | SHA-512/256 |X |X | | +MessageDigest | SHA3-224 |X |X | | +MessageDigest | SHA3-256 |X |X | | +MessageDigest | SHA3-384 |X |X | | +MessageDigest | SHA3-512 |X |X | | +SecretKeyFactory | AES |X |X | | +SecretKeyFactory | ChaCha20 | |X | | +SecretKeyFactory | DESede | |X | | +SecretKeyFactory | PBKDF2WithHmacSHA1 | |X | | +SecretKeyFactory | PBKDF2WithHmacSHA224 |X |X | | +SecretKeyFactory | PBKDF2WithHmacSHA256 |X |X | | +SecretKeyFactory | PBKDF2WithHmacSHA384 |X |X | | +SecretKeyFactory | PBKDF2WithHmacSHA512 |X |X | | +SecretKeyFactory | PBKDF2WithHmacSHA512/224 | |X | | +SecretKeyFactory | PBKDF2WithHmacSHA512/256 | |X | | +SecretKeyFactory | PBEWithHmacSHA1AndAES_128 | |X | | +SecretKeyFactory | PBEWithHmacSHA1AndAES_256 | |X | | +SecretKeyFactory | PBEWithHmacSHA224AndAES_128 | |X | | +SecretKeyFactory | PBEWithHmacSHA224AndAES_256 | |X | | +SecretKeyFactory | PBEWithHmacSHA256AndAES_128 | |X | | +SecretKeyFactory | PBEWithHmacSHA256AndAES_256 | |X | | +SecretKeyFactory | PBEWithHmacSHA384AndAES_128 | |X | | +SecretKeyFactory | PBEWithHmacSHA384AndAES_256 | |X | | +SecretKeyFactory | PBEWithHmacSHA512AndAES_128 | |X | | +SecretKeyFactory | PBEWithHmacSHA512AndAES_256 | |X | | +SecretKeyFactory | PBEWithHmacSHA512/224AndAES_128 | |X | | +SecretKeyFactory | PBEWithHmacSHA512/224AndAES_256 | |X | | +SecretKeyFactory | PBEWithHmacSHA512/256AndAES_128 | |X | | +SecretKeyFactory | PBEWithHmacSHA512/256AndAES_256 | |X | | +SecureRandom | SHA256DRBG |X |X | | +SecureRandom | SHA512DRBG |X |X | | +Signature | Ed25519 | |X | | +Signature | Ed448 | |X | | +Signature | EdDSA |X |X | | +Signature | ML-DSA-44 | |X |[ML-DSA](#ml-dsa)| +Signature | ML-DSA-65 | |X |[ML-DSA](#ml-dsa)| +Signature | ML-DSA-87 | |X |[ML-DSA](#ml-dsa)| +Signature | NONEwithDSA |X |X | | +Signature | NONEwithECDSA |X |X | | +Signature | NONEwithRSA |X |X | | +Signature | RSAPSS |X |X | | +Signature | RSAforSSL |X |X | | +Signature | SHA1withDSA | |X | | +Signature | SHA1withECDSA | |X | | +Signature | SHA1withRSA |X |X | | +Signature | SHA224withDSA |X |X | | +Signature | SHA224withECDSA |X |X | | +Signature | SHA224withRSA |X |X | | +Signature | SHA256withDSA |X |X | | +Signature | SHA256withECDSA |X |X | | +Signature | SHA256withRSA |X |X | | +Signature | SHA3-224withDSA | |X | | +Signature | SHA3-224withECDSA | |X | | +Signature | SHA3-224withRSA | |X | | +Signature | SHA3-256withDSA | |X | | +Signature | SHA3-256withECDSA | |X | | +Signature | SHA3-256withRSA | |X | | +Signature | SHA3-384withDSA | |X | | +Signature | SHA3-384withECDSA | |X | | +Signature | SHA3-384withRSA | |X | | +Signature | SHA3-512withDSA | |X | | +Signature | SHA3-512withECDSA | |X | | +Signature | SHA3-512withRSA | |X | | +Signature | SHA384withECDSA |X |X | | +Signature | SHA384withRSA |X |X | | +Signature | SHA512withECDSA |X |X | | +Signature | SHA512withRSA |X |X | | ## Algorithm Notes diff --git a/src/main/java/com/ibm/crypto/plus/provider/OpenJCEPlus.java b/src/main/java/com/ibm/crypto/plus/provider/OpenJCEPlus.java index 150c569e6..780b374a7 100644 --- a/src/main/java/com/ibm/crypto/plus/provider/OpenJCEPlus.java +++ b/src/main/java/com/ibm/crypto/plus/provider/OpenJCEPlus.java @@ -31,8 +31,16 @@ public final class OpenJCEPlus extends OpenJCEPlusProvider { private static final String info = "OpenJCEPlus Provider implements the following:\n" + "Algorithm parameter : AES, ChaCha20, ChaCha20-Poly1305, DESede, DiffieHellman, DSA, EC, XEC, GCM, CCM, OAEP, RSAPSS\n" + + " PBEWithHmacSHA1AndAES_128, PBEWithHmacSHA1AndAES_256, PBEWithHmacSHA224AndAES_128, PBEWithHmacSHA224AndAES_256\n" + + " PBEWithHmacSHA256AndAES_128, PBEWithHmacSHA256AndAES_256, PBEWithHmacSHA384AndAES_128, PBEWithHmacSHA384AndAES_256\n" + + " PBEWithHmacSHA512AndAES_128, PBEWithHmacSHA512AndAES_256, PBEWithHmacSHA512/224AndAES_128, PBEWithHmacSHA512/224AndAES_256\n" + + " PBEWithHmacSHA512/256AndAES_128, PBEWithHmacSHA512/256AndAES_256\n" + "Algorithm parameter generator : DiffieHellman, DSA, EC, XEC, GCM, CCM\n" + "Cipher algorithms : AES, ChaCha20, ChaCha20-Poly1305, DESede, RSA\n" + + " PBEWithHmacSHA1AndAES_128, PBEWithHmacSHA1AndAES_256, PBEWithHmacSHA224AndAES_128, PBEWithHmacSHA224AndAES_256\n" + + " PBEWithHmacSHA256AndAES_128, PBEWithHmacSHA256AndAES_256, PBEWithHmacSHA384AndAES_128, PBEWithHmacSHA384AndAES_256\n" + + " PBEWithHmacSHA512AndAES_128, PBEWithHmacSHA512AndAES_256, PBEWithHmacSHA512/224AndAES_128, PBEWithHmacSHA512/224AndAES_256\n" + + " PBEWithHmacSHA512/256AndAES_128, PBEWithHmacSHA512/256AndAES_256\n" + "Key agreement algorithms : DiffieHellman, ECDH, XDH\n" + "Key Encapsulation Mechanisms : ML-KEM-512, ML-KEM-768, ML-KEM-1024\n" + "Key factory : DiffieHellman, DSA, EC, XEC, RSA, RSAPSS, ML-KEM-512, ML-KEM-768, ML-KEM-1024\n" @@ -49,6 +57,10 @@ public final class OpenJCEPlus extends OpenJCEPlusProvider { + "Message digest : MD5, SHA-1, SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224, SHA-512/256, SHA3-224, SHA3-256, SHA3-384, SHA3-512\n" + "Secret key factory : AES, ChaCha20, DESede, PBKDF2WithHmacSHA1, PBKDF2WithHmacSHA224, PBKDF2WithHmacSHA256, PBKDF2WithHmacSHA384, PBKDF2WithHmacSHA512\n" + " PBKDF2WithHmacSHA512/224, PBKDF2WithHmacSHA512/256\n" + + " PBEWithHmacSHA1AndAES_128, PBEWithHmacSHA1AndAES_256, PBEWithHmacSHA224AndAES_128, PBEWithHmacSHA224AndAES_256\n" + + " PBEWithHmacSHA256AndAES_128, PBEWithHmacSHA256AndAES_256, PBEWithHmacSHA384AndAES_128, PBEWithHmacSHA384AndAES_256\n" + + " PBEWithHmacSHA512AndAES_128, PBEWithHmacSHA512AndAES_256, PBEWithHmacSHA512/224AndAES_128, PBEWithHmacSHA512/224AndAES_256\n" + + " PBEWithHmacSHA512/256AndAES_128, PBEWithHmacSHA512/256AndAES_256\n" + "Secure random : HASHDRBG, SHA256DRBG, SHA512DRBG\n" + "Signature algorithms : NONEwithDSA, SHA1withDSA, SHA224withDSA, SHA256withDSA,\n" + " SHA3-224withDSA, SHA3-256withDSA, SHA3-384withDSA, SHA3-512withDSA,\n" @@ -147,7 +159,63 @@ private void registerAlgorithms(Provider jce) { aliases = null; putService(new OpenJCEPlusService(jce, "AlgorithmParameters", "OAEP", "com.ibm.crypto.plus.provider.OAEPParameters", aliases)); + + aliases = null; + putService(new OpenJCEPlusService(jce, "AlgorithmParameters", "PBEWithHmacSHA1AndAES_128", + "com.ibm.crypto.plus.provider.PBES2Parameters$HmacSHA1AndAES_128", aliases)); + + aliases = null; + putService(new OpenJCEPlusService(jce, "AlgorithmParameters", "PBEWithHmacSHA1AndAES_256", + "com.ibm.crypto.plus.provider.PBES2Parameters$HmacSHA1AndAES_256", aliases)); + + aliases = null; + putService(new OpenJCEPlusService(jce, "AlgorithmParameters", "PBEWithHmacSHA224AndAES_128", + "com.ibm.crypto.plus.provider.PBES2Parameters$HmacSHA224AndAES_128", aliases)); + + aliases = null; + putService(new OpenJCEPlusService(jce, "AlgorithmParameters", "PBEWithHmacSHA224AndAES_256", + "com.ibm.crypto.plus.provider.PBES2Parameters$HmacSHA224AndAES_256", aliases)); + + aliases = null; + putService(new OpenJCEPlusService(jce, "AlgorithmParameters", "PBEWithHmacSHA256AndAES_128", + "com.ibm.crypto.plus.provider.PBES2Parameters$HmacSHA256AndAES_128", aliases)); + + aliases = null; + putService(new OpenJCEPlusService(jce, "AlgorithmParameters", "PBEWithHmacSHA256AndAES_256", + "com.ibm.crypto.plus.provider.PBES2Parameters$HmacSHA256AndAES_256", aliases)); + + aliases = null; + putService(new OpenJCEPlusService(jce, "AlgorithmParameters", "PBEWithHmacSHA384AndAES_128", + "com.ibm.crypto.plus.provider.PBES2Parameters$HmacSHA384AndAES_128", aliases)); + + aliases = null; + putService(new OpenJCEPlusService(jce, "AlgorithmParameters", "PBEWithHmacSHA384AndAES_256", + "com.ibm.crypto.plus.provider.PBES2Parameters$HmacSHA384AndAES_256", aliases)); + + aliases = null; + putService(new OpenJCEPlusService(jce, "AlgorithmParameters", "PBEWithHmacSHA512AndAES_128", + "com.ibm.crypto.plus.provider.PBES2Parameters$HmacSHA512AndAES_128", aliases)); + + aliases = null; + putService(new OpenJCEPlusService(jce, "AlgorithmParameters", "PBEWithHmacSHA512AndAES_256", + "com.ibm.crypto.plus.provider.PBES2Parameters$HmacSHA512AndAES_256", aliases)); + + aliases = null; + putService(new OpenJCEPlusService(jce, "AlgorithmParameters", "PBEWithHmacSHA512/224AndAES_128", + "com.ibm.crypto.plus.provider.PBES2Parameters$HmacSHA512_224AndAES_128", aliases)); + + aliases = null; + putService(new OpenJCEPlusService(jce, "AlgorithmParameters", "PBEWithHmacSHA512/224AndAES_256", + "com.ibm.crypto.plus.provider.PBES2Parameters$HmacSHA512_224AndAES_256", aliases)); + aliases = null; + putService(new OpenJCEPlusService(jce, "AlgorithmParameters", "PBEWithHmacSHA512/256AndAES_128", + "com.ibm.crypto.plus.provider.PBES2Parameters$HmacSHA512_256AndAES_128", aliases)); + + aliases = null; + putService(new OpenJCEPlusService(jce, "AlgorithmParameters", "PBEWithHmacSHA512/256AndAES_256", + "com.ibm.crypto.plus.provider.PBES2Parameters$HmacSHA512_256AndAES_256", aliases)); + /*aliases = null; putService(new OpenJCEPlusService(jce, "AlgorithmParameters", @@ -262,6 +330,62 @@ private void registerAlgorithms(Provider jce) { putService(new OpenJCEPlusService(jce, "Cipher", "ChaCha20-Poly1305", "com.ibm.crypto.plus.provider.ChaCha20Poly1305Cipher", aliases)); + aliases = null; + putService(new OpenJCEPlusService(jce, "Cipher", "PBEWithHmacSHA1AndAES_128", + "com.ibm.crypto.plus.provider.PBES2Core$HmacSHA1AndAES_128", aliases)); + + aliases = null; + putService(new OpenJCEPlusService(jce, "Cipher", "PBEWithHmacSHA1AndAES_256", + "com.ibm.crypto.plus.provider.PBES2Core$HmacSHA1AndAES_256", aliases)); + + aliases = null; + putService(new OpenJCEPlusService(jce, "Cipher", "PBEWithHmacSHA224AndAES_128", + "com.ibm.crypto.plus.provider.PBES2Core$HmacSHA224AndAES_128", aliases)); + + aliases = null; + putService(new OpenJCEPlusService(jce, "Cipher", "PBEWithHmacSHA224AndAES_256", + "com.ibm.crypto.plus.provider.PBES2Core$HmacSHA224AndAES_256", aliases)); + + aliases = null; + putService(new OpenJCEPlusService(jce, "Cipher", "PBEWithHmacSHA256AndAES_128", + "com.ibm.crypto.plus.provider.PBES2Core$HmacSHA256AndAES_128", aliases)); + + aliases = null; + putService(new OpenJCEPlusService(jce, "Cipher", "PBEWithHmacSHA256AndAES_256", + "com.ibm.crypto.plus.provider.PBES2Core$HmacSHA256AndAES_256", aliases)); + + aliases = null; + putService(new OpenJCEPlusService(jce, "Cipher", "PBEWithHmacSHA384AndAES_128", + "com.ibm.crypto.plus.provider.PBES2Core$HmacSHA384AndAES_128", aliases)); + + aliases = null; + putService(new OpenJCEPlusService(jce, "Cipher", "PBEWithHmacSHA384AndAES_256", + "com.ibm.crypto.plus.provider.PBES2Core$HmacSHA384AndAES_256", aliases)); + + aliases = null; + putService(new OpenJCEPlusService(jce, "Cipher", "PBEWithHmacSHA512AndAES_128", + "com.ibm.crypto.plus.provider.PBES2Core$HmacSHA512AndAES_128", aliases)); + + aliases = null; + putService(new OpenJCEPlusService(jce, "Cipher", "PBEWithHmacSHA512AndAES_256", + "com.ibm.crypto.plus.provider.PBES2Core$HmacSHA512AndAES_256", aliases)); + + aliases = null; + putService(new OpenJCEPlusService(jce, "Cipher", "PBEWithHmacSHA512/224AndAES_128", + "com.ibm.crypto.plus.provider.PBES2Core$HmacSHA512_224AndAES_128", aliases)); + + aliases = null; + putService(new OpenJCEPlusService(jce, "Cipher", "PBEWithHmacSHA512/224AndAES_256", + "com.ibm.crypto.plus.provider.PBES2Core$HmacSHA512_224AndAES_256", aliases)); + + aliases = null; + putService(new OpenJCEPlusService(jce, "Cipher", "PBEWithHmacSHA512/256AndAES_128", + "com.ibm.crypto.plus.provider.PBES2Core$HmacSHA512_256AndAES_128", aliases)); + + aliases = null; + putService(new OpenJCEPlusService(jce, "Cipher", "PBEWithHmacSHA512/256AndAES_256", + "com.ibm.crypto.plus.provider.PBES2Core$HmacSHA512_256AndAES_256", aliases)); + /* ======================================================================= * Key agreement * ======================================================================= @@ -790,6 +914,62 @@ private void registerAlgorithms(Provider jce) { putService(new OpenJCEPlusService(jce, "SecretKeyFactory", "ChaCha20", "com.ibm.crypto.plus.provider.ChaCha20KeyFactory", aliases)); + aliases = null; + putService(new OpenJCEPlusService(jce, "SecretKeyFactory", "PBEWithHmacSHA1AndAES_128", + "com.ibm.crypto.plus.provider.PBEKeyFactory$PBEWithHmacSHA1AndAES_128", aliases)); + + aliases = null; + putService(new OpenJCEPlusService(jce, "SecretKeyFactory", "PBEWithHmacSHA1AndAES_256", + "com.ibm.crypto.plus.provider.PBEKeyFactory$PBEWithHmacSHA1AndAES_256", aliases)); + + aliases = null; + putService(new OpenJCEPlusService(jce, "SecretKeyFactory", "PBEWithHmacSHA224AndAES_128", + "com.ibm.crypto.plus.provider.PBEKeyFactory$PBEWithHmacSHA224AndAES_128", aliases)); + + aliases = null; + putService(new OpenJCEPlusService(jce, "SecretKeyFactory", "PBEWithHmacSHA224AndAES_256", + "com.ibm.crypto.plus.provider.PBEKeyFactory$PBEWithHmacSHA224AndAES_256", aliases)); + + aliases = null; + putService(new OpenJCEPlusService(jce, "SecretKeyFactory", "PBEWithHmacSHA256AndAES_128", + "com.ibm.crypto.plus.provider.PBEKeyFactory$PBEWithHmacSHA256AndAES_128", aliases)); + + aliases = null; + putService(new OpenJCEPlusService(jce, "SecretKeyFactory", "PBEWithHmacSHA256AndAES_256", + "com.ibm.crypto.plus.provider.PBEKeyFactory$PBEWithHmacSHA256AndAES_256", aliases)); + + aliases = null; + putService(new OpenJCEPlusService(jce, "SecretKeyFactory", "PBEWithHmacSHA384AndAES_128", + "com.ibm.crypto.plus.provider.PBEKeyFactory$PBEWithHmacSHA384AndAES_128", aliases)); + + aliases = null; + putService(new OpenJCEPlusService(jce, "SecretKeyFactory", "PBEWithHmacSHA384AndAES_256", + "com.ibm.crypto.plus.provider.PBEKeyFactory$PBEWithHmacSHA384AndAES_256", aliases)); + + aliases = null; + putService(new OpenJCEPlusService(jce, "SecretKeyFactory", "PBEWithHmacSHA512AndAES_128", + "com.ibm.crypto.plus.provider.PBEKeyFactory$PBEWithHmacSHA512AndAES_128", aliases)); + + aliases = null; + putService(new OpenJCEPlusService(jce, "SecretKeyFactory", "PBEWithHmacSHA512AndAES_256", + "com.ibm.crypto.plus.provider.PBEKeyFactory$PBEWithHmacSHA512AndAES_256", aliases)); + + aliases = null; + putService(new OpenJCEPlusService(jce, "SecretKeyFactory", "PBEWithHmacSHA512/224AndAES_128", + "com.ibm.crypto.plus.provider.PBEKeyFactory$PBEWithHmacSHA512_224AndAES_128", aliases)); + + aliases = null; + putService(new OpenJCEPlusService(jce, "SecretKeyFactory", "PBEWithHmacSHA512/224AndAES_256", + "com.ibm.crypto.plus.provider.PBEKeyFactory$PBEWithHmacSHA512_224AndAES_256", aliases)); + + aliases = null; + putService(new OpenJCEPlusService(jce, "SecretKeyFactory", "PBEWithHmacSHA512/256AndAES_128", + "com.ibm.crypto.plus.provider.PBEKeyFactory$PBEWithHmacSHA512_256AndAES_128", aliases)); + + aliases = null; + putService(new OpenJCEPlusService(jce, "SecretKeyFactory", "PBEWithHmacSHA512/256AndAES_256", + "com.ibm.crypto.plus.provider.PBEKeyFactory$PBEWithHmacSHA512_256AndAES_256", aliases)); + /* ======================================================================= * SecureRandom * ======================================================================= diff --git a/src/main/java/com/ibm/crypto/plus/provider/PBEKey.java b/src/main/java/com/ibm/crypto/plus/provider/PBEKey.java new file mode 100644 index 000000000..6c54c14e9 --- /dev/null +++ b/src/main/java/com/ibm/crypto/plus/provider/PBEKey.java @@ -0,0 +1,184 @@ +/* + * Copyright IBM Corp. 2025 + * + * This code is free software; you can redistribute it and/or modify it + * under the terms provided by IBM in the LICENSE file that accompanied + * this code, including the "Classpath" Exception described therein. + */ + +package com.ibm.crypto.plus.provider; + +import java.io.IOException; +import java.io.InvalidObjectException; +import java.lang.ref.Reference; +import java.security.MessageDigest; +import java.security.spec.InvalidKeySpecException; +import java.util.Arrays; +import java.util.Locale; +import javax.crypto.SecretKey; +import javax.crypto.spec.PBEKeySpec; +import sun.security.util.PBEUtil; + +final class PBEKey implements SecretKey { + + @java.io.Serial + private static final long serialVersionUID = 9223372036854775807L; + + private byte[] key; + + private final String type; + + private boolean destroyed = false; + + private OpenJCEPlusProvider provider = null; + + /** + * Creates a PBE key from a given PBE key specification. + * + * @param keytype the given PBE key specification + */ + PBEKey(OpenJCEPlusProvider provider, PBEKeySpec keySpec, String keytype) throws InvalidKeySpecException { + char[] passwd = keySpec.getPassword(); + + if (passwd == null || passwd.length == 0) { + // Should allow an empty password. + passwd = new char[0]; + } + + for (char c : passwd) { + if (Character.isISOControl(c)) + throw new InvalidKeySpecException("Invalid Password."); + } + + this.key = PBEUtil.encodePassword(passwd); + Arrays.fill(passwd, '\0'); + type = keytype; + this.provider = provider; + } + + public byte[] getEncoded() { + try { + return key.clone(); + } finally { + // prevent this from being cleaned for the above block + Reference.reachabilityFence(this); + } + } + + public String getAlgorithm() { + return type; + } + + public String getFormat() { + return "RAW"; + } + + @Override + public int hashCode() { + try { + return Arrays.hashCode(this.key) + ^ getAlgorithm().toLowerCase(Locale.ENGLISH).hashCode(); + } finally { + // prevent this from being cleaned for the above block + Reference.reachabilityFence(this); + } + } + + @Override + public boolean equals(Object obj) { + try { + if (obj == this) + return true; + + if (!(obj instanceof SecretKey that)) + return false; + + if (isDestroyed() || that.isDestroyed()) { + return false; + } + + if (!(that.getAlgorithm().equalsIgnoreCase(type))) + return false; + + byte[] thatEncoded = that.getEncoded(); + boolean ret = MessageDigest.isEqual(this.key, thatEncoded); + Arrays.fill(thatEncoded, (byte) 0x00); + return ret; + } finally { + // prevent this from being cleaned for the above block + Reference.reachabilityFence(this); + } + } + + @Override + public void destroy() { + if (this.key != null) { + Arrays.fill(this.key, (byte) 0x00); + destroyed = true; + } + } + + @Override + public boolean isDestroyed() { + return destroyed; + } + + /** + * Restores the state of this object from the stream. + * + * @param s the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(java.io.ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + if (key == null) { + throw new InvalidObjectException( + "PBEKey couldn't be deserialized"); + } + byte[] temp = key; + key = temp.clone(); + Arrays.fill(temp, (byte) 0x00); + + // Accept "\0" to signify "zero-length password with no terminator". + if (!(key.length == 1 && key[0] == 0)) { + for (int i = 0; i < key.length; i++) { + if ((key[i] < '\u0020') || (key[i] > '\u007E')) { + throw new InvalidObjectException( + "PBEKey had non-ASCII chars"); + } + } + } + } + + + /** + * Replace the PBE key to be serialized. + * + * @return the standard KeyRep object to be serialized + * + * @throws java.io.ObjectStreamException if a new object representing + * this PBE key could not be created + */ + @java.io.Serial + private Object writeReplace() throws java.io.ObjectStreamException { + try { + return new JCEPlusKeyRep(JCEPlusKeyRep.Type.SECRET, + getAlgorithm(), getFormat(), getEncoded(), provider.getName()); + } finally { + // prevent this from being cleaned for the above block + Reference.reachabilityFence(this); + } + } + + protected void finalize() throws Throwable { + try { + destroy(); + } finally { + super.finalize(); + } + } +} diff --git a/src/main/java/com/ibm/crypto/plus/provider/PBEKeyFactory.java b/src/main/java/com/ibm/crypto/plus/provider/PBEKeyFactory.java new file mode 100644 index 000000000..6f05fe720 --- /dev/null +++ b/src/main/java/com/ibm/crypto/plus/provider/PBEKeyFactory.java @@ -0,0 +1,224 @@ +/* + * Copyright IBM Corp. 2025 + * + * This code is free software; you can redistribute it and/or modify it + * under the terms provided by IBM in the LICENSE file that accompanied + * this code, including the "Classpath" Exception described therein. + */ + +package com.ibm.crypto.plus.provider; + +import java.security.InvalidKeyException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactorySpi; +import javax.crypto.spec.PBEKeySpec; + +abstract class PBEKeyFactory extends SecretKeyFactorySpi { + + private final String type; + private OpenJCEPlusProvider provider = null; + + private PBEKeyFactory(String keytype, OpenJCEPlusProvider provider) { + type = keytype; + this.provider = provider; + } + + public static final class PBEWithHmacSHA1AndAES_128 extends PBEKeyFactory { + public PBEWithHmacSHA1AndAES_128(OpenJCEPlusProvider provider) { + super("PBEWithHmacSHA1AndAES_128", provider); + } + } + + public static final class PBEWithHmacSHA224AndAES_128 + extends PBEKeyFactory { + public PBEWithHmacSHA224AndAES_128(OpenJCEPlusProvider provider) { + super("PBEWithHmacSHA224AndAES_128", provider); + } + } + + public static final class PBEWithHmacSHA256AndAES_128 + extends PBEKeyFactory { + public PBEWithHmacSHA256AndAES_128(OpenJCEPlusProvider provider) { + super("PBEWithHmacSHA256AndAES_128", provider); + } + } + + public static final class PBEWithHmacSHA384AndAES_128 + extends PBEKeyFactory { + public PBEWithHmacSHA384AndAES_128(OpenJCEPlusProvider provider) { + super("PBEWithHmacSHA384AndAES_128", provider); + } + } + + public static final class PBEWithHmacSHA512AndAES_128 + extends PBEKeyFactory { + public PBEWithHmacSHA512AndAES_128(OpenJCEPlusProvider provider) { + super("PBEWithHmacSHA512AndAES_128", provider); + } + } + + public static final class PBEWithHmacSHA512_224AndAES_128 + extends PBEKeyFactory { + public PBEWithHmacSHA512_224AndAES_128(OpenJCEPlusProvider provider) { + super("PBEWithHmacSHA512/224AndAES_128", provider); + } + } + + public static final class PBEWithHmacSHA512_256AndAES_128 + extends PBEKeyFactory { + public PBEWithHmacSHA512_256AndAES_128(OpenJCEPlusProvider provider) { + super("PBEWithHmacSHA512/256AndAES_128", provider); + } + } + + public static final class PBEWithHmacSHA1AndAES_256 extends PBEKeyFactory { + public PBEWithHmacSHA1AndAES_256(OpenJCEPlusProvider provider) { + super("PBEWithHmacSHA1AndAES_256", provider); + } + } + + public static final class PBEWithHmacSHA224AndAES_256 + extends PBEKeyFactory { + public PBEWithHmacSHA224AndAES_256(OpenJCEPlusProvider provider) { + super("PBEWithHmacSHA224AndAES_256", provider); + } + } + + public static final class PBEWithHmacSHA256AndAES_256 + extends PBEKeyFactory { + public PBEWithHmacSHA256AndAES_256(OpenJCEPlusProvider provider) { + super("PBEWithHmacSHA256AndAES_256", provider); + } + } + + public static final class PBEWithHmacSHA384AndAES_256 + extends PBEKeyFactory { + public PBEWithHmacSHA384AndAES_256(OpenJCEPlusProvider provider) { + super("PBEWithHmacSHA384AndAES_256", provider); + } + } + + public static final class PBEWithHmacSHA512AndAES_256 + extends PBEKeyFactory { + public PBEWithHmacSHA512AndAES_256(OpenJCEPlusProvider provider) { + super("PBEWithHmacSHA512AndAES_256", provider); + } + } + + public static final class PBEWithHmacSHA512_224AndAES_256 + extends PBEKeyFactory { + public PBEWithHmacSHA512_224AndAES_256(OpenJCEPlusProvider provider) { + super("PBEWithHmacSHA512/224AndAES_256", provider); + } + } + + public static final class PBEWithHmacSHA512_256AndAES_256 + extends PBEKeyFactory { + public PBEWithHmacSHA512_256AndAES_256(OpenJCEPlusProvider provider) { + super("PBEWithHmacSHA512/256AndAES_256", provider); + } + } + + /** + * Generates a SecretKey object from the provided key + * specification (key material). + * + * @param keySpec the specification (key material) of the secret key + * + * @return the secret key + * + * @exception InvalidKeySpecException if the given key specification + * is inappropriate for this key factory to produce a public key. + */ + protected SecretKey engineGenerateSecret(KeySpec keySpec) + throws InvalidKeySpecException + { + if (!(keySpec instanceof PBEKeySpec)) { + throw new InvalidKeySpecException("Invalid key spec"); + } + return new PBEKey(provider, (PBEKeySpec) keySpec, type); + } + + /** + * Returns a specification (key material) of the given key + * in the requested format. + * + * @param key the key + * + * @param keySpecCl the requested format in which the key material shall be + * returned + * + * @return the underlying key specification (key material) in the + * requested format + * + * @exception InvalidKeySpecException if the requested key specification is + * inappropriate for the given key, or the given key cannot be processed + * (e.g., the given key has an unrecognized algorithm or format). + */ + protected KeySpec engineGetKeySpec(SecretKey key, Class keySpecCl) + throws InvalidKeySpecException { + if ((key != null) && (key.getFormat() != null) && (key.getFormat().equalsIgnoreCase("RAW")) && (key.getEncoded() != null)) { + // Check if requested key spec is amongst the valid ones + if ((keySpecCl != null) && keySpecCl.isAssignableFrom(PBEKeySpec.class)) { + byte[] passwdBytes = key.getEncoded(); + char[] passwdChars = new char[passwdBytes.length]; + for (int i=0; i < passwdChars.length; i++) + passwdChars[i] = (char) (passwdBytes[i] & 0x7f); + PBEKeySpec ret = new PBEKeySpec(passwdChars); + // password char[] was cloned in PBEKeySpec constructor, + // so we can zero it out here + java.util.Arrays.fill(passwdChars, '\0'); + java.util.Arrays.fill(passwdBytes, (byte) 0x00); + return ret; + } else { + throw new InvalidKeySpecException("Invalid key spec"); + } + } else { + throw new InvalidKeySpecException("Invalid key " + + "format/algorithm"); + } + } + + /** + * Translates a SecretKey object, whose provider may be + * unknown or potentially untrusted, into a corresponding + * SecretKey object of this key factory. + * + * @param key the key whose provider is unknown or untrusted + * + * @return the translated key + * + * @exception InvalidKeyException if the given key cannot be processed by + * this key factory. + */ + protected SecretKey engineTranslateKey(SecretKey key) + throws InvalidKeyException + { + try { + if ((key != null) && (key.getFormat() != null) && (key.getFormat().equalsIgnoreCase("RAW"))) { + + if (key instanceof com.ibm.crypto.plus.provider.PBEKey) { + return key; + } + + // Convert key to spec + PBEKeySpec pbeKeySpec = (PBEKeySpec) engineGetKeySpec + (key, PBEKeySpec.class); + + try { + return engineGenerateSecret(pbeKeySpec); + } finally { + pbeKeySpec.clearPassword(); + } + } else { + throw new InvalidKeyException("Invalid key format/algorithm"); + } + + } catch (InvalidKeySpecException ikse) { + throw new InvalidKeyException("Cannot translate key: " + + ikse.getMessage()); + } + } +} diff --git a/src/main/java/com/ibm/crypto/plus/provider/PBES2Core.java b/src/main/java/com/ibm/crypto/plus/provider/PBES2Core.java new file mode 100644 index 000000000..88dafdde3 --- /dev/null +++ b/src/main/java/com/ibm/crypto/plus/provider/PBES2Core.java @@ -0,0 +1,311 @@ +/* + * Copyright IBM Corp. 2025 + * + * This code is free software; you can redistribute it and/or modify it + * under the terms provided by IBM in the LICENSE file that accompanied + * this code, including the "Classpath" Exception described therein. + */ + +package com.ibm.crypto.plus.provider; + +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidKeySpecException; +import java.util.Arrays; +import javax.crypto.BadPaddingException; +import javax.crypto.CipherSpi; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.ShortBufferException; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.SecretKeySpec; +import sun.security.util.PBEUtil; + +abstract class PBES2Core extends CipherSpi { + private final AESCipher cipher; + private final int keyLength; // in bits + private final int blkSize; // in bits + private final PBKDF2Core kdf; + private final String pbeAlgo; + private final String cipherAlgo; + private final PBEUtil.PBES2Params pbes2Params = new PBEUtil.PBES2Params(); + private OpenJCEPlusProvider provider = null; + + /** + * Creates an instance of PBE Scheme 2 according to the selected + * password-based key derivation function and encryption scheme. + */ + PBES2Core(String kdfAlgo, String cipherAlgo, int keySize, OpenJCEPlusProvider provider) + throws NoSuchAlgorithmException, NoSuchPaddingException { + + blkSize = AESCipher.AES_BLOCK_SIZE; + this.cipherAlgo = cipherAlgo; + keyLength = keySize * 8; + pbeAlgo = "PBEWith" + kdfAlgo + "And" + cipherAlgo + "_" + keyLength; + this.provider = provider; + + if (cipherAlgo.equalsIgnoreCase("AES")) { + cipher = new AESCipher(provider); + + switch(kdfAlgo.toLowerCase()) { + case "hmacsha1": + kdf = new PBKDF2Core.HmacSHA1(provider); + break; + case "hmacsha224": + kdf = new PBKDF2Core.HmacSHA224(provider); + break; + case "hmacsha256": + kdf = new PBKDF2Core.HmacSHA256(provider); + break; + case "hmacsha384": + kdf = new PBKDF2Core.HmacSHA384(provider); + break; + case "hmacsha512": + kdf = new PBKDF2Core.HmacSHA512(provider); + break; + case "hmacsha512/224": + kdf = new PBKDF2Core.HmacSHA512_224(provider); + break; + case "hmacsha512/256": + kdf = new PBKDF2Core.HmacSHA512_256(provider); + break; + default: + throw new NoSuchAlgorithmException( + "No Cipher implementation for " + kdfAlgo); + } + } else { + throw new NoSuchAlgorithmException("No Cipher implementation for " + + pbeAlgo); + } + cipher.engineSetMode("CBC"); + cipher.engineSetPadding("PKCS5Padding"); + } + + protected void engineSetMode(String mode) throws NoSuchAlgorithmException { + if ((mode != null) && (!mode.equalsIgnoreCase("CBC"))) { + throw new NoSuchAlgorithmException("Invalid cipher mode: " + mode); + } + } + + protected void engineSetPadding(String paddingScheme) + throws NoSuchPaddingException { + if ((paddingScheme != null) && + (!paddingScheme.equalsIgnoreCase("PKCS5Padding"))) { + throw new NoSuchPaddingException("Invalid padding scheme: " + + paddingScheme); + } + } + + protected int engineGetBlockSize() { + return blkSize; + } + + protected int engineGetOutputSize(int inputLen) { + return cipher.engineGetOutputSize(inputLen); + } + + protected byte[] engineGetIV() { + return cipher.engineGetIV(); + } + + protected AlgorithmParameters engineGetParameters() { + return pbes2Params.getAlgorithmParameters( + blkSize, pbeAlgo, provider, provider.getSecureRandom(null)); + } + + protected void engineInit(int opmode, Key key, SecureRandom random) + throws InvalidKeyException { + try { + engineInit(opmode, key, (AlgorithmParameterSpec) null, random); + } catch (InvalidAlgorithmParameterException ie) { + throw new InvalidKeyException("requires PBE parameters", ie); + } + } + + protected void engineInit(int opmode, Key key, + AlgorithmParameterSpec params, + SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException { + PBEKeySpec pbeSpec = pbes2Params.getPBEKeySpec(blkSize, keyLength, + opmode, key, params, random); + PBKDF2KeyImpl s = null; + byte[] derivedKey; + try { + s = (PBKDF2KeyImpl) kdf.engineGenerateSecret(pbeSpec); + derivedKey = s.getEncoded(); + } catch (InvalidKeySpecException ikse) { + throw new InvalidKeyException("Cannot construct PBE key", ikse); + } finally { + if (s != null) { + try { + s.finalize(); + } catch (Throwable e) { + //some error happened + } + } + pbeSpec.clearPassword(); + } + + SecretKeySpec cipherKey = null; + try { + cipherKey = new SecretKeySpec(derivedKey, cipherAlgo); + // initialize the underlying cipher + cipher.engineInit(opmode, cipherKey, pbes2Params.getIvSpec(), random); + } finally { + if (cipherKey != null) { + byte[] clean = cipherKey.getEncoded(); + Arrays.fill(clean, (byte) 0x00); + } + Arrays.fill(derivedKey, (byte) 0x00); + } + } + + protected void engineInit(int opmode, Key key, AlgorithmParameters params, + SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException { + engineInit(opmode, key, PBEUtil.PBES2Params.getParameterSpec(params), + random); + } + + protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) { + return cipher.engineUpdate(input, inputOffset, inputLen); + } + + protected int engineUpdate(byte[] input, int inputOffset, int inputLen, + byte[] output, int outputOffset) + throws ShortBufferException { + return cipher.engineUpdate(input, inputOffset, inputLen, + output, outputOffset); + } + + protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen) + throws IllegalBlockSizeException, BadPaddingException { + return cipher.engineDoFinal(input, inputOffset, inputLen); + } + + protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, + byte[] output, int outputOffset) + throws ShortBufferException, IllegalBlockSizeException, + BadPaddingException { + return cipher.engineDoFinal(input, inputOffset, inputLen, + output, outputOffset); + } + + protected int engineGetKeySize(Key key) throws InvalidKeyException { + return keyLength; + } + + protected byte[] engineWrap(Key key) + throws IllegalBlockSizeException, InvalidKeyException { + return cipher.engineWrap(key); + } + + protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, + int wrappedKeyType) + throws InvalidKeyException, NoSuchAlgorithmException { + return cipher.engineUnwrap(wrappedKey, wrappedKeyAlgorithm, + wrappedKeyType); + } + + public static final class HmacSHA1AndAES_128 extends PBES2Core { + public HmacSHA1AndAES_128(OpenJCEPlusProvider provider) + throws NoSuchAlgorithmException, NoSuchPaddingException { + super("HmacSHA1", "AES", 16, provider); + } + } + + public static final class HmacSHA224AndAES_128 extends PBES2Core { + public HmacSHA224AndAES_128(OpenJCEPlusProvider provider) + throws NoSuchAlgorithmException, NoSuchPaddingException { + super("HmacSHA224", "AES", 16, provider); + } + } + + public static final class HmacSHA256AndAES_128 extends PBES2Core { + public HmacSHA256AndAES_128(OpenJCEPlusProvider provider) + throws NoSuchAlgorithmException, NoSuchPaddingException { + super("HmacSHA256", "AES", 16, provider); + } + } + + public static final class HmacSHA384AndAES_128 extends PBES2Core { + public HmacSHA384AndAES_128(OpenJCEPlusProvider provider) + throws NoSuchAlgorithmException, NoSuchPaddingException { + super("HmacSHA384", "AES", 16, provider); + } + } + + public static final class HmacSHA512AndAES_128 extends PBES2Core { + public HmacSHA512AndAES_128(OpenJCEPlusProvider provider) + throws NoSuchAlgorithmException, NoSuchPaddingException { + super("HmacSHA512", "AES", 16, provider); + } + } + + public static final class HmacSHA512_224AndAES_128 extends PBES2Core { + public HmacSHA512_224AndAES_128(OpenJCEPlusProvider provider) + throws NoSuchAlgorithmException, NoSuchPaddingException { + super("HmacSHA512/224", "AES", 16, provider); + } + } + + public static final class HmacSHA512_256AndAES_128 extends PBES2Core { + public HmacSHA512_256AndAES_128(OpenJCEPlusProvider provider) + throws NoSuchAlgorithmException, NoSuchPaddingException { + super("HmacSHA512/256", "AES", 16, provider); + } + } + + public static final class HmacSHA1AndAES_256 extends PBES2Core { + public HmacSHA1AndAES_256(OpenJCEPlusProvider provider) + throws NoSuchAlgorithmException, NoSuchPaddingException { + super("HmacSHA1", "AES", 32, provider); + } + } + + public static final class HmacSHA224AndAES_256 extends PBES2Core { + public HmacSHA224AndAES_256(OpenJCEPlusProvider provider) + throws NoSuchAlgorithmException, NoSuchPaddingException { + super("HmacSHA224", "AES", 32, provider); + } + } + + public static final class HmacSHA256AndAES_256 extends PBES2Core { + public HmacSHA256AndAES_256(OpenJCEPlusProvider provider) + throws NoSuchAlgorithmException, NoSuchPaddingException { + super("HmacSHA256", "AES", 32, provider); + } + } + + public static final class HmacSHA384AndAES_256 extends PBES2Core { + public HmacSHA384AndAES_256(OpenJCEPlusProvider provider) + throws NoSuchAlgorithmException, NoSuchPaddingException { + super("HmacSHA384", "AES", 32, provider); + } + } + + public static final class HmacSHA512AndAES_256 extends PBES2Core { + public HmacSHA512AndAES_256(OpenJCEPlusProvider provider) + throws NoSuchAlgorithmException, NoSuchPaddingException { + super("HmacSHA512", "AES", 32, provider); + } + } + + public static final class HmacSHA512_224AndAES_256 extends PBES2Core { + public HmacSHA512_224AndAES_256(OpenJCEPlusProvider provider) + throws NoSuchAlgorithmException, NoSuchPaddingException { + super("HmacSHA512/224", "AES", 32, provider); + } + } + public static final class HmacSHA512_256AndAES_256 extends PBES2Core { + public HmacSHA512_256AndAES_256(OpenJCEPlusProvider provider) + throws NoSuchAlgorithmException, NoSuchPaddingException { + super("HmacSHA512/256", "AES", 32, provider); + } + } +} diff --git a/src/main/java/com/ibm/crypto/plus/provider/PBES2Parameters.java b/src/main/java/com/ibm/crypto/plus/provider/PBES2Parameters.java new file mode 100644 index 000000000..1dd6ebb3c --- /dev/null +++ b/src/main/java/com/ibm/crypto/plus/provider/PBES2Parameters.java @@ -0,0 +1,478 @@ +/* + * Copyright IBM Corp. 2025 + * + * This code is free software; you can redistribute it and/or modify it + * under the terms provided by IBM in the LICENSE file that accompanied + * this code, including the "Classpath" Exception described therein. + */ + +package com.ibm.crypto.plus.provider; + +import java.io.IOException; +import java.security.AlgorithmParametersSpi; +import java.security.NoSuchAlgorithmException; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidParameterSpecException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEParameterSpec; +import sun.security.util.DerOutputStream; +import sun.security.util.DerValue; +import sun.security.util.KnownOIDs; +import sun.security.util.ObjectIdentifier; + +/** + * This class implements the parameter set used with password-based + * encryption scheme 2 (PBES2), which is defined in PKCS#5 as follows: + * + *
+ * -- PBES2
+ *
+ * PBES2Algorithms ALGORITHM-IDENTIFIER ::=
+ *   { {PBES2-params IDENTIFIED BY id-PBES2}, ...}
+ *
+ * id-PBES2 OBJECT IDENTIFIER ::= {pkcs-5 13}
+ *
+ * PBES2-params ::= SEQUENCE {
+ *   keyDerivationFunc AlgorithmIdentifier {{PBES2-KDFs}},
+ *   encryptionScheme AlgorithmIdentifier {{PBES2-Encs}} }
+ *
+ * PBES2-KDFs ALGORITHM-IDENTIFIER ::=
+ *   { {PBKDF2-params IDENTIFIED BY id-PBKDF2}, ... }
+ *
+ * PBES2-Encs ALGORITHM-IDENTIFIER ::= { ... }
+ *
+ * -- PBKDF2
+ *
+ * PBKDF2Algorithms ALGORITHM-IDENTIFIER ::=
+ *   { {PBKDF2-params IDENTIFIED BY id-PBKDF2}, ...}
+ *
+ * id-PBKDF2 OBJECT IDENTIFIER ::= {pkcs-5 12}
+ *
+ * PBKDF2-params ::= SEQUENCE {
+ *     salt CHOICE {
+ *       specified OCTET STRING,
+ *       otherSource AlgorithmIdentifier {{PBKDF2-SaltSources}}
+ *     },
+ *     iterationCount INTEGER (1..MAX),
+ *     keyLength INTEGER (1..MAX) OPTIONAL,
+ *     prf AlgorithmIdentifier {{PBKDF2-PRFs}} DEFAULT algid-hmacWithSHA1
+ * }
+ *
+ * PBKDF2-SaltSources ALGORITHM-IDENTIFIER ::= { ... }
+ *
+ * PBKDF2-PRFs ALGORITHM-IDENTIFIER ::= {
+ *     {NULL IDENTIFIED BY id-hmacWithSHA1} |
+ *     {NULL IDENTIFIED BY id-hmacWithSHA224} |
+ *     {NULL IDENTIFIED BY id-hmacWithSHA256} |
+ *     {NULL IDENTIFIED BY id-hmacWithSHA384} |
+ *     {NULL IDENTIFIED BY id-hmacWithSHA512}, ... }
+ *
+ * algid-hmacWithSHA1 AlgorithmIdentifier {{PBKDF2-PRFs}} ::=
+ *     {algorithm id-hmacWithSHA1, parameters NULL : NULL}
+ *
+ * id-hmacWithSHA1 OBJECT IDENTIFIER ::= {digestAlgorithm 7}
+ *
+ * PBES2-Encs ALGORITHM-IDENTIFIER ::= { ... }
+ *
+ * 
+ */ +abstract class PBES2Parameters extends AlgorithmParametersSpi { + + private static final ObjectIdentifier pkcs5PBKDF2_OID = + ObjectIdentifier.of(KnownOIDs.PBKDF2WithHmacSHA1); + private static final ObjectIdentifier pkcs5PBES2_OID = + ObjectIdentifier.of(KnownOIDs.PBES2); + private static final ObjectIdentifier aes128CBC_OID = + ObjectIdentifier.of(KnownOIDs.AES_128$CBC$NoPadding); + private static final ObjectIdentifier aes192CBC_OID = + ObjectIdentifier.of(KnownOIDs.AES_192$CBC$NoPadding); + private static final ObjectIdentifier aes256CBC_OID = + ObjectIdentifier.of(KnownOIDs.AES_256$CBC$NoPadding); + + // the PBES2 algorithm name + private String pbes2AlgorithmName = null; + + // the salt + private byte[] salt = null; + + // the iteration count + private int iCount = 0; + + // the cipher parameter + private AlgorithmParameterSpec cipherParam = null; + + // the key derivation function (default is HmacSHA1) + private ObjectIdentifier kdfAlgo_OID = + ObjectIdentifier.of(KnownOIDs.HmacSHA1); + + // the encryption function + private ObjectIdentifier cipherAlgo_OID = null; + + // the cipher keysize (in bits) + private int keysize = -1; + + PBES2Parameters() { + // KDF, encryption & keysize values are set later, in engineInit(byte[]) + } + + PBES2Parameters(String pbes2AlgorithmName) throws NoSuchAlgorithmException { + int and; + String kdfAlgo; + String cipherAlgo; + + // Extract the KDF and encryption algorithm names + this.pbes2AlgorithmName = pbes2AlgorithmName; + if (pbes2AlgorithmName.startsWith("PBEWith") && + (and = pbes2AlgorithmName.indexOf("And", 7 + 1)) > 0) { + kdfAlgo = pbes2AlgorithmName.substring(7, and); + cipherAlgo = pbes2AlgorithmName.substring(and + 3); + + // Check for keysize + int underscore; + if ((underscore = cipherAlgo.indexOf('_')) > 0) { + int slash; + if ((slash = cipherAlgo.indexOf('/', underscore + 1)) > 0) { + keysize = + Integer.parseInt(cipherAlgo.substring(underscore + 1, + slash)); + } else { + keysize = + Integer.parseInt(cipherAlgo.substring(underscore + 1)); + } + cipherAlgo = cipherAlgo.substring(0, underscore); + } + } else { + throw new NoSuchAlgorithmException("No crypto implementation for " + + pbes2AlgorithmName); + } + + switch (kdfAlgo) { + case "HmacSHA1": + case "HmacSHA224": + case "HmacSHA256": + case "HmacSHA384": + case "HmacSHA512": + case "HmacSHA512/224": + case "HmacSHA512/256": + kdfAlgo_OID = ObjectIdentifier.of(KnownOIDs.findMatch(kdfAlgo)); + break; + default: + throw new NoSuchAlgorithmException( + "No crypto implementation for " + kdfAlgo); + } + + if (cipherAlgo.equals("AES")) { + switch (keysize) { + case 128: + cipherAlgo_OID = aes128CBC_OID; + break; + case 256: + cipherAlgo_OID = aes256CBC_OID; + break; + default: + throw new NoSuchAlgorithmException( + "No Cipher implementation for " + keysize + "-bit " + + cipherAlgo); + } + } else { + throw new NoSuchAlgorithmException("No Cipher implementation for " + + cipherAlgo); + } + } + + protected void engineInit(AlgorithmParameterSpec paramSpec) + throws InvalidParameterSpecException + { + if (!(paramSpec instanceof PBEParameterSpec)) { + throw new InvalidParameterSpecException ("Inappropriate parameter specification"); + } + this.salt = ((PBEParameterSpec) paramSpec).getSalt().clone(); + this.iCount = ((PBEParameterSpec) paramSpec).getIterationCount(); + this.cipherParam = ((PBEParameterSpec) paramSpec).getParameterSpec(); + } + + protected void engineInit(byte[] encoded) + throws IOException + { + DerValue pBES2_params = new DerValue(encoded); + if (pBES2_params.tag != DerValue.tag_Sequence) { + throw new IOException("PBE parameter parsing error: " + + "not an ASN.1 SEQUENCE tag"); + } + DerValue kdf = pBES2_params.data.getDerValue(); + + // Before JDK-8202837, PBES2-params was mistakenly encoded like + // an AlgorithmId which is a sequence of its own OID and the real + // PBES2-params. If the first DerValue is an OID instead of a + // PBES2-KDFs (which should be a SEQUENCE), we are likely to be + // dealing with this buggy encoding. Skip the OID and treat the + // next DerValue as the real PBES2-params. + if (kdf.getTag() == DerValue.tag_ObjectId) { + pBES2_params = pBES2_params.data.getDerValue(); + kdf = pBES2_params.data.getDerValue(); + } + + String kdfAlgo = parseKDF(kdf); + + if (pBES2_params.tag != DerValue.tag_Sequence) { + throw new IOException("PBE parameter parsing error: " + + "not an ASN.1 SEQUENCE tag"); + } + String cipherAlgo = parseES(pBES2_params.data.getDerValue()); + + this.pbes2AlgorithmName = "PBEWith" + kdfAlgo + "And" + cipherAlgo; + } + + private String parseKDF(DerValue keyDerivationFunc) throws IOException { + + if (keyDerivationFunc.tag != DerValue.tag_Sequence) { + throw new IOException("PBE parameter parsing error: " + + "not an ASN.1 SEQUENCE tag"); + } + if (!pkcs5PBKDF2_OID.equals(keyDerivationFunc.data.getOID())) { + throw new IOException("PBE parameter parsing error: " + + "expecting the object identifier for PBKDF2"); + } + DerValue pBKDF2_params = keyDerivationFunc.data.getDerValue(); + if (pBKDF2_params.tag != DerValue.tag_Sequence) { + throw new IOException("PBE parameter parsing error: " + + "not an ASN.1 SEQUENCE tag"); + } + DerValue specified = pBKDF2_params.data.getDerValue(); + // the 'specified' ASN.1 CHOICE for 'salt' is supported + if (specified.tag == DerValue.tag_OctetString) { + salt = specified.getOctetString(); + } else { + // the 'otherSource' ASN.1 CHOICE for 'salt' is not supported + throw new IOException("PBE parameter parsing error: " + + "not an ASN.1 OCTET STRING tag"); + } + iCount = pBKDF2_params.data.getInteger(); + + // keyLength INTEGER (1..MAX) OPTIONAL, + var ksDer = pBKDF2_params.data.getOptional(DerValue.tag_Integer); + if (ksDer.isPresent()) { + keysize = ksDer.get().getInteger() * 8; // keysize (in bits) + } + + // prf AlgorithmIdentifier {{PBKDF2-PRFs}} DEFAULT algid-hmacWithSHA1 + String kdfAlgo; + var prfDer = pBKDF2_params.data.getOptional(DerValue.tag_Sequence); + if (prfDer.isPresent()) { + DerValue prf = prfDer.get(); + kdfAlgo_OID = prf.data.getOID(); + KnownOIDs o = KnownOIDs.findMatch(kdfAlgo_OID.toString()); + if (o == null || (!o.stdName().equals("HmacSHA1") && + !o.stdName().equals("HmacSHA224") && + !o.stdName().equals("HmacSHA256") && + !o.stdName().equals("HmacSHA384") && + !o.stdName().equals("HmacSHA512") && + !o.stdName().equals("HmacSHA512/224") && + !o.stdName().equals("HmacSHA512/256"))) { + throw new IOException("PBE parameter parsing error: " + + "expecting the object identifier for a HmacSHA key " + + "derivation function"); + } + kdfAlgo = o.stdName(); + prf.data.getOptional(DerValue.tag_Null); + prf.data.atEnd(); + } else { + kdfAlgo = "HmacSHA1"; + } + return kdfAlgo; + } + + private String parseES(DerValue encryptionScheme) throws IOException { + String cipherAlgo; + + cipherAlgo_OID = encryptionScheme.data.getOID(); + if (aes128CBC_OID.equals(cipherAlgo_OID)) { + cipherAlgo = "AES_128"; + // parameter is AES-IV 'OCTET STRING (SIZE(16))' + cipherParam = + new IvParameterSpec(encryptionScheme.data.getOctetString()); + keysize = 128; + } else if (aes256CBC_OID.equals(cipherAlgo_OID)) { + cipherAlgo = "AES_256"; + // parameter is AES-IV 'OCTET STRING (SIZE(16))' + cipherParam = + new IvParameterSpec(encryptionScheme.data.getOctetString()); + keysize = 256; + } else { + throw new IOException("PBE parameter parsing error: " + + "expecting the object identifier for AES cipher"); + } + + return cipherAlgo; + } + + protected void engineInit(byte[] encoded, String decodingMethod) + throws IOException + { + engineInit(encoded); + } + + protected + T engineGetParameterSpec(Class paramSpec) + throws InvalidParameterSpecException + { + if (paramSpec.isAssignableFrom(PBEParameterSpec.class)) { + return paramSpec.cast( + new PBEParameterSpec(this.salt, this.iCount, this.cipherParam)); + } else { + throw new InvalidParameterSpecException ("Inappropriate parameter specification"); + } + } + + protected byte[] engineGetEncoded() throws IOException { + DerOutputStream out = new DerOutputStream(); + + DerOutputStream pBES2_params = new DerOutputStream(); + + DerOutputStream keyDerivationFunc = new DerOutputStream(); + keyDerivationFunc.putOID(pkcs5PBKDF2_OID); + + DerOutputStream pBKDF2_params = new DerOutputStream(); + pBKDF2_params.putOctetString(salt); // choice: 'specified OCTET STRING' + pBKDF2_params.putInteger(iCount); + + if (keysize > 0) { + pBKDF2_params.putInteger(keysize / 8); // derived key length (in octets) + } + + DerOutputStream prf = new DerOutputStream(); + // algorithm is id-hmacWith + prf.putOID(kdfAlgo_OID); + // parameters is 'NULL' + prf.putNull(); + pBKDF2_params.write(DerValue.tag_Sequence, prf); + + keyDerivationFunc.write(DerValue.tag_Sequence, pBKDF2_params); + pBES2_params.write(DerValue.tag_Sequence, keyDerivationFunc); + + DerOutputStream encryptionScheme = new DerOutputStream(); + // algorithm is id-aes128-CBC or id-aes256-CBC + encryptionScheme.putOID(cipherAlgo_OID); + // parameters is 'AES-IV ::= OCTET STRING (SIZE(16))' + if (cipherParam != null && cipherParam instanceof IvParameterSpec) { + encryptionScheme.putOctetString( + ((IvParameterSpec) cipherParam).getIV()); + } else { + throw new IOException("Wrong parameter type: IV expected"); + } + pBES2_params.write(DerValue.tag_Sequence, encryptionScheme); + + out.write(DerValue.tag_Sequence, pBES2_params); + + return out.toByteArray(); + } + + protected byte[] engineGetEncoded(String encodingMethod) + throws IOException + { + return engineGetEncoded(); + } + + /* + * Returns a formatted string describing the parameters. + * + * The algorithm name pattern is: "PBEWithAnd" + * where is one of: HmacSHA1, HmacSHA224, HmacSHA256, HmacSHA384, + * HmacSHA512, HmacSHA512/224, or HmacSHA512/256 and is + * AES with a keysize suffix. + */ + protected String engineToString() { + return pbes2AlgorithmName; + } + + public static final class General extends PBES2Parameters { + public General() throws NoSuchAlgorithmException { + super(); + } + } + + public static final class HmacSHA1AndAES_128 extends PBES2Parameters { + public HmacSHA1AndAES_128() throws NoSuchAlgorithmException { + super("PBEWithHmacSHA1AndAES_128"); + } + } + + public static final class HmacSHA224AndAES_128 extends PBES2Parameters { + public HmacSHA224AndAES_128() throws NoSuchAlgorithmException { + super("PBEWithHmacSHA224AndAES_128"); + } + } + + public static final class HmacSHA256AndAES_128 extends PBES2Parameters { + public HmacSHA256AndAES_128() throws NoSuchAlgorithmException { + super("PBEWithHmacSHA256AndAES_128"); + } + } + + public static final class HmacSHA384AndAES_128 extends PBES2Parameters { + public HmacSHA384AndAES_128() throws NoSuchAlgorithmException { + super("PBEWithHmacSHA384AndAES_128"); + } + } + + public static final class HmacSHA512AndAES_128 extends PBES2Parameters { + public HmacSHA512AndAES_128() throws NoSuchAlgorithmException { + super("PBEWithHmacSHA512AndAES_128"); + } + } + + public static final class HmacSHA512_224AndAES_128 extends PBES2Parameters { + public HmacSHA512_224AndAES_128() throws NoSuchAlgorithmException { + super("PBEWithHmacSHA512/224AndAES_128"); + } + } + + public static final class HmacSHA512_256AndAES_128 extends PBES2Parameters { + public HmacSHA512_256AndAES_128() throws NoSuchAlgorithmException { + super("PBEWithHmacSHA512/256AndAES_128"); + } + } + + public static final class HmacSHA1AndAES_256 extends PBES2Parameters { + public HmacSHA1AndAES_256() throws NoSuchAlgorithmException { + super("PBEWithHmacSHA1AndAES_256"); + } + } + + public static final class HmacSHA224AndAES_256 extends PBES2Parameters { + public HmacSHA224AndAES_256() throws NoSuchAlgorithmException { + super("PBEWithHmacSHA224AndAES_256"); + } + } + + public static final class HmacSHA256AndAES_256 extends PBES2Parameters { + public HmacSHA256AndAES_256() throws NoSuchAlgorithmException { + super("PBEWithHmacSHA256AndAES_256"); + } + } + + public static final class HmacSHA384AndAES_256 extends PBES2Parameters { + public HmacSHA384AndAES_256() throws NoSuchAlgorithmException { + super("PBEWithHmacSHA384AndAES_256"); + } + } + + public static final class HmacSHA512AndAES_256 extends PBES2Parameters { + public HmacSHA512AndAES_256() throws NoSuchAlgorithmException { + super("PBEWithHmacSHA512AndAES_256"); + } + } + + public static final class HmacSHA512_224AndAES_256 extends PBES2Parameters { + public HmacSHA512_224AndAES_256() throws NoSuchAlgorithmException { + super("PBEWithHmacSHA512/224AndAES_256"); + } + } + + public static final class HmacSHA512_256AndAES_256 extends PBES2Parameters { + public HmacSHA512_256AndAES_256() throws NoSuchAlgorithmException { + super("PBEWithHmacSHA512/256AndAES_256"); + } + } +} + diff --git a/src/test/java/ibm/jceplus/jmh/PBEBenchmark.java b/src/test/java/ibm/jceplus/jmh/PBEBenchmark.java new file mode 100644 index 000000000..430b16cce --- /dev/null +++ b/src/test/java/ibm/jceplus/jmh/PBEBenchmark.java @@ -0,0 +1,104 @@ +/* + * Copyright IBM Corp. 2025 + * + * This code is free software; you can redistribute it and/or modify it + * under the terms provided by IBM in the LICENSE file that accompanied + * this code, including the "Classpath" Exception described therein. + */ + +package ibm.jceplus.jmh; + +import java.security.AlgorithmParameters; +import java.util.Locale; +import java.util.concurrent.TimeUnit; +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.PBEParameterSpec; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; + +@BenchmarkMode(Mode.Throughput) +@OutputTimeUnit(TimeUnit.SECONDS) +@State(Scope.Benchmark) +@Warmup(iterations = 3, time = 10, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 4, time = 30, timeUnit = TimeUnit.SECONDS) +public class PBEBenchmark extends JMHBase { + + private Cipher pbeEncrypt; + private Cipher pbeDecrypt; + + private byte[] salt = new byte[16]; + private byte[] ivBytes = { + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, + }; + private byte[] text = "Bob the builder by IBM".getBytes(); + private byte[] cipherText; + + @Param({"PBEWithHmacSHA1AndAES_128", "PBEWithHmacSHA256AndAES_256", "PBEWithHmacSHA384AndAES_256", + "PBEWithHmacSHA512AndAES_256", "PBEWithHmacSHA512/256AndAES_256"}) + private String algorithm; + + @Param({"OpenJCEPlus", "SunJCE"}) + private String provider; + + @Param({"1000", "300000"}) + private int iterationCount; + + @Setup + public void setup() throws Exception { + insertProvider(provider); + + pbeEncrypt = Cipher.getInstance(algorithm, provider); + SecretKey pbeKey = getKey(algorithm); + pbeEncrypt.init(Cipher.ENCRYPT_MODE, pbeKey, new PBEParameterSpec(salt, iterationCount, + new IvParameterSpec(ivBytes))); + cipherText = pbeEncrypt.doFinal(text); + + AlgorithmParameters aps = pbeEncrypt.getParameters(); + + pbeDecrypt = Cipher.getInstance(algorithm, provider); + pbeDecrypt.init(Cipher.DECRYPT_MODE, pbeKey, aps); + } + + @Benchmark + public byte[] encrypt() throws Exception { + return pbeEncrypt.doFinal(text); + } + + @Benchmark + public byte[] decrypt() throws Exception { + return pbeDecrypt.doFinal(cipherText); + } + + private SecretKey getKey(String algo) throws Exception { + PBEKeySpec pbeKeySpec = new PBEKeySpec("mypassword".toCharArray()); + int modeIdx = algo.toUpperCase(Locale.ENGLISH).indexOf("/CBC"); + String keyAlgo = (modeIdx == -1 ? algo : algo.substring(0, modeIdx)); + SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(keyAlgo, provider); + SecretKey pbeKey = keyFactory.generateSecret(pbeKeySpec); + + return pbeKey; + } + + public static void main(String[] args) throws RunnerException { + String testSimpleName = PBEBenchmark.class.getSimpleName(); + Options opt = optionsBuild(testSimpleName, testSimpleName); + + new Runner(opt).run(); + } +} diff --git a/src/test/java/ibm/jceplus/junit/base/BaseTestKeySerialization.java b/src/test/java/ibm/jceplus/junit/base/BaseTestKeySerialization.java index ff1f2023d..d4bf333f5 100644 --- a/src/test/java/ibm/jceplus/junit/base/BaseTestKeySerialization.java +++ b/src/test/java/ibm/jceplus/junit/base/BaseTestKeySerialization.java @@ -111,8 +111,15 @@ public void SerializationSecretKeyTest (String algorithm, int size, String ciphe @ParameterizedTest @CsvSource({"PBKDF2WithHmacSHA224", "PBKDF2WithHmacSHA256", - "PBKDF2WithHmacSHA384", "PBKDF2WithHmacSHA512"}) + "PBKDF2WithHmacSHA384", "PBKDF2WithHmacSHA512", + "PBEWithHmacSHA1AndAES_128", "PBEWithHmacSHA1AndAES_256", "PBEWithHmacSHA224AndAES_128", "PBEWithHmacSHA224AndAES_256", + "PBEWithHmacSHA256AndAES_128", "PBEWithHmacSHA256AndAES_256", "PBEWithHmacSHA384AndAES_128", "PBEWithHmacSHA384AndAES_256", + "PBEWithHmacSHA512AndAES_128", "PBEWithHmacSHA512AndAES_256", "PBEWithHmacSHA512/224AndAES_128", "PBEWithHmacSHA512/224AndAES_256", + "PBEWithHmacSHA512/256AndAES_128", "PBEWithHmacSHA512/256AndAES_256"}) public void SerializationPBKDF2tKeyTest (String algorithm) throws Exception { + if (algorithm.startsWith("PBE") && this.getProviderName().equalsIgnoreCase("OpenJCEPlusFIPS")) + return; + SecretKey key = null; PBEKeySpec pbeks = new PBEKeySpec("ABCDEFGHIJ".toCharArray(), new byte[32], 10000, 512); diff --git a/src/test/java/ibm/jceplus/junit/base/BaseTestPBECipher.java b/src/test/java/ibm/jceplus/junit/base/BaseTestPBECipher.java new file mode 100644 index 000000000..b40a33689 --- /dev/null +++ b/src/test/java/ibm/jceplus/junit/base/BaseTestPBECipher.java @@ -0,0 +1,134 @@ +/* + * Copyright IBM Corp. 2025 + * + * This code is free software; you can redistribute it and/or modify it + * under the terms provided by IBM in the LICENSE file that accompanied + * this code, including the "Classpath" Exception described therein. + */ +package ibm.jceplus.junit.base; + +import java.io.ByteArrayOutputStream; +import java.security.AlgorithmParameters; +import java.util.Locale; +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.PBEParameterSpec; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +public class BaseTestPBECipher extends BaseTestJunit5 { + + private byte[] ivBytes = { + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, + }; + private int iterationCount = 300000; + private byte[] salt = new byte[]{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; + + @ParameterizedTest + @CsvSource({"PBEWithHmacSHA1AndAES_128", "PBEWithHmacSHA1AndAES_256", "PBEWithHmacSHA224AndAES_128", "PBEWithHmacSHA224AndAES_256", + "PBEWithHmacSHA256AndAES_128", "PBEWithHmacSHA256AndAES_256", "PBEWithHmacSHA384AndAES_128", "PBEWithHmacSHA384AndAES_256", + "PBEWithHmacSHA512AndAES_128", "PBEWithHmacSHA512AndAES_256", "PBEWithHmacSHA512/224AndAES_128", "PBEWithHmacSHA512/224AndAES_256", + "PBEWithHmacSHA512/256AndAES_128", "PBEWithHmacSHA512/256AndAES_256", "PBEWithHmacSha1AndAES_128/CBC/PKCS5PAdding", + "PBEWithHmacSha1AndAES_256/CBC/PKCS5PAdding", "PBEWithHmacSHA224andAES_128/CBC/PkCS5Padding", "PBEWithHmacSHA224andAES_256/CBC/PkCS5Padding", + "PBEWithHmacSHA256AndAes_128/CBC/PKCS5PaddIng", "PBEWithHmacSHA256AndAes_256/CBC/PKCS5PaddIng", "PBEWithHmacSHa384AndAES_128/CbC/PKCS5Padding", + "PBEWithHmacSHa384AndAES_256/CbC/PKCS5Padding", "PBEWithHmacSHA512andAES_128/CBc/PKCS5Padding", "PBEWithHmacSHA512andAES_256/CBc/PKCS5Padding", + "PBEWithHmacSha512/224andAES_128/cBC/PKCS5Padding", "PBEWithHmacSha512/224andAES_256/cBC/PKCS5Padding", "PBEWithHmacShA512/256AndAES_128/CBC/pkCS5Padding", + "PBEWithHmacShA512/256AndAES_256/CBC/pkCS5Padding"}) + public void testPBE(String algorithm) throws Exception { + test(algorithm, true); // salt, ic, IV supplied by the application + test(algorithm, false); // salt, ic, IV generated by the implementation + } + + private void test(String algo, boolean suppliedParams) + throws Exception { + + // Create PBE key + SecretKey pbeKey = createKey(algo); + + // Create PBE cipher + Cipher cipher = createCipher(Cipher.ENCRYPT_MODE, algo, pbeKey, suppliedParams); + Cipher cipherEncrypt = createCipher(Cipher.ENCRYPT_MODE, algo, pbeKey, suppliedParams); + Cipher cipherWrap = createCipher(Cipher.WRAP_MODE, algo, pbeKey, suppliedParams); + Cipher cipherEncryptUpdate = createCipher(Cipher.ENCRYPT_MODE, algo, pbeKey, suppliedParams); + + byte[] cleartext = "This is just an example".getBytes(); + + // Encrypt + byte[] ciphertextSame = cipher.doFinal(cleartext); + byte[] ciphertext = cipherEncrypt.doFinal(cleartext); + + // Wrap + byte[] wrappedKey = cipherWrap.wrap(pbeKey); + + // Update + byte[] cipherUpdateText = update(cipherEncryptUpdate, cleartext, 4); + + // Decrypt + Cipher cipherDecrypt = createCipher(Cipher.DECRYPT_MODE, algo, pbeKey, cipherEncrypt.getParameters()); + cipher.init(Cipher.DECRYPT_MODE, pbeKey, cipher.getParameters()); + byte[] cleartextSame = cipher.doFinal(ciphertextSame); + byte[] cleartext2 = cipherDecrypt.doFinal(ciphertext); + + // Unwrap + Cipher cipherUnwrap = createCipher(Cipher.UNWRAP_MODE, algo, pbeKey, cipherWrap.getParameters()); + SecretKey unwrappedKey = (SecretKey) cipherUnwrap.unwrap(wrappedKey, pbeKey.getAlgorithm(), Cipher.SECRET_KEY); + + // Decrypt Update + Cipher cipherDecryptUpdate = createCipher(Cipher.DECRYPT_MODE, algo, pbeKey, cipherEncryptUpdate.getParameters()); + byte[] cleartextUpdate = update(cipherDecryptUpdate, cipherUpdateText, 5); + + assertArrayEquals(cleartext, cleartextSame); + assertArrayEquals(cleartext, cleartext2); + assertArrayEquals(pbeKey.getEncoded(), unwrappedKey.getEncoded()); + assertArrayEquals(cleartext, cleartextUpdate); + } + + private SecretKey createKey(String algorithm) throws Exception { + PBEKeySpec pbeKeySpec = new PBEKeySpec("mypassword".toCharArray()); + int modeIdx = algorithm.toUpperCase(Locale.ENGLISH).indexOf("/CBC"); + String keyAlgo = (modeIdx == -1 ? algorithm : algorithm.substring(0, modeIdx)); + SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(keyAlgo, getProviderName()); + SecretKey pbeKey = keyFactory.generateSecret(pbeKeySpec); + + return pbeKey; + } + + private Cipher createCipher(int mode, String algorithm, SecretKey pbeKey, boolean suppliedParams) throws Exception { + Cipher c = Cipher.getInstance(algorithm, getProviderName()); + if (suppliedParams) { + c.init(mode, pbeKey, + new PBEParameterSpec(salt, iterationCount, + new IvParameterSpec(ivBytes))); + } else { + c.init(mode, pbeKey); + } + + return c; + } + + private Cipher createCipher(int mode, String algorithm, SecretKey pbeKey, AlgorithmParameters params) throws Exception { + Cipher c = Cipher.getInstance(algorithm, getProviderName()); + c.init(mode, pbeKey, params); + return c; + } + + private byte[] update(Cipher c, byte[] text, int offset) throws Exception { + byte[] update1 = c.update(text, 0, offset); + byte[] update2 = c.update(text, offset, offset); + byte[] update3 = c.update(text, 2 * offset, text.length - (2 * offset)); + byte[] finalUpdate = c.doFinal(); + ByteArrayOutputStream encryptStream = new ByteArrayOutputStream(); + encryptStream.write(update1); + encryptStream.write(update2); + encryptStream.write(update3); + encryptStream.write(finalUpdate); + finalUpdate = encryptStream.toByteArray(); + + return finalUpdate; + } +} diff --git a/src/test/java/ibm/jceplus/junit/base/BaseTestPBECipherInterop.java b/src/test/java/ibm/jceplus/junit/base/BaseTestPBECipherInterop.java new file mode 100644 index 000000000..2876e4b1c --- /dev/null +++ b/src/test/java/ibm/jceplus/junit/base/BaseTestPBECipherInterop.java @@ -0,0 +1,142 @@ +/* + * Copyright IBM Corp. 2025 + * + * This code is free software; you can redistribute it and/or modify it + * under the terms provided by IBM in the LICENSE file that accompanied + * this code, including the "Classpath" Exception described therein. + */ + +package ibm.jceplus.junit.base; + +import java.io.ByteArrayOutputStream; +import java.security.AlgorithmParameters; +import java.util.Locale; +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.PBEParameterSpec; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +public class BaseTestPBECipherInterop extends BaseTestJunit5Interop { + + private byte[] ivBytes = { + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, + }; + private int iterationCount = 300000; + private byte[] salt = new byte[]{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; + + @ParameterizedTest + @CsvSource({"PBEWithHmacSHA1AndAES_128", "PBEWithHmacSHA1AndAES_256", "PBEWithHmacSHA224AndAES_128", "PBEWithHmacSHA224AndAES_256", + "PBEWithHmacSHA256AndAES_128", "PBEWithHmacSHA256AndAES_256", "PBEWithHmacSHA384AndAES_128", "PBEWithHmacSHA384AndAES_256", + "PBEWithHmacSHA512AndAES_128", "PBEWithHmacSHA512AndAES_256", "PBEWithHmacSHA512/224AndAES_128", "PBEWithHmacSHA512/224AndAES_256", + "PBEWithHmacSHA512/256AndAES_128", "PBEWithHmacSHA512/256AndAES_256", "PBEWithHmacSha1AndAES_128/CBC/PKCS5PAdding", + "PBEWithHmacSha1AndAES_256/CBC/PKCS5PAdding", "PBEWithHmacSHA224andAES_128/CBC/PkCS5Padding", "PBEWithHmacSHA224andAES_256/CBC/PkCS5Padding", + "PBEWithHmacSHA256AndAes_128/CBC/PKCS5PaddIng", "PBEWithHmacSHA256AndAes_256/CBC/PKCS5PaddIng", "PBEWithHmacSHa384AndAES_128/CbC/PKCS5Padding", + "PBEWithHmacSHa384AndAES_256/CbC/PKCS5Padding", "PBEWithHmacSHA512andAES_128/CBc/PKCS5Padding", "PBEWithHmacSHA512andAES_256/CBc/PKCS5Padding", + "PBEWithHmacSha512/224andAES_128/cBC/PKCS5Padding", "PBEWithHmacSha512/224andAES_256/cBC/PKCS5Padding", "PBEWithHmacShA512/256AndAES_128/CBC/pkCS5Padding", + "PBEWithHmacShA512/256AndAES_256/CBC/pkCS5Padding"}) + public void testPBE(String algorithm) throws Exception { + test(algorithm, true, 1); // salt, ic, IV supplied by the application + test(algorithm, true, 2); // salt, ic, IV generated by the implementation + test(algorithm, false, 1); // salt, ic, IV supplied by the application + test(algorithm, false, 2); // salt, ic, IV generated by the implementation + } + + private void test(String algo, boolean suppliedParams, int variation) + throws Exception { + + String provider, interopProvider; + if (variation == 1) { + provider = getProviderName(); + interopProvider = getInteropProviderName(); + } else { + provider = getInteropProviderName(); + interopProvider = getProviderName(); + } + + // Create PBE key + SecretKey pbeKey = createKey(algo, provider); + + // Create PBE cipher + Cipher cipherEncrypt = createCipher(Cipher.ENCRYPT_MODE, algo, pbeKey, suppliedParams, provider); + Cipher cipherWrap = createCipher(Cipher.WRAP_MODE, algo, pbeKey, suppliedParams, provider); + Cipher cipherEncryptUpdate = createCipher(Cipher.ENCRYPT_MODE, algo, pbeKey, suppliedParams, provider); + + byte[] cleartext = "This is just an example".getBytes(); + + // Encrypt + byte[] ciphertext = cipherEncrypt.doFinal(cleartext); + + // Wrap + byte[] wrappedKey = cipherWrap.wrap(pbeKey); + + // Update + byte[] cipherUpdateText = update(cipherEncryptUpdate, cleartext, 4); + + // Decrypt + Cipher cipherDecrypt = createCipher(Cipher.DECRYPT_MODE, algo, pbeKey, cipherEncrypt.getParameters(), interopProvider); + byte[] cleartext2 = cipherDecrypt.doFinal(ciphertext); + + // Unwrap + Cipher cipherUnwrap = createCipher(Cipher.UNWRAP_MODE, algo, pbeKey, cipherWrap.getParameters(), interopProvider); + SecretKey unwrappedKey = (SecretKey) cipherUnwrap.unwrap(wrappedKey, pbeKey.getAlgorithm(), Cipher.SECRET_KEY); + + // Decrypt Update + Cipher cipherDecryptUpdate = createCipher(Cipher.DECRYPT_MODE, algo, pbeKey, cipherEncryptUpdate.getParameters(), interopProvider); + byte[] cleartextUpdate = update(cipherDecryptUpdate, cipherUpdateText, 5); + + assertArrayEquals(cleartext, cleartext2); + assertArrayEquals(pbeKey.getEncoded(), unwrappedKey.getEncoded()); + assertArrayEquals(cleartext, cleartextUpdate); + } + + private SecretKey createKey(String algorithm, String provider) throws Exception { + PBEKeySpec pbeKeySpec = new PBEKeySpec("mypassword".toCharArray()); + int modeIdx = algorithm.toUpperCase(Locale.ENGLISH).indexOf("/CBC"); + String keyAlgo = (modeIdx == -1 ? algorithm : algorithm.substring(0, modeIdx)); + SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(keyAlgo, provider); + SecretKey pbeKey = keyFactory.generateSecret(pbeKeySpec); + + return pbeKey; + } + + private Cipher createCipher(int mode, String algorithm, SecretKey pbeKey, boolean suppliedParams, String provider) throws Exception { + Cipher c = Cipher.getInstance(algorithm, provider); + if (suppliedParams) { + c.init(mode, pbeKey, + new PBEParameterSpec(salt, iterationCount, + new IvParameterSpec(ivBytes))); + } else { + c.init(mode, pbeKey); + } + + return c; + } + + private Cipher createCipher(int mode, String algorithm, SecretKey pbeKey, AlgorithmParameters params, String provider) throws Exception { + Cipher c = Cipher.getInstance(algorithm, provider); + c.init(mode, pbeKey, params); + return c; + } + + private byte[] update(Cipher c, byte[] text, int offset) throws Exception { + byte[] update1 = c.update(text, 0, offset); + byte[] update2 = c.update(text, offset, offset); + byte[] update3 = c.update(text, 2 * offset, text.length - (2 * offset)); + byte[] finalUpdate = c.doFinal(); + ByteArrayOutputStream encryptStream = new ByteArrayOutputStream(); + encryptStream.write(update1); + encryptStream.write(update2); + encryptStream.write(update3); + encryptStream.write(finalUpdate); + finalUpdate = encryptStream.toByteArray(); + + return finalUpdate; + } +} + diff --git a/src/test/java/ibm/jceplus/junit/base/BaseTestPBEKeyFactory.java b/src/test/java/ibm/jceplus/junit/base/BaseTestPBEKeyFactory.java new file mode 100644 index 000000000..6161e2363 --- /dev/null +++ b/src/test/java/ibm/jceplus/junit/base/BaseTestPBEKeyFactory.java @@ -0,0 +1,58 @@ +/* + * Copyright IBM Corp. 2025 + * + * This code is free software; you can redistribute it and/or modify it + * under the terms provided by IBM in the LICENSE file that accompanied + * this code, including the "Classpath" Exception described therein. + */ + +package ibm.jceplus.junit.base; + +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +public class BaseTestPBEKeyFactory extends BaseTestJunit5 { + + @ParameterizedTest + @CsvSource({"PBEWithHmacSHA1AndAES_128", "PBEWithHmacSHA1AndAES_256", "PBEWithHmacSHA224AndAES_128", "PBEWithHmacSHA224AndAES_256", + "PBEWithHmacSHA256AndAES_128", "PBEWithHmacSHA256AndAES_256", "PBEWithHmacSHA384AndAES_128", "PBEWithHmacSHA384AndAES_256", + "PBEWithHmacSHA512AndAES_128", "PBEWithHmacSHA512AndAES_256", "PBEWithHmacSHA512/224AndAES_128", "PBEWithHmacSHA512/224AndAES_256", + "PBEWithHmacSHA512/256AndAES_128", "PBEWithHmacSHA512/256AndAES_256"}) + public void testValid(String algorithm) throws Exception { + + SecretKeyFactory fac = SecretKeyFactory.getInstance(algorithm, getProviderName()); + + char[] pass = new char[] {'p', 'a', 's', 's', 'w', 'o', 'r', 'd'}; + PBEKeySpec spec = new PBEKeySpec(pass); + SecretKey skey = fac.generateSecret(spec); + KeySpec spec1 = fac.getKeySpec(skey, PBEKeySpec.class); + SecretKey skey1 = fac.generateSecret(spec1); + assertEquals(skey, skey1); + } + + @ParameterizedTest + @CsvSource({"PBEWithHmacSHA1AndAES_128", "PBEWithHmacSHA1AndAES_256", "PBEWithHmacSHA224AndAES_128", "PBEWithHmacSHA224AndAES_256", + "PBEWithHmacSHA256AndAES_128", "PBEWithHmacSHA256AndAES_256", "PBEWithHmacSHA384AndAES_128", "PBEWithHmacSHA384AndAES_256", + "PBEWithHmacSHA512AndAES_128", "PBEWithHmacSHA512AndAES_256", "PBEWithHmacSHA512/224AndAES_128", "PBEWithHmacSHA512/224AndAES_256", + "PBEWithHmacSHA512/256AndAES_128", "PBEWithHmacSHA512/256AndAES_256"}) + public void testInvalid(String algorithm) throws Exception { + + SecretKeyFactory fac = SecretKeyFactory.getInstance(algorithm, getProviderName()); + + char[] pass = new char[] {'p', 'a', 's', 's', 'w', 'o', 'r', '\u0019'}; + PBEKeySpec spec = new PBEKeySpec(pass); + try { + SecretKey skey = fac.generateSecret(spec); + fail("Expected InvalidKeySpecException for algorithm: " + skey.getAlgorithm() + " but none was thrown."); + } catch (InvalidKeySpecException e) { + assertEquals("Invalid Password.", e.getMessage()); + } + } +} diff --git a/src/test/java/ibm/jceplus/junit/base/BaseTestPBEKeyFactoryInterop.java b/src/test/java/ibm/jceplus/junit/base/BaseTestPBEKeyFactoryInterop.java new file mode 100644 index 000000000..9fdc706bc --- /dev/null +++ b/src/test/java/ibm/jceplus/junit/base/BaseTestPBEKeyFactoryInterop.java @@ -0,0 +1,200 @@ +/* + * Copyright IBM Corp. 2025 + * + * This code is free software; you can redistribute it and/or modify it + * under the terms provided by IBM in the LICENSE file that accompanied + * this code, including the "Classpath" Exception described therein. + */ + +package ibm.jceplus.junit.base; + +import java.security.SecureRandom; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Tests to perform interoperability tests between a provider under test, + * typically OpenJCEPlus or OpenJCEPlusFIPS and another provider for + * PBE supported algorithms. + */ +public class BaseTestPBEKeyFactoryInterop extends BaseTestJunit5Interop { + + final String PASSWORD = "Thequickbrownfoxjumpsoverthelazydog"; + byte[] randomSalt = new byte[32]; + SecureRandom random = new SecureRandom(); + PBEKeySpec pbeks = null; + + @BeforeAll + public void setUp() { + random.nextBytes(randomSalt); + this.pbeks = new PBEKeySpec(PASSWORD.toCharArray()); + } + /** + * Test used to perform interoperability tests using a KeyFactory for + * the method `getAlgorithm()`. + */ + @ParameterizedTest + @CsvSource({"PBEWithHmacSHA1AndAES_128", "PBEWithHmacSHA1AndAES_256", "PBEWithHmacSHA224AndAES_128", "PBEWithHmacSHA224AndAES_256", + "PBEWithHmacSHA256AndAES_128", "PBEWithHmacSHA256AndAES_256", "PBEWithHmacSHA384AndAES_128", "PBEWithHmacSHA384AndAES_256", + "PBEWithHmacSHA512AndAES_128", "PBEWithHmacSHA512AndAES_256", "PBEWithHmacSHA512/224AndAES_128", "PBEWithHmacSHA512/224AndAES_256", + "PBEWithHmacSHA512/256AndAES_128", "PBEWithHmacSHA512/256AndAES_256"}) + public void testGetAlgorithm(String algorithm) throws Exception { + + SecretKeyFactory skf = SecretKeyFactory.getInstance(algorithm, this.getProviderName()); + SecretKeyFactory skfInterop = SecretKeyFactory.getInstance(algorithm, + this.getInteropProviderName()); + + // Validate that the algorithm name from provider under test matches the interop provider. + System.out.println(" Checking getAlgorithm()"); + assertEquals(skf.getAlgorithm(), skfInterop.getAlgorithm(), + "Algorithm name is not as expected."); + } + + /** + * Test used to perform interoperability tests using a KeyFactory for + * the method `getEncoded()`. + */ + @ParameterizedTest + @CsvSource({"PBEWithHmacSHA1AndAES_128", "PBEWithHmacSHA1AndAES_256", "PBEWithHmacSHA224AndAES_128", "PBEWithHmacSHA224AndAES_256", + "PBEWithHmacSHA256AndAES_128", "PBEWithHmacSHA256AndAES_256", "PBEWithHmacSHA384AndAES_128", "PBEWithHmacSHA384AndAES_256", + "PBEWithHmacSHA512AndAES_128", "PBEWithHmacSHA512AndAES_256", "PBEWithHmacSHA512/224AndAES_128", "PBEWithHmacSHA512/224AndAES_256", + "PBEWithHmacSHA512/256AndAES_128", "PBEWithHmacSHA512/256AndAES_256"}) + public void testGetEncoding(String algorithm) throws Exception { + + SecretKeyFactory skf = SecretKeyFactory.getInstance(algorithm, this.getProviderName()); + SecretKeyFactory skfInterop = SecretKeyFactory.getInstance(algorithm, + this.getInteropProviderName()); + + // Validate key encodings generated from provider under test matches the interop provider. + SecretKey sk1 = skf.generateSecret(pbeks); + SecretKey skInterop = skfInterop.generateSecret(pbeks); + assertArrayEquals(sk1.getEncoded(), skInterop.getEncoded(), "Key encodings do not match."); + } + + /** + * Test used to perform interoperability tests using a KeyFactory for + * the method `translateKey()`. + */ + @ParameterizedTest + @CsvSource({"PBEWithHmacSHA1AndAES_128", "PBEWithHmacSHA1AndAES_256", "PBEWithHmacSHA224AndAES_128", "PBEWithHmacSHA224AndAES_256", + "PBEWithHmacSHA256AndAES_128", "PBEWithHmacSHA256AndAES_256", "PBEWithHmacSHA384AndAES_128", "PBEWithHmacSHA384AndAES_256", + "PBEWithHmacSHA512AndAES_128", "PBEWithHmacSHA512AndAES_256", "PBEWithHmacSHA512/224AndAES_128", "PBEWithHmacSHA512/224AndAES_256", + "PBEWithHmacSHA512/256AndAES_128", "PBEWithHmacSHA512/256AndAES_256"}) + public void testTranslate(String algorithm) throws Exception { + + SecretKeyFactory skf = SecretKeyFactory.getInstance(algorithm, this.getProviderName()); + SecretKeyFactory skfInterop = SecretKeyFactory.getInstance(algorithm, + this.getInteropProviderName()); + + // Validate key translations of the same key generated from provider under test matches the interop provider. + System.out.println(" Checking translateKey()"); + SecretKey sk1 = skf.generateSecret(pbeks); + SecretKey skInterop = skfInterop.generateSecret(pbeks); + SecretKey sk1T = skf.translateKey(sk1); + SecretKey skInteropT = skf.translateKey(skInterop); + assertArrayEquals(sk1T.getEncoded(), skInteropT.getEncoded(), + "Translated keys do not match."); + SecretKey sk1TI = skfInterop.translateKey(sk1); + SecretKey skInteropTI = skfInterop.translateKey(skInterop); + assertArrayEquals(sk1TI.getEncoded(), skInteropTI.getEncoded(), + "Translated keys do not match."); + } + + /** + * Test used to perform interoperability tests using a KeyFactory for + * the method `getKeySpec()`. + */ + @ParameterizedTest + @CsvSource({"PBEWithHmacSHA1AndAES_128", "PBEWithHmacSHA1AndAES_256", "PBEWithHmacSHA224AndAES_128", "PBEWithHmacSHA224AndAES_256", + "PBEWithHmacSHA256AndAES_128", "PBEWithHmacSHA256AndAES_256", "PBEWithHmacSHA384AndAES_128", "PBEWithHmacSHA384AndAES_256", + "PBEWithHmacSHA512AndAES_128", "PBEWithHmacSHA512AndAES_256", "PBEWithHmacSHA512/224AndAES_128", "PBEWithHmacSHA512/224AndAES_256", + "PBEWithHmacSHA512/256AndAES_128", "PBEWithHmacSHA512/256AndAES_256"}) + public void testKeySpec(String algorithm) throws Exception { + + SecretKeyFactory skf = SecretKeyFactory.getInstance(algorithm, this.getProviderName()); + SecretKeyFactory skfInterop = SecretKeyFactory.getInstance(algorithm, + this.getInteropProviderName()); + + // Validate that the key spec produced by the provider under test matches the interop provider. + System.out.println(" Checking getKeySpec()"); + SecretKey sk1 = skf.generateSecret(pbeks); + SecretKey skInterop = skfInterop.generateSecret(pbeks); + PBEKeySpec ks1 = (PBEKeySpec) skf.getKeySpec(sk1, PBEKeySpec.class); + PBEKeySpec ksInterop = (PBEKeySpec) skfInterop.getKeySpec(skInterop, PBEKeySpec.class); + assertEquals(ks1.getIterationCount(), ksInterop.getIterationCount(), + "Iteration count does not match."); + assertEquals(ks1.getKeyLength(), ksInterop.getKeyLength(), "Key length does not match."); + assertArrayEquals(ks1.getPassword(), ksInterop.getPassword(), "Password does not match."); + assertArrayEquals(ks1.getSalt(), ksInterop.getSalt(), "Salt does not match."); + } + + /** + * Test used to perform interoperability tests using a KeyFactory for + * the method `hashCode()`. + */ + @ParameterizedTest + @CsvSource({"PBEWithHmacSHA1AndAES_128", "PBEWithHmacSHA1AndAES_256", "PBEWithHmacSHA224AndAES_128", "PBEWithHmacSHA224AndAES_256", + "PBEWithHmacSHA256AndAES_128", "PBEWithHmacSHA256AndAES_256", "PBEWithHmacSHA384AndAES_128", "PBEWithHmacSHA384AndAES_256", + "PBEWithHmacSHA512AndAES_128", "PBEWithHmacSHA512AndAES_256", "PBEWithHmacSHA512/224AndAES_128", "PBEWithHmacSHA512/224AndAES_256", + "PBEWithHmacSHA512/256AndAES_128", "PBEWithHmacSHA512/256AndAES_256"}) + public void testHashCode(String algorithm) throws Exception { + + SecretKeyFactory skf = SecretKeyFactory.getInstance(algorithm, this.getProviderName()); + SecretKeyFactory skfInterop = SecretKeyFactory.getInstance(algorithm, + this.getInteropProviderName()); + SecretKey sk1 = skf.generateSecret(pbeks); + SecretKey skInterop = skfInterop.generateSecret(pbeks); + assertEquals(sk1.hashCode(), skInterop.hashCode(), "Hash codes do not match." + algorithm); + } + + /** + * Test used to perform interoperability tests using a KeyFactory for + * the method `equals()`. + */ + @ParameterizedTest + @CsvSource({"PBEWithHmacSHA1AndAES_128", "PBEWithHmacSHA1AndAES_256", "PBEWithHmacSHA224AndAES_128", "PBEWithHmacSHA224AndAES_256", + "PBEWithHmacSHA256AndAES_128", "PBEWithHmacSHA256AndAES_256", "PBEWithHmacSHA384AndAES_128", "PBEWithHmacSHA384AndAES_256", + "PBEWithHmacSHA512AndAES_128", "PBEWithHmacSHA512AndAES_256", "PBEWithHmacSHA512/224AndAES_128", "PBEWithHmacSHA512/224AndAES_256", + "PBEWithHmacSHA512/256AndAES_128", "PBEWithHmacSHA512/256AndAES_256"}) + public void testEquality(String algorithm) throws Exception { + + SecretKeyFactory skf = SecretKeyFactory.getInstance(algorithm, this.getProviderName()); + SecretKeyFactory skfInterop = SecretKeyFactory.getInstance(algorithm, + this.getInteropProviderName()); + SecretKey sk1 = skf.generateSecret(pbeks); + SecretKey skInterop = skfInterop.generateSecret(pbeks); + assertTrue(sk1.equals(skInterop), "Keys are not equal between different providers." + algorithm); + assertTrue(sk1.equals(sk1), "Keys are not equal when key is exactly the same."); + + PBEKeySpec pbeksDifferent = new PBEKeySpec("DifferentPW".toCharArray()); + SecretKey skDifferent = skf.generateSecret(pbeksDifferent); + assertFalse(sk1.equals(skDifferent), "Keys are not expected to be equal."); + } + + /** + * Test used to perform interoperability tests using a SecretKey generated + * by a providers KeyFactory for the method `getFormat()`. + */ + @ParameterizedTest + @CsvSource({"PBEWithHmacSHA1AndAES_128", "PBEWithHmacSHA1AndAES_256", "PBEWithHmacSHA224AndAES_128", "PBEWithHmacSHA224AndAES_256", + "PBEWithHmacSHA256AndAES_128", "PBEWithHmacSHA256AndAES_256", "PBEWithHmacSHA384AndAES_128", "PBEWithHmacSHA384AndAES_256", + "PBEWithHmacSHA512AndAES_128", "PBEWithHmacSHA512AndAES_256", "PBEWithHmacSHA512/224AndAES_128", "PBEWithHmacSHA512/224AndAES_256", + "PBEWithHmacSHA512/256AndAES_128", "PBEWithHmacSHA512/256AndAES_256"}) + public void testGetFormat(String algorithm) throws Exception { + + SecretKeyFactory skf = SecretKeyFactory.getInstance(algorithm, this.getProviderName()); + SecretKeyFactory skfInterop = SecretKeyFactory.getInstance(algorithm, + this.getInteropProviderName()); + SecretKey sk1 = skf.generateSecret(pbeks); + SecretKey skInterop = skfInterop.generateSecret(pbeks); + assertEquals(sk1.getFormat(), skInterop.getFormat(), "Format does not match."); + } +} diff --git a/src/test/java/ibm/jceplus/junit/base/BaseTestPBES2Parameters.java b/src/test/java/ibm/jceplus/junit/base/BaseTestPBES2Parameters.java new file mode 100644 index 000000000..7c4242b74 --- /dev/null +++ b/src/test/java/ibm/jceplus/junit/base/BaseTestPBES2Parameters.java @@ -0,0 +1,50 @@ +/* + * Copyright IBM Corp. 2025 + * + * This code is free software; you can redistribute it and/or modify it + * under the terms provided by IBM in the LICENSE file that accompanied + * this code, including the "Classpath" Exception described therein. + */ + +package ibm.jceplus.junit.base; + +import java.security.AlgorithmParameters; +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.PBEParameterSpec; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class BaseTestPBES2Parameters extends BaseTestJunit5 { + + @ParameterizedTest + @CsvSource({"PBEWithHmacSHA1AndAES_128", "PBEWithHmacSHA1AndAES_256", "PBEWithHmacSHA224AndAES_128", "PBEWithHmacSHA224AndAES_256", + "PBEWithHmacSHA256AndAES_128", "PBEWithHmacSHA256AndAES_256", "PBEWithHmacSHA384AndAES_128", "PBEWithHmacSHA384AndAES_256", + "PBEWithHmacSHA512AndAES_128", "PBEWithHmacSHA512AndAES_256", "PBEWithHmacSHA512/224AndAES_128", "PBEWithHmacSHA512/224AndAES_256", + "PBEWithHmacSHA512/256AndAES_128", "PBEWithHmacSHA512/256AndAES_256"}) + public void testParameters(String algorithm) throws Exception { + PBEKeySpec ks = new PBEKeySpec("password".toCharArray()); + SecretKeyFactory skf = SecretKeyFactory.getInstance(algorithm, getProviderName()); + SecretKey key = skf.generateSecret(ks); + + Cipher c = Cipher.getInstance(algorithm, getProviderName()); + c.init(Cipher.ENCRYPT_MODE, key); + c.doFinal("This is just an example".getBytes()); + + AlgorithmParameters params = c.getParameters(); + + AlgorithmParameters testParams = AlgorithmParameters.getInstance(algorithm, getProviderName()); + testParams.init(params.getEncoded()); + + assertEquals(algorithm, testParams.getAlgorithm()); + PBEParameterSpec spec = params.getParameterSpec(PBEParameterSpec.class); + PBEParameterSpec testSpec = testParams.getParameterSpec(PBEParameterSpec.class); + assertArrayEquals(spec.getSalt(), testSpec.getSalt()); + assertEquals(spec.getIterationCount(), testSpec.getIterationCount()); + assertArrayEquals(params.getEncoded(), testParams.getEncoded()); + } +} diff --git a/src/test/java/ibm/jceplus/junit/openjceplus/TestAll.java b/src/test/java/ibm/jceplus/junit/openjceplus/TestAll.java index e13129d54..625511e1f 100644 --- a/src/test/java/ibm/jceplus/junit/openjceplus/TestAll.java +++ b/src/test/java/ibm/jceplus/junit/openjceplus/TestAll.java @@ -99,6 +99,11 @@ TestMD5.class, TestMiniRSAPSS2.class, TestOAEPOrderCheck.class, + TestPBEKeyFactory.class, + TestPBEKeyFactoryInterop.class, + TestPBES2Parameters.class, + TestPBECipher.class, + TestPBECipherInterop.class, TestPBKDF2.class, TestPBKDF2Interop.class, TestPQCKEM.class, diff --git a/src/test/java/ibm/jceplus/junit/openjceplus/TestPBECipher.java b/src/test/java/ibm/jceplus/junit/openjceplus/TestPBECipher.java new file mode 100644 index 000000000..d031244bd --- /dev/null +++ b/src/test/java/ibm/jceplus/junit/openjceplus/TestPBECipher.java @@ -0,0 +1,25 @@ +/* + * Copyright IBM Corp. 2025 + * + * This code is free software; you can redistribute it and/or modify it + * under the terms provided by IBM in the LICENSE file that accompanied + * this code, including the "Classpath" Exception described therein. + */ + +package ibm.jceplus.junit.openjceplus; + +import ibm.jceplus.junit.base.BaseTestPBECipher; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestInstance.Lifecycle; + +@TestInstance(Lifecycle.PER_CLASS) +public class TestPBECipher extends BaseTestPBECipher{ + + @BeforeAll + public void beforeAll() { + Utils.loadProviderTestSuite(); + setProviderName(Utils.TEST_SUITE_PROVIDER_NAME); + } + +} diff --git a/src/test/java/ibm/jceplus/junit/openjceplus/TestPBECipherInterop.java b/src/test/java/ibm/jceplus/junit/openjceplus/TestPBECipherInterop.java new file mode 100644 index 000000000..5ed5a5749 --- /dev/null +++ b/src/test/java/ibm/jceplus/junit/openjceplus/TestPBECipherInterop.java @@ -0,0 +1,26 @@ +/* + * Copyright IBM Corp. 2025 + * + * This code is free software; you can redistribute it and/or modify it + * under the terms provided by IBM in the LICENSE file that accompanied + * this code, including the "Classpath" Exception described therein. + */ + +package ibm.jceplus.junit.openjceplus; + +import ibm.jceplus.junit.base.BaseTestPBECipherInterop; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestInstance.Lifecycle; + +@TestInstance(Lifecycle.PER_CLASS) +public class TestPBECipherInterop extends BaseTestPBECipherInterop { + + @BeforeAll + public void beforeAll() { + Utils.loadProviderTestSuite(); + setProviderName(Utils.TEST_SUITE_PROVIDER_NAME); + setInteropProviderName(Utils.PROVIDER_SunJCE); + } + +} diff --git a/src/test/java/ibm/jceplus/junit/openjceplus/TestPBEKeyFactory.java b/src/test/java/ibm/jceplus/junit/openjceplus/TestPBEKeyFactory.java new file mode 100644 index 000000000..d5820d609 --- /dev/null +++ b/src/test/java/ibm/jceplus/junit/openjceplus/TestPBEKeyFactory.java @@ -0,0 +1,24 @@ +/* + * Copyright IBM Corp. 2025 + * + * This code is free software; you can redistribute it and/or modify it + * under the terms provided by IBM in the LICENSE file that accompanied + * this code, including the "Classpath" Exception described therein. + */ + +package ibm.jceplus.junit.openjceplus; + +import ibm.jceplus.junit.base.BaseTestPBEKeyFactory; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestInstance.Lifecycle; + +@TestInstance(Lifecycle.PER_CLASS) +public class TestPBEKeyFactory extends BaseTestPBEKeyFactory { + + @BeforeAll + public void beforeAll() { + Utils.loadProviderTestSuite(); + setProviderName(Utils.TEST_SUITE_PROVIDER_NAME); + } +} diff --git a/src/test/java/ibm/jceplus/junit/openjceplus/TestPBEKeyFactoryInterop.java b/src/test/java/ibm/jceplus/junit/openjceplus/TestPBEKeyFactoryInterop.java new file mode 100644 index 000000000..d58b0958d --- /dev/null +++ b/src/test/java/ibm/jceplus/junit/openjceplus/TestPBEKeyFactoryInterop.java @@ -0,0 +1,25 @@ +/* + * Copyright IBM Corp. 2025 + * + * This code is free software; you can redistribute it and/or modify it + * under the terms provided by IBM in the LICENSE file that accompanied + * this code, including the "Classpath" Exception described therein. + */ + +package ibm.jceplus.junit.openjceplus; + +import ibm.jceplus.junit.base.BaseTestPBEKeyFactoryInterop; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestInstance.Lifecycle; + +@TestInstance(Lifecycle.PER_CLASS) +public class TestPBEKeyFactoryInterop extends BaseTestPBEKeyFactoryInterop { + + @BeforeAll + public void beforeAll() { + Utils.loadProviderTestSuite(); + setProviderName(Utils.TEST_SUITE_PROVIDER_NAME); + setInteropProviderName(Utils.PROVIDER_SunJCE); + } +} diff --git a/src/test/java/ibm/jceplus/junit/openjceplus/TestPBES2Parameters.java b/src/test/java/ibm/jceplus/junit/openjceplus/TestPBES2Parameters.java new file mode 100644 index 000000000..e85676055 --- /dev/null +++ b/src/test/java/ibm/jceplus/junit/openjceplus/TestPBES2Parameters.java @@ -0,0 +1,24 @@ +/* + * Copyright IBM Corp. 2025 + * + * This code is free software; you can redistribute it and/or modify it + * under the terms provided by IBM in the LICENSE file that accompanied + * this code, including the "Classpath" Exception described therein. + */ + +package ibm.jceplus.junit.openjceplus; + +import ibm.jceplus.junit.base.BaseTestPBES2Parameters; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestInstance.Lifecycle; + +@TestInstance(Lifecycle.PER_CLASS) +public class TestPBES2Parameters extends BaseTestPBES2Parameters { + + @BeforeAll + public void beforeAll() { + Utils.loadProviderTestSuite(); + setProviderName(Utils.TEST_SUITE_PROVIDER_NAME); + } +}