commit
4aca27554a
|
|
@ -19,6 +19,14 @@ extern "C" {
|
|||
#define com_wolfssl_wolfcrypt_Dh_WC_FFDHE_6144 259L
|
||||
#undef com_wolfssl_wolfcrypt_Dh_WC_FFDHE_8192
|
||||
#define com_wolfssl_wolfcrypt_Dh_WC_FFDHE_8192 260L
|
||||
/*
|
||||
* Class: com_wolfssl_wolfcrypt_Dh
|
||||
* Method: dhMinSize
|
||||
* Signature: ()I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_Dh_dhMinSize
|
||||
(JNIEnv *, jclass);
|
||||
|
||||
/*
|
||||
* Class: com_wolfssl_wolfcrypt_Dh
|
||||
* Method: mallocNativeStruct_internal
|
||||
|
|
|
|||
|
|
@ -335,6 +335,14 @@ JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_RsaKeyGenEna
|
|||
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_RsaPssEnabled
|
||||
(JNIEnv *, jclass);
|
||||
|
||||
/*
|
||||
* Class: com_wolfssl_wolfcrypt_FeatureDetect
|
||||
* Method: RsaPssLongSaltEnabled
|
||||
* Signature: ()Z
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_RsaPssLongSaltEnabled
|
||||
(JNIEnv *, jclass);
|
||||
|
||||
/*
|
||||
* Class: com_wolfssl_wolfcrypt_FeatureDetect
|
||||
* Method: RsaOaepEnabled
|
||||
|
|
|
|||
25
jni/jni_dh.c
25
jni/jni_dh.c
|
|
@ -41,6 +41,18 @@
|
|||
#define RNG WC_RNG
|
||||
#endif
|
||||
|
||||
/* Some FIPS versions don't have DH_MIN_SIZE defined. Values match those
|
||||
* used by wolfSSL settings.h. */
|
||||
#ifndef DH_MIN_SIZE
|
||||
#ifdef HAVE_FIPS
|
||||
#define DH_MIN_SIZE 2048
|
||||
#elif defined(WOLFSSL_MIN_DHKEY_BITS)
|
||||
#define DH_MIN_SIZE WOLFSSL_MIN_DHKEY_BITS
|
||||
#else
|
||||
#define DH_MIN_SIZE 1024
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Some FIPS versions don't have DH_MAX_SIZE defined */
|
||||
#ifndef DH_MAX_SIZE
|
||||
#ifdef USE_FAST_MATH
|
||||
|
|
@ -60,6 +72,19 @@
|
|||
#endif
|
||||
#endif
|
||||
|
||||
JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_Dh_dhMinSize
|
||||
(JNIEnv* env, jclass jcl)
|
||||
{
|
||||
(void)env;
|
||||
(void)jcl;
|
||||
|
||||
#if !defined(NO_DH)
|
||||
return (jint)DH_MIN_SIZE;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
JNIEXPORT jlong JNICALL Java_com_wolfssl_wolfcrypt_Dh_mallocNativeStruct_1internal(
|
||||
JNIEnv* env, jobject this)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -522,6 +522,18 @@ JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_RsaPssEnable
|
|||
#endif
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_RsaPssLongSaltEnabled
|
||||
(JNIEnv* env, jclass jcl)
|
||||
{
|
||||
(void)env;
|
||||
(void)jcl;
|
||||
#if !defined(NO_RSA) && defined(WC_RSA_PSS) && defined(WOLFSSL_PSS_LONG_SALT)
|
||||
return JNI_TRUE;
|
||||
#else
|
||||
return JNI_FALSE;
|
||||
#endif
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_RsaOaepEnabled
|
||||
(JNIEnv* env, jclass jcl)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -42,6 +42,17 @@
|
|||
/* #define WOLFCRYPT_JNI_DEBUG_ON */
|
||||
#include <wolfcrypt_jni_debug.h>
|
||||
|
||||
/* Max PEM input size for the single-block PEM to DER conversion functions.
|
||||
* Ample room for real cert/key, but bounds upper memory use. */
|
||||
#define WC_JNI_MAX_PEM_SIZE (1024 * 1024)
|
||||
|
||||
/* Force-zero a buffer holding sensitive material */
|
||||
#if (LIBWOLFSSL_VERSION_HEX >= 0x05008004) && !defined(WOLFSSL_NO_FORCE_ZERO)
|
||||
#define WC_JNI_FORCE_ZERO(p, len) wc_ForceZero((p), (len))
|
||||
#else
|
||||
#define WC_JNI_FORCE_ZERO(p, len) XMEMSET((p), 0, (len))
|
||||
#endif
|
||||
|
||||
JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfCrypt_getWC_1HASH_1TYPE_1NONE
|
||||
(JNIEnv* env, jclass class)
|
||||
{
|
||||
|
|
@ -365,6 +376,7 @@ JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_WolfCrypt_wcKeyPemToDer
|
|||
byte* pem = NULL;
|
||||
byte* der = NULL;
|
||||
const char* password = NULL;
|
||||
jboolean pwIsCopy = JNI_FALSE;
|
||||
jbyteArray derArr = NULL;
|
||||
(void)jcl;
|
||||
|
||||
|
|
@ -377,20 +389,16 @@ JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_WolfCrypt_wcKeyPemToDer
|
|||
}
|
||||
|
||||
if (ret == 0) {
|
||||
pem = (byte*)(*env)->GetByteArrayElements(env, pemArr, NULL);
|
||||
pemSz = (*env)->GetArrayLength(env, pemArr);
|
||||
if (pem == NULL || pemSz <= 0) {
|
||||
if (pemSz <= 0 || pemSz > WC_JNI_MAX_PEM_SIZE) {
|
||||
ret = BAD_FUNC_ARG;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get password if provided */
|
||||
if (ret == 0) {
|
||||
if (passwordStr != NULL) {
|
||||
password = (*env)->GetStringUTFChars(env, passwordStr, NULL);
|
||||
if (password == NULL) {
|
||||
ret = MEMORY_E;
|
||||
}
|
||||
pem = (byte*)(*env)->GetByteArrayElements(env, pemArr, NULL);
|
||||
if (pem == NULL) {
|
||||
ret = BAD_FUNC_ARG;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -400,10 +408,22 @@ JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_WolfCrypt_wcKeyPemToDer
|
|||
if (der == NULL) {
|
||||
ret = MEMORY_E;
|
||||
}
|
||||
else {
|
||||
XMEMSET(der, 0, pemSz);
|
||||
}
|
||||
}
|
||||
|
||||
/* Get password if provided */
|
||||
if (ret == 0) {
|
||||
if (passwordStr != NULL) {
|
||||
password = (*env)->GetStringUTFChars(env, passwordStr, &pwIsCopy);
|
||||
if (password == NULL) {
|
||||
ret = MEMORY_E;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
XMEMSET(der, 0, pemSz);
|
||||
ret = wc_KeyPemToDer(pem, pemSz, der, pemSz, password);
|
||||
if (ret > 0) {
|
||||
derSz = ret;
|
||||
|
|
@ -431,15 +451,14 @@ JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_WolfCrypt_wcKeyPemToDer
|
|||
(*env)->ReleaseByteArrayElements(env, pemArr, (jbyte*)pem, JNI_ABORT);
|
||||
}
|
||||
if (password != NULL) {
|
||||
/* Only clear when JNI handed back a private copy */
|
||||
if (pwIsCopy == JNI_TRUE) {
|
||||
WC_JNI_FORCE_ZERO((void*)password, XSTRLEN(password));
|
||||
}
|
||||
(*env)->ReleaseStringUTFChars(env, passwordStr, password);
|
||||
}
|
||||
if (der != NULL) {
|
||||
#if (LIBWOLFSSL_VERSION_HEX >= 0x05008004) && \
|
||||
!defined(WOLFSSL_NO_FORCE_ZERO)
|
||||
wc_ForceZero(der, pemSz);
|
||||
#else
|
||||
XMEMSET(der, 0, pemSz);
|
||||
#endif
|
||||
WC_JNI_FORCE_ZERO(der, pemSz);
|
||||
XFREE(der, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
}
|
||||
if (ret != 0) {
|
||||
|
|
@ -479,9 +498,15 @@ JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_WolfCrypt_wcCertPemToDer
|
|||
}
|
||||
|
||||
if (ret == 0) {
|
||||
pem = (byte*)(*env)->GetByteArrayElements(env, pemArr, NULL);
|
||||
pemSz = (*env)->GetArrayLength(env, pemArr);
|
||||
if (pem == NULL || pemSz <= 0) {
|
||||
if (pemSz <= 0 || pemSz > WC_JNI_MAX_PEM_SIZE) {
|
||||
ret = BAD_FUNC_ARG;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
pem = (byte*)(*env)->GetByteArrayElements(env, pemArr, NULL);
|
||||
if (pem == NULL) {
|
||||
ret = BAD_FUNC_ARG;
|
||||
}
|
||||
}
|
||||
|
|
@ -561,9 +586,15 @@ JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_WolfCrypt_wcPubKeyPemToD
|
|||
}
|
||||
|
||||
if (ret == 0) {
|
||||
pem = (byte*)(*env)->GetByteArrayElements(env, pemArr, NULL);
|
||||
pemSz = (*env)->GetArrayLength(env, pemArr);
|
||||
if (pem == NULL || pemSz <= 0) {
|
||||
if (pemSz <= 0 || pemSz > WC_JNI_MAX_PEM_SIZE) {
|
||||
ret = BAD_FUNC_ARG;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
pem = (byte*)(*env)->GetByteArrayElements(env, pemArr, NULL);
|
||||
if (pem == NULL) {
|
||||
ret = BAD_FUNC_ARG;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -793,6 +793,33 @@ public class WolfCryptCipher extends CipherSpi {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an AEAD tag length from bits to bytes.
|
||||
*
|
||||
* Rejects lengths that are not a whole number of bytes, since the byte
|
||||
* count is what reaches native wolfSSL and a non-multiple of 8 would be
|
||||
* silently truncated. Native validates which sizes the mode supports.
|
||||
*
|
||||
* @param modeName mode label used in the error message
|
||||
* @param tagBits requested tag length in bits
|
||||
*
|
||||
* @return tag length in bytes
|
||||
*
|
||||
* @throws InvalidAlgorithmParameterException if tagBits is not a
|
||||
* positive multiple of 8
|
||||
*/
|
||||
private static int tagLenToBytes(String modeName, int tagBits)
|
||||
throws InvalidAlgorithmParameterException {
|
||||
|
||||
if (tagBits <= 0 || (tagBits % 8) != 0) {
|
||||
throw new InvalidAlgorithmParameterException(
|
||||
modeName + " tag length must be a positive multiple of " +
|
||||
"8 bits, got " + tagBits);
|
||||
}
|
||||
|
||||
return (tagBits / 8);
|
||||
}
|
||||
|
||||
private void wolfCryptSetIV(AlgorithmParameterSpec spec,
|
||||
SecureRandom random) throws InvalidAlgorithmParameterException {
|
||||
|
||||
|
|
@ -853,13 +880,7 @@ public class WolfCryptCipher extends CipherSpi {
|
|||
}
|
||||
|
||||
this.iv = gcmSpec.getIV().clone();
|
||||
|
||||
/* store tag length as bytes */
|
||||
if (gcmSpec.getTLen() == 0) {
|
||||
throw new InvalidAlgorithmParameterException(
|
||||
"Tag length cannot be zero");
|
||||
}
|
||||
this.gcmTagLen = (gcmSpec.getTLen() / 8);
|
||||
this.gcmTagLen = tagLenToBytes("AES-GCM", gcmSpec.getTLen());
|
||||
}
|
||||
else if (cipherMode == CipherMode.WC_CCM) {
|
||||
/*
|
||||
|
|
@ -891,13 +912,7 @@ public class WolfCryptCipher extends CipherSpi {
|
|||
}
|
||||
|
||||
this.iv = ccmSpec.getIV().clone();
|
||||
|
||||
/* store tag length as bytes */
|
||||
if (ccmSpec.getTLen() == 0) {
|
||||
throw new InvalidAlgorithmParameterException(
|
||||
"Tag length cannot be zero");
|
||||
}
|
||||
this.gcmTagLen = (ccmSpec.getTLen() / 8);
|
||||
this.gcmTagLen = tagLenToBytes("AES-CCM", ccmSpec.getTLen());
|
||||
}
|
||||
else {
|
||||
if (!(spec instanceof IvParameterSpec)) {
|
||||
|
|
|
|||
|
|
@ -198,6 +198,10 @@ public class WolfCryptDHPrivateKey implements DHPrivateKey, Destroyable {
|
|||
}
|
||||
pLen = WolfCryptASN1Util.getDERLength(derData, idx);
|
||||
idx += WolfCryptASN1Util.getDERLengthSize(derData, idx);
|
||||
if (pLen < 0 || pLen > derData.length - idx) {
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid PKCS#8: p length exceeds buffer");
|
||||
}
|
||||
pBytes = new byte[pLen];
|
||||
System.arraycopy(derData, idx, pBytes, 0, pLen);
|
||||
p = new BigInteger(1, pBytes);
|
||||
|
|
@ -210,6 +214,10 @@ public class WolfCryptDHPrivateKey implements DHPrivateKey, Destroyable {
|
|||
}
|
||||
gLen = WolfCryptASN1Util.getDERLength(derData, idx);
|
||||
idx += WolfCryptASN1Util.getDERLengthSize(derData, idx);
|
||||
if (gLen < 0 || gLen > derData.length - idx) {
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid PKCS#8: g length exceeds buffer");
|
||||
}
|
||||
gBytes = new byte[gLen];
|
||||
System.arraycopy(derData, idx, gBytes, 0, gLen);
|
||||
g = new BigInteger(1, gBytes);
|
||||
|
|
@ -230,6 +238,10 @@ public class WolfCryptDHPrivateKey implements DHPrivateKey, Destroyable {
|
|||
}
|
||||
privLen = WolfCryptASN1Util.getDERLength(derData, idx);
|
||||
idx += WolfCryptASN1Util.getDERLengthSize(derData, idx);
|
||||
if (privLen < 0 || privLen > derData.length - idx) {
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid PKCS#8: private value length exceeds buffer");
|
||||
}
|
||||
privBytes = new byte[privLen];
|
||||
System.arraycopy(derData, idx, privBytes, 0, privLen);
|
||||
privateVal = new BigInteger(1, privBytes);
|
||||
|
|
@ -382,6 +394,10 @@ public class WolfCryptDHPrivateKey implements DHPrivateKey, Destroyable {
|
|||
}
|
||||
pLen = WolfCryptASN1Util.getDERLength(this.encoded, idx);
|
||||
idx += WolfCryptASN1Util.getDERLengthSize(this.encoded, idx);
|
||||
if (pLen < 0 || pLen > this.encoded.length - idx) {
|
||||
throw new IllegalStateException(
|
||||
"Invalid PKCS#8: p length exceeds buffer");
|
||||
}
|
||||
pBytes = new byte[pLen];
|
||||
System.arraycopy(this.encoded, idx, pBytes, 0, pLen);
|
||||
p = new BigInteger(1, pBytes);
|
||||
|
|
@ -394,6 +410,10 @@ public class WolfCryptDHPrivateKey implements DHPrivateKey, Destroyable {
|
|||
}
|
||||
gLen = WolfCryptASN1Util.getDERLength(this.encoded, idx);
|
||||
idx += WolfCryptASN1Util.getDERLengthSize(this.encoded, idx);
|
||||
if (gLen < 0 || gLen > this.encoded.length - idx) {
|
||||
throw new IllegalStateException(
|
||||
"Invalid PKCS#8: g length exceeds buffer");
|
||||
}
|
||||
gBytes = new byte[gLen];
|
||||
System.arraycopy(this.encoded, idx, gBytes, 0, gLen);
|
||||
g = new BigInteger(1, gBytes);
|
||||
|
|
@ -486,6 +506,10 @@ public class WolfCryptDHPrivateKey implements DHPrivateKey, Destroyable {
|
|||
}
|
||||
privLen = WolfCryptASN1Util.getDERLength(this.encoded, idx);
|
||||
idx += WolfCryptASN1Util.getDERLengthSize(this.encoded, idx);
|
||||
if (privLen < 0 || privLen > this.encoded.length - idx) {
|
||||
throw new IllegalStateException(
|
||||
"Invalid PKCS#8: private value length exceeds buffer");
|
||||
}
|
||||
privBytes = new byte[privLen];
|
||||
System.arraycopy(this.encoded, idx, privBytes, 0, privLen);
|
||||
privateVal = new BigInteger(1, privBytes);
|
||||
|
|
|
|||
|
|
@ -189,6 +189,10 @@ public class WolfCryptDHPublicKey implements DHPublicKey, Destroyable {
|
|||
}
|
||||
pLen = WolfCryptASN1Util.getDERLength(derData, idx);
|
||||
idx += WolfCryptASN1Util.getDERLengthSize(derData, idx);
|
||||
if (pLen < 0 || pLen > derData.length - idx) {
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid X.509: p length exceeds buffer");
|
||||
}
|
||||
pBytes = new byte[pLen];
|
||||
System.arraycopy(derData, idx, pBytes, 0, pLen);
|
||||
p = new BigInteger(1, pBytes);
|
||||
|
|
@ -201,6 +205,10 @@ public class WolfCryptDHPublicKey implements DHPublicKey, Destroyable {
|
|||
}
|
||||
gLen = WolfCryptASN1Util.getDERLength(derData, idx);
|
||||
idx += WolfCryptASN1Util.getDERLengthSize(derData, idx);
|
||||
if (gLen < 0 || gLen > derData.length - idx) {
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid X.509: g length exceeds buffer");
|
||||
}
|
||||
gBytes = new byte[gLen];
|
||||
System.arraycopy(derData, idx, gBytes, 0, gLen);
|
||||
g = new BigInteger(1, gBytes);
|
||||
|
|
@ -224,6 +232,10 @@ public class WolfCryptDHPublicKey implements DHPublicKey, Destroyable {
|
|||
}
|
||||
pubLen = WolfCryptASN1Util.getDERLength(derData, idx);
|
||||
idx += WolfCryptASN1Util.getDERLengthSize(derData, idx);
|
||||
if (pubLen < 0 || pubLen > derData.length - idx) {
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid X.509: public value length exceeds buffer");
|
||||
}
|
||||
pubBytes = new byte[pubLen];
|
||||
System.arraycopy(derData, idx, pubBytes, 0, pubLen);
|
||||
publicVal = new BigInteger(1, pubBytes);
|
||||
|
|
@ -374,6 +386,10 @@ public class WolfCryptDHPublicKey implements DHPublicKey, Destroyable {
|
|||
}
|
||||
pLen = WolfCryptASN1Util.getDERLength(this.encoded, idx);
|
||||
idx += WolfCryptASN1Util.getDERLengthSize(this.encoded, idx);
|
||||
if (pLen < 0 || pLen > this.encoded.length - idx) {
|
||||
throw new IllegalStateException(
|
||||
"Invalid X.509: p length exceeds buffer");
|
||||
}
|
||||
pBytes = new byte[pLen];
|
||||
System.arraycopy(this.encoded, idx, pBytes, 0, pLen);
|
||||
p = new BigInteger(1, pBytes);
|
||||
|
|
@ -386,6 +402,10 @@ public class WolfCryptDHPublicKey implements DHPublicKey, Destroyable {
|
|||
}
|
||||
gLen = WolfCryptASN1Util.getDERLength(this.encoded, idx);
|
||||
idx += WolfCryptASN1Util.getDERLengthSize(this.encoded, idx);
|
||||
if (gLen < 0 || gLen > this.encoded.length - idx) {
|
||||
throw new IllegalStateException(
|
||||
"Invalid X.509: g length exceeds buffer");
|
||||
}
|
||||
gBytes = new byte[gLen];
|
||||
System.arraycopy(this.encoded, idx, gBytes, 0, gLen);
|
||||
g = new BigInteger(1, gBytes);
|
||||
|
|
@ -472,6 +492,10 @@ public class WolfCryptDHPublicKey implements DHPublicKey, Destroyable {
|
|||
}
|
||||
pubLen = WolfCryptASN1Util.getDERLength(this.encoded, idx);
|
||||
idx += WolfCryptASN1Util.getDERLengthSize(this.encoded, idx);
|
||||
if (pubLen < 0 || pubLen > this.encoded.length - idx) {
|
||||
throw new IllegalStateException(
|
||||
"Invalid X.509: public value length exceeds buffer");
|
||||
}
|
||||
pubBytes = new byte[pubLen];
|
||||
System.arraycopy(this.encoded, idx, pubBytes, 0, pubLen);
|
||||
publicVal = new BigInteger(1, pubBytes);
|
||||
|
|
|
|||
|
|
@ -476,14 +476,31 @@ public class WolfCryptKeyPairGenerator extends KeyPairGeneratorSpi {
|
|||
}
|
||||
|
||||
DHParameterSpec dhSpec = (DHParameterSpec)params;
|
||||
this.dhP = dhSpec.getP().toByteArray();
|
||||
this.dhG = dhSpec.getG().toByteArray();
|
||||
BigInteger dhSpecP = dhSpec.getP();
|
||||
BigInteger dhSpecG = dhSpec.getG();
|
||||
|
||||
if (dhP == null || dhG == null) {
|
||||
if (dhSpecP == null || dhSpecG == null) {
|
||||
throw new InvalidAlgorithmParameterException(
|
||||
"Invalid parameters, either p or g is null");
|
||||
}
|
||||
|
||||
if (dhSpecP.signum() <= 0 || dhSpecG.signum() <= 0) {
|
||||
throw new InvalidAlgorithmParameterException(
|
||||
"DH parameters p and g must be positive");
|
||||
}
|
||||
|
||||
/* Reject primes the native library would refuse, so the
|
||||
* failure throws here instead of from generateKeyPair() */
|
||||
int dhPrimeBits = dhSpecP.bitLength();
|
||||
if (dhPrimeBits < Dh.DH_MIN_SIZE) {
|
||||
throw new InvalidAlgorithmParameterException(
|
||||
"DH prime size must be at least " + Dh.DH_MIN_SIZE +
|
||||
" bits, got " + dhPrimeBits);
|
||||
}
|
||||
|
||||
this.dhP = dhSpecP.toByteArray();
|
||||
this.dhG = dhSpecG.toByteArray();
|
||||
|
||||
log("init with spec, prime len: " + this.dhP.length);
|
||||
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -280,8 +280,8 @@ public class WolfCryptSignature extends SignatureSpi {
|
|||
"Unsupported signature algorithm digest type");
|
||||
}
|
||||
|
||||
/* Initialize PSS parameters if PSS padding */
|
||||
if (ptype == PaddingType.WC_RSA_PSS) {
|
||||
/* Set default PSS parameters, only if caller has not already */
|
||||
if (ptype == PaddingType.WC_RSA_PSS && this.pssParams == null) {
|
||||
String digestAlg = digestTypeToJavaName(dtype);
|
||||
MGF1ParameterSpec mgf1Spec = getMGF1SpecForDigest(digestAlg);
|
||||
int saltLen = this.digestSz; /* Use actual hash length */
|
||||
|
|
@ -1246,6 +1246,24 @@ public class WolfCryptSignature extends SignatureSpi {
|
|||
"Only MGF1 supported, got " + mgfAlg);
|
||||
}
|
||||
|
||||
/* Validate spec type and MGF1 inner digest */
|
||||
AlgorithmParameterSpec mgfParams = pss.getMGFParameters();
|
||||
if (mgfParams != null) {
|
||||
/* Different spec type silently falls back to the default digest */
|
||||
if (!(mgfParams instanceof MGF1ParameterSpec)) {
|
||||
throw new InvalidAlgorithmParameterException(
|
||||
"MGF1 parameters must be of type MGF1ParameterSpec, got " +
|
||||
mgfParams.getClass().getName());
|
||||
}
|
||||
|
||||
String mgfDigest =
|
||||
((MGF1ParameterSpec)mgfParams).getDigestAlgorithm();
|
||||
if (!isDigestSupported(mgfDigest)) {
|
||||
throw new InvalidAlgorithmParameterException(
|
||||
"MGF1 digest not supported: " + mgfDigest);
|
||||
}
|
||||
}
|
||||
|
||||
/* Validate salt length is reasonable */
|
||||
int saltLen = pss.getSaltLength();
|
||||
if (saltLen < -2) {
|
||||
|
|
|
|||
|
|
@ -109,8 +109,8 @@ public class AesCcm extends NativeStruct {
|
|||
(state != WolfCryptState.RELEASED)) {
|
||||
synchronized (pointerLock) {
|
||||
wc_AesFree();
|
||||
super.releaseNativeStruct();
|
||||
}
|
||||
super.releaseNativeStruct();
|
||||
state = WolfCryptState.RELEASED;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -109,8 +109,8 @@ public class AesGcm extends NativeStruct {
|
|||
(state != WolfCryptState.RELEASED)) {
|
||||
synchronized (pointerLock) {
|
||||
wc_AesFree();
|
||||
super.releaseNativeStruct();
|
||||
}
|
||||
super.releaseNativeStruct();
|
||||
state = WolfCryptState.RELEASED;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,13 @@ public class Dh extends NativeStruct {
|
|||
/** Lock around object state */
|
||||
protected final Object stateLock = new Object();
|
||||
|
||||
/**
|
||||
* Minimum DH prime size in bits accepted by the native wolfSSL build.
|
||||
* Tracks native DH_MIN_SIZE, which is build configurable through
|
||||
* WOLFSSL_MIN_DHKEY_BITS.
|
||||
*/
|
||||
public static final int DH_MIN_SIZE = Dh.dhMinSize();
|
||||
|
||||
/* Named DH group constants (FFDHE from RFC 7919) */
|
||||
/** FFDHE 2048-bit group */
|
||||
public static final int WC_FFDHE_2048 = 256;
|
||||
|
|
@ -93,16 +100,17 @@ public class Dh extends NativeStruct {
|
|||
|
||||
synchronized (pointerLock) {
|
||||
wc_FreeDhKey();
|
||||
super.releaseNativeStruct();
|
||||
}
|
||||
setPrivateKey(new byte[0]);
|
||||
setPublicKey(new byte[0]);
|
||||
|
||||
super.releaseNativeStruct();
|
||||
state = WolfCryptState.RELEASED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static native int dhMinSize();
|
||||
private native long mallocNativeStruct_internal() throws OutOfMemoryError;
|
||||
private native void wc_InitDhKey();
|
||||
private native void wc_FreeDhKey();
|
||||
|
|
|
|||
|
|
@ -90,6 +90,7 @@ public class Ecc extends NativeStruct {
|
|||
|
||||
synchronized (pointerLock) {
|
||||
wc_ecc_free();
|
||||
super.releaseNativeStruct();
|
||||
}
|
||||
|
||||
synchronized (rngLock) {
|
||||
|
|
@ -100,7 +101,6 @@ public class Ecc extends NativeStruct {
|
|||
}
|
||||
}
|
||||
|
||||
super.releaseNativeStruct();
|
||||
state = WolfCryptState.RELEASED;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,8 +63,8 @@ public class Ed25519 extends NativeStruct {
|
|||
|
||||
synchronized (pointerLock) {
|
||||
wc_ed25519_free();
|
||||
super.releaseNativeStruct();
|
||||
}
|
||||
super.releaseNativeStruct();
|
||||
state = WolfCryptState.RELEASED;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -325,6 +325,14 @@ public class FeatureDetect {
|
|||
*/
|
||||
public static native boolean RsaPssEnabled();
|
||||
|
||||
/**
|
||||
* Tests if RSA-PSS salt lengths longer than the digest are compiled into
|
||||
* the native wolfSSL library (WOLFSSL_PSS_LONG_SALT).
|
||||
*
|
||||
* @return true if enabled, otherwise false if not compiled in.
|
||||
*/
|
||||
public static native boolean RsaPssLongSaltEnabled();
|
||||
|
||||
/**
|
||||
* Tests if RSA-OAEP is compiled into the native wolfSSL library.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -95,8 +95,8 @@ public class Lms extends NativeStruct {
|
|||
|
||||
synchronized (pointerLock) {
|
||||
wc_LmsKey_free();
|
||||
super.releaseNativeStruct();
|
||||
}
|
||||
super.releaseNativeStruct();
|
||||
state = WolfCryptState.RELEASED;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -144,8 +144,8 @@ public class MlDsa extends NativeStruct {
|
|||
|
||||
synchronized (pointerLock) {
|
||||
wc_dilithium_free();
|
||||
super.releaseNativeStruct();
|
||||
}
|
||||
super.releaseNativeStruct();
|
||||
state = WolfCryptState.RELEASED;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,8 +124,8 @@ public class MlKem extends NativeStruct {
|
|||
|
||||
synchronized (pointerLock) {
|
||||
wc_mlkem_free();
|
||||
super.releaseNativeStruct();
|
||||
}
|
||||
super.releaseNativeStruct();
|
||||
state = WolfCryptState.RELEASED;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -250,8 +250,8 @@ public class Rsa extends NativeStruct {
|
|||
|
||||
synchronized (pointerLock) {
|
||||
wc_FreeRsaKey();
|
||||
super.releaseNativeStruct();
|
||||
}
|
||||
super.releaseNativeStruct();
|
||||
state = WolfCryptState.RELEASED;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -189,8 +189,8 @@ public class SlhDsa extends NativeStruct {
|
|||
|
||||
synchronized (pointerLock) {
|
||||
wc_SlhDsaKey_free();
|
||||
super.releaseNativeStruct();
|
||||
}
|
||||
super.releaseNativeStruct();
|
||||
state = WolfCryptState.RELEASED;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -344,9 +344,10 @@ public class WolfCrypt extends WolfObject {
|
|||
*
|
||||
* @return DER-encoded private key as byte array
|
||||
*
|
||||
* @throws WolfCryptException if conversion fails, native operation
|
||||
* encounters an error, or native ASN/PEM support is not
|
||||
* compiled in (NO_ASN or WOLFSSL_NO_PEM defined)
|
||||
* @throws WolfCryptException if conversion fails, input is larger than
|
||||
* the 1 MB maximum PEM size, native operation encounters an
|
||||
* error, or native ASN/PEM support is not compiled in
|
||||
* (NO_ASN or WOLFSSL_NO_PEM defined)
|
||||
*/
|
||||
public static byte[] keyPemToDer(byte[] pem, String password)
|
||||
throws WolfCryptException {
|
||||
|
|
@ -367,9 +368,10 @@ public class WolfCrypt extends WolfObject {
|
|||
*
|
||||
* @return DER-encoded certificate as byte array
|
||||
*
|
||||
* @throws WolfCryptException if conversion fails, native operation
|
||||
* encounters an error, or native ASN/PEM support is not
|
||||
* compiled in (NO_ASN or WOLFSSL_NO_PEM defined)
|
||||
* @throws WolfCryptException if conversion fails, input is larger than
|
||||
* the 1 MB maximum PEM size, native operation encounters an
|
||||
* error, or native ASN/PEM support is not compiled in
|
||||
* (NO_ASN or WOLFSSL_NO_PEM defined)
|
||||
*/
|
||||
public static byte[] certPemToDer(byte[] pem) throws WolfCryptException {
|
||||
|
||||
|
|
@ -389,9 +391,10 @@ public class WolfCrypt extends WolfObject {
|
|||
*
|
||||
* @return DER-encoded public key as byte array
|
||||
*
|
||||
* @throws WolfCryptException if conversion fails, native operation
|
||||
* encounters an error, or native ASN/PEM support is not
|
||||
* compiled in (NO_ASN or WOLFSSL_NO_PEM defined)
|
||||
* @throws WolfCryptException if conversion fails, input is larger than
|
||||
* the 1 MB maximum PEM size, native operation encounters an
|
||||
* error, or native ASN/PEM support is not compiled in
|
||||
* (NO_ASN or WOLFSSL_NO_PEM defined)
|
||||
*/
|
||||
public static byte[] pubKeyPemToDer(byte[] pem) throws WolfCryptException {
|
||||
|
||||
|
|
|
|||
|
|
@ -82,8 +82,8 @@ public class Xmss extends NativeStruct {
|
|||
|
||||
synchronized (pointerLock) {
|
||||
wc_XmssKey_free();
|
||||
super.releaseNativeStruct();
|
||||
}
|
||||
super.releaseNativeStruct();
|
||||
state = WolfCryptState.RELEASED;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,6 +84,7 @@ import com.wolfssl.wolfcrypt.FeatureDetect;
|
|||
import com.wolfssl.wolfcrypt.Aes;
|
||||
import com.wolfssl.wolfcrypt.Fips;
|
||||
import com.wolfssl.provider.jce.WolfCryptProvider;
|
||||
import java.security.GeneralSecurityException;
|
||||
import com.wolfssl.wolfcrypt.WolfCryptException;
|
||||
import com.wolfssl.wolfcrypt.test.TimedTestWatcher;
|
||||
|
||||
|
|
@ -5772,6 +5773,127 @@ public class WolfCryptCipherTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAesGcmTagLengthValidation() throws Exception {
|
||||
|
||||
if (!enabledJCEAlgos.contains("AES/GCM/NoPadding")) {
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] key = new byte[16];
|
||||
byte[] iv = new byte[12];
|
||||
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
|
||||
|
||||
int[] truncatingTagBits = { 4, 100, 129 };
|
||||
for (int tagBits : truncatingTagBits) {
|
||||
Cipher cipher =
|
||||
Cipher.getInstance("AES/GCM/NoPadding", jceProvider);
|
||||
try {
|
||||
cipher.init(Cipher.ENCRYPT_MODE, keySpec,
|
||||
new GCMParameterSpec(tagBits, iv));
|
||||
fail("Should reject GCM tag length " + tagBits + " bits");
|
||||
} catch (InvalidAlgorithmParameterException e) {
|
||||
assertTrue("Error should mention tag length",
|
||||
e.getMessage().contains("tag length"));
|
||||
}
|
||||
}
|
||||
|
||||
/* Whole byte counts, so rejected below the JCE layer rather than at
|
||||
* init. Only sizes over the AES block size are rejected on every
|
||||
* build, minimum tag size enforcement varies by configuration. */
|
||||
int[] unsupportedTagBits = { 256 };
|
||||
for (int tagBits : unsupportedTagBits) {
|
||||
Cipher cipher =
|
||||
Cipher.getInstance("AES/GCM/NoPadding", jceProvider);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, keySpec,
|
||||
new GCMParameterSpec(tagBits, iv));
|
||||
try {
|
||||
cipher.doFinal("test".getBytes());
|
||||
fail("Should reject GCM tag length " + tagBits + " bits");
|
||||
} catch (WolfCryptException | GeneralSecurityException e) {
|
||||
/* expected */
|
||||
}
|
||||
}
|
||||
|
||||
byte[] plaintext = "GCM tag length test".getBytes();
|
||||
int[] goodTagBits = { 96, 104, 112, 120, 128 };
|
||||
for (int tagBits : goodTagBits) {
|
||||
secureRandom.nextBytes(iv);
|
||||
Cipher enc =
|
||||
Cipher.getInstance("AES/GCM/NoPadding", jceProvider);
|
||||
enc.init(Cipher.ENCRYPT_MODE, keySpec,
|
||||
new GCMParameterSpec(tagBits, iv));
|
||||
byte[] ct = enc.doFinal(plaintext);
|
||||
|
||||
Cipher dec =
|
||||
Cipher.getInstance("AES/GCM/NoPadding", jceProvider);
|
||||
dec.init(Cipher.DECRYPT_MODE, keySpec,
|
||||
new GCMParameterSpec(tagBits, iv));
|
||||
assertArrayEquals(plaintext, dec.doFinal(ct));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAesCcmTagLengthValidation() throws Exception {
|
||||
|
||||
if (!enabledJCEAlgos.contains("AES/CCM/NoPadding")) {
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] key = new byte[16];
|
||||
byte[] nonce = new byte[12];
|
||||
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
|
||||
|
||||
int[] truncatingTagBits = { 4, 100, 129 };
|
||||
for (int tagBits : truncatingTagBits) {
|
||||
Cipher cipher =
|
||||
Cipher.getInstance("AES/CCM/NoPadding", jceProvider);
|
||||
try {
|
||||
cipher.init(Cipher.ENCRYPT_MODE, keySpec,
|
||||
new GCMParameterSpec(tagBits, nonce));
|
||||
fail("Should reject CCM tag length " + tagBits + " bits");
|
||||
} catch (InvalidAlgorithmParameterException e) {
|
||||
assertTrue("Error should mention tag length",
|
||||
e.getMessage().contains("tag length"));
|
||||
}
|
||||
}
|
||||
|
||||
/* Whole byte counts, so rejected below the JCE layer rather than at
|
||||
* init. Only sizes over the AES block size are rejected on every
|
||||
* build, the RFC 3610 set is not enforced in all FIPS bundles. */
|
||||
int[] unsupportedTagBits = { 256 };
|
||||
for (int tagBits : unsupportedTagBits) {
|
||||
Cipher cipher =
|
||||
Cipher.getInstance("AES/CCM/NoPadding", jceProvider);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, keySpec,
|
||||
new GCMParameterSpec(tagBits, nonce));
|
||||
try {
|
||||
cipher.doFinal("test".getBytes());
|
||||
fail("Should reject CCM tag length " + tagBits + " bits");
|
||||
} catch (WolfCryptException | GeneralSecurityException e) {
|
||||
/* expected */
|
||||
}
|
||||
}
|
||||
|
||||
/* 32-bit tags are valid for CCM, unlike GCM */
|
||||
byte[] plaintext = "CCM tag length test".getBytes();
|
||||
int[] goodTagBits = { 32, 64, 96, 128 };
|
||||
for (int tagBits : goodTagBits) {
|
||||
secureRandom.nextBytes(nonce);
|
||||
Cipher enc =
|
||||
Cipher.getInstance("AES/CCM/NoPadding", jceProvider);
|
||||
enc.init(Cipher.ENCRYPT_MODE, keySpec,
|
||||
new GCMParameterSpec(tagBits, nonce));
|
||||
byte[] ct = enc.doFinal(plaintext);
|
||||
|
||||
Cipher dec =
|
||||
Cipher.getInstance("AES/CCM/NoPadding", jceProvider);
|
||||
dec.init(Cipher.DECRYPT_MODE, keySpec,
|
||||
new GCMParameterSpec(tagBits, nonce));
|
||||
assertArrayEquals(plaintext, dec.doFinal(ct));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test IV consistency across encryption/decryption cycles.
|
||||
* This test prevents regression of the issue where getIV() would return
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ import org.junit.BeforeClass;
|
|||
import org.junit.Test;
|
||||
|
||||
import com.wolfssl.provider.jce.WolfCryptProvider;
|
||||
import java.lang.reflect.Field;
|
||||
import com.wolfssl.provider.jce.WolfCryptDHPublicKey;
|
||||
import com.wolfssl.wolfcrypt.FeatureDetect;
|
||||
import com.wolfssl.wolfcrypt.test.TimedTestWatcher;
|
||||
|
|
@ -490,6 +491,81 @@ public class WolfCryptDHKeyFactoryTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPKCS8RejectsOversizedDERFieldLength() throws Exception {
|
||||
|
||||
Assume.assumeTrue(FeatureDetect.DhEnabled());
|
||||
|
||||
KeyFactory kf = KeyFactory.getInstance("DH", "wolfJCE");
|
||||
|
||||
/* PKCS#8 that parses up to the p INTEGER, whose length is a 4-byte
|
||||
* long-form 0x7FFFFFFF. Guard must reject it as a clean
|
||||
* InvalidKeySpecException instead. */
|
||||
byte[] malicious = new byte[] {
|
||||
(byte)0x30, (byte)0x30,
|
||||
(byte)0x02, (byte)0x01, (byte)0x00,
|
||||
(byte)0x30, (byte)0x03,
|
||||
(byte)0x06, (byte)0x01, (byte)0x2A,
|
||||
(byte)0x30, (byte)0x09,
|
||||
(byte)0x02,
|
||||
(byte)0x84, (byte)0x7F, (byte)0xFF, (byte)0xFF, (byte)0xFF
|
||||
};
|
||||
|
||||
try {
|
||||
kf.generatePrivate(new PKCS8EncodedKeySpec(malicious));
|
||||
fail("Should reject PKCS#8 with oversized DER field length");
|
||||
|
||||
} catch (InvalidKeySpecException e) {
|
||||
/* Expected */
|
||||
}
|
||||
|
||||
/* Positive test: a well-formed key still parses */
|
||||
if (enabledKeySizes.contains(2048)) {
|
||||
KeyPairGenerator kpg =
|
||||
KeyPairGenerator.getInstance("DH", "wolfJCE");
|
||||
kpg.initialize(2048);
|
||||
byte[] valid = kpg.generateKeyPair().getPrivate().getEncoded();
|
||||
assertNotNull(kf.generatePrivate(new PKCS8EncodedKeySpec(valid)));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testX509RejectsOversizedDERFieldLength() throws Exception {
|
||||
|
||||
Assume.assumeTrue(FeatureDetect.DhEnabled());
|
||||
|
||||
KeyFactory kf = KeyFactory.getInstance("DH", "wolfJCE");
|
||||
|
||||
/* X.509 SPKI that parses up to the p INTEGER, whose length is a
|
||||
* 4-byte long-form 0x7FFFFFFF. Guard must reject it as a clean
|
||||
* InvalidKeySpecException instead. */
|
||||
byte[] malicious = new byte[] {
|
||||
(byte)0x30, (byte)0x30,
|
||||
(byte)0x30, (byte)0x03,
|
||||
(byte)0x06, (byte)0x01, (byte)0x2A,
|
||||
(byte)0x30, (byte)0x09,
|
||||
(byte)0x02,
|
||||
(byte)0x84, (byte)0x7F, (byte)0xFF, (byte)0xFF, (byte)0xFF
|
||||
};
|
||||
|
||||
try {
|
||||
kf.generatePublic(new X509EncodedKeySpec(malicious));
|
||||
fail("Should reject X.509 with oversized DER field length");
|
||||
|
||||
} catch (InvalidKeySpecException e) {
|
||||
/* Expected */
|
||||
}
|
||||
|
||||
/* Positive test: a well-formed key still parses */
|
||||
if (enabledKeySizes.contains(2048)) {
|
||||
KeyPairGenerator kpg =
|
||||
KeyPairGenerator.getInstance("DH", "wolfJCE");
|
||||
kpg.initialize(2048);
|
||||
byte[] valid = kpg.generateKeyPair().getPublic().getEncoded();
|
||||
assertNotNull(kf.generatePublic(new X509EncodedKeySpec(valid)));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDHPublicKeySpecRangeValidation() throws Exception {
|
||||
|
||||
|
|
@ -994,5 +1070,95 @@ public class WolfCryptDHKeyFactoryTest {
|
|||
assertEquals("Public key p should match",
|
||||
originalPub.getParams().getP(), roundTripPubDH.getParams().getP());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the cached DER of a key and clear its lazily-extracted fields,
|
||||
* so the next getter re-parses the supplied bytes. Mirrors the state a
|
||||
* key is left in after deserialization, where transient caches are null
|
||||
* but the encoded form is populated.
|
||||
*/
|
||||
private static void resetEncoded(Object key, byte[] der,
|
||||
String... cacheFields) throws Exception {
|
||||
|
||||
Field enc = key.getClass().getDeclaredField("encoded");
|
||||
enc.setAccessible(true);
|
||||
enc.set(key, der);
|
||||
|
||||
for (String name : cacheFields) {
|
||||
Field f = key.getClass().getDeclaredField(name);
|
||||
f.setAccessible(true);
|
||||
f.set(key, null);
|
||||
}
|
||||
}
|
||||
|
||||
/* X.509 SPKI whose p INTEGER carries a 4-byte long-form 0x7FFFFFFF
|
||||
* length, used to drive the lazy extraction guards */
|
||||
private static final byte[] oversizedSpki = new byte[] {
|
||||
(byte)0x30, (byte)0x30,
|
||||
(byte)0x30, (byte)0x03,
|
||||
(byte)0x06, (byte)0x01, (byte)0x2A,
|
||||
(byte)0x30, (byte)0x09,
|
||||
(byte)0x02,
|
||||
(byte)0x84, (byte)0x7F, (byte)0xFF, (byte)0xFF, (byte)0xFF
|
||||
};
|
||||
|
||||
@Test
|
||||
public void testPublicKeyLazyExtractRejectsOversizedLength()
|
||||
throws Exception {
|
||||
|
||||
Assume.assumeTrue(FeatureDetect.DhEnabled());
|
||||
Assume.assumeTrue(enabledKeySizes.contains(2048));
|
||||
|
||||
KeyPairGenerator kpg = KeyPairGenerator.getInstance("DH", "wolfJCE");
|
||||
kpg.initialize(2048);
|
||||
DHPublicKey pub = (DHPublicKey)kpg.generateKeyPair().getPublic();
|
||||
|
||||
resetEncoded(pub, oversizedSpki, "paramSpec", "publicValue");
|
||||
|
||||
try {
|
||||
pub.getParams();
|
||||
fail("getParams() should reject an oversized DER length");
|
||||
} catch (IllegalStateException e) {
|
||||
/* expected */
|
||||
}
|
||||
|
||||
try {
|
||||
pub.getY();
|
||||
fail("getY() should reject an oversized DER length");
|
||||
} catch (IllegalStateException e) {
|
||||
/* expected */
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrivateKeyLazyExtractRejectsOversizedLength()
|
||||
throws Exception {
|
||||
|
||||
Assume.assumeTrue(FeatureDetect.DhEnabled());
|
||||
Assume.assumeTrue(enabledKeySizes.contains(2048));
|
||||
|
||||
KeyPairGenerator kpg = KeyPairGenerator.getInstance("DH", "wolfJCE");
|
||||
kpg.initialize(2048);
|
||||
DHPrivateKey priv = (DHPrivateKey)kpg.generateKeyPair().getPrivate();
|
||||
|
||||
/* PKCS#8 variant, adds the version INTEGER ahead of the params */
|
||||
byte[] oversizedPkcs8 = new byte[] {
|
||||
(byte)0x30, (byte)0x30,
|
||||
(byte)0x02, (byte)0x01, (byte)0x00,
|
||||
(byte)0x30, (byte)0x03,
|
||||
(byte)0x06, (byte)0x01, (byte)0x2A,
|
||||
(byte)0x30, (byte)0x09,
|
||||
(byte)0x02,
|
||||
(byte)0x84, (byte)0x7F, (byte)0xFF, (byte)0xFF, (byte)0xFF
|
||||
};
|
||||
|
||||
resetEncoded(priv, oversizedPkcs8, "paramSpec", "privateValue");
|
||||
|
||||
try {
|
||||
priv.getParams();
|
||||
fail("getParams() should reject an oversized DER length");
|
||||
} catch (IllegalStateException e) {
|
||||
/* expected */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ import java.security.interfaces.ECPrivateKey;
|
|||
import java.security.spec.ECParameterSpec;
|
||||
|
||||
import com.wolfssl.wolfcrypt.Rsa;
|
||||
import com.wolfssl.wolfcrypt.Dh;
|
||||
import com.wolfssl.wolfcrypt.Ecc;
|
||||
import com.wolfssl.wolfcrypt.Fips;
|
||||
import com.wolfssl.wolfcrypt.FeatureDetect;
|
||||
|
|
@ -598,7 +599,7 @@ public class WolfCryptKeyPairGeneratorTest {
|
|||
KeyPairGenerator.getInstance("DH", "wolfJCE");
|
||||
|
||||
DHParameterSpec spec = new DHParameterSpec(
|
||||
new BigInteger(prime),
|
||||
new BigInteger(1, prime),
|
||||
new BigInteger(base),
|
||||
testDHKeySizes[i]);
|
||||
|
||||
|
|
@ -609,6 +610,103 @@ public class WolfCryptKeyPairGeneratorTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeyPairGeneratorDhRejectsSmallPrimeSpec()
|
||||
throws NoSuchProviderException, NoSuchAlgorithmException,
|
||||
InvalidAlgorithmParameterException {
|
||||
|
||||
/* skip test if DH is not compiled in native wolfSSL */
|
||||
if (!FeatureDetect.DhEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
KeyPairGenerator kpg = KeyPairGenerator.getInstance("DH", "wolfJCE");
|
||||
|
||||
/* 255-bit prime (Curve25519 field prime), below the 512-bit min,
|
||||
* must be rejected before key generation */
|
||||
DHParameterSpec weakSpec = new DHParameterSpec(
|
||||
new BigInteger("7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" +
|
||||
"FFFFFFFFFFFFFFFFFFED", 16),
|
||||
new BigInteger(base));
|
||||
|
||||
try {
|
||||
kpg.initialize(weakSpec);
|
||||
fail("initialize(DHParameterSpec) should reject a prime " +
|
||||
"below the native minimum");
|
||||
} catch (InvalidAlgorithmParameterException e) {
|
||||
/* expected */
|
||||
}
|
||||
|
||||
/* Positive check: 2048-bit prime spec should be accepted */
|
||||
DHParameterSpec goodSpec = new DHParameterSpec(
|
||||
new BigInteger(1, prime), new BigInteger(base));
|
||||
kpg.initialize(goodSpec);
|
||||
assertNotNull(kpg.generateKeyPair());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeyPairGeneratorDhRejectsNonPositiveParams()
|
||||
throws NoSuchProviderException, NoSuchAlgorithmException,
|
||||
InvalidAlgorithmParameterException {
|
||||
|
||||
/* skip test if DH is not compiled in native wolfSSL */
|
||||
if (!FeatureDetect.DhEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
BigInteger goodP = new BigInteger(1, prime);
|
||||
BigInteger goodG = new BigInteger(base);
|
||||
|
||||
/* A negative p can still clear the minimum bit length check */
|
||||
BigInteger[][] bad = new BigInteger[][] {
|
||||
{ goodP.negate(), goodG },
|
||||
{ goodP, goodG.negate() },
|
||||
{ BigInteger.ZERO, goodG },
|
||||
{ goodP, BigInteger.ZERO }
|
||||
};
|
||||
|
||||
for (BigInteger[] pg : bad) {
|
||||
KeyPairGenerator kpg =
|
||||
KeyPairGenerator.getInstance("DH", "wolfJCE");
|
||||
try {
|
||||
kpg.initialize(new DHParameterSpec(pg[0], pg[1]));
|
||||
fail("initialize() should reject non-positive DH params");
|
||||
} catch (InvalidAlgorithmParameterException e) {
|
||||
/* expected */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeyPairGeneratorDhRejectedSpecLeavesStateIntact()
|
||||
throws NoSuchProviderException, NoSuchAlgorithmException,
|
||||
InvalidAlgorithmParameterException {
|
||||
|
||||
/* skip test if DH is not compiled in native wolfSSL */
|
||||
if (!FeatureDetect.DhEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
KeyPairGenerator kpg = KeyPairGenerator.getInstance("DH", "wolfJCE");
|
||||
kpg.initialize(2048);
|
||||
|
||||
DHParameterSpec weakSpec = new DHParameterSpec(
|
||||
BigInteger.ONE.shiftLeft(255).setBit(0), new BigInteger(base));
|
||||
|
||||
try {
|
||||
kpg.initialize(weakSpec);
|
||||
fail("initialize(DHParameterSpec) should reject a prime " +
|
||||
"below the native minimum");
|
||||
} catch (InvalidAlgorithmParameterException e) {
|
||||
/* expected */
|
||||
}
|
||||
|
||||
/* The rejected spec must not have replaced the 2048-bit params */
|
||||
DHPrivateKey priv = (DHPrivateKey)kpg.generateKeyPair().getPrivate();
|
||||
assertTrue("Rejected spec must not weaken generator state",
|
||||
priv.getParams().getP().bitLength() >= Dh.DH_MIN_SIZE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeyPairGeneratorDhInitWithKeySize()
|
||||
throws NoSuchProviderException, NoSuchAlgorithmException,
|
||||
|
|
@ -684,7 +782,7 @@ public class WolfCryptKeyPairGeneratorTest {
|
|||
KeyPairGenerator.getInstance("DH", "wolfJCE");
|
||||
|
||||
DHParameterSpec spec = new DHParameterSpec(
|
||||
new BigInteger(prime), new BigInteger(base), 512);
|
||||
new BigInteger(1, prime), new BigInteger(base), 512);
|
||||
|
||||
kpg.initialize(spec);
|
||||
kpg.initialize(spec);
|
||||
|
|
@ -704,7 +802,7 @@ public class WolfCryptKeyPairGeneratorTest {
|
|||
KeyPairGenerator.getInstance("DH", "wolfJCE");
|
||||
|
||||
DHParameterSpec spec = new DHParameterSpec(
|
||||
new BigInteger(prime), new BigInteger(base), 512);
|
||||
new BigInteger(1, prime), new BigInteger(base), 512);
|
||||
|
||||
kpg.initialize(spec);
|
||||
|
||||
|
|
|
|||
|
|
@ -1737,9 +1737,7 @@ public class WolfCryptSignatureTest {
|
|||
|
||||
@Test
|
||||
public void testRsaPssEdgeCases()
|
||||
throws NoSuchProviderException, NoSuchAlgorithmException,
|
||||
SignatureException, InvalidKeyException,
|
||||
InvalidAlgorithmParameterException {
|
||||
throws Exception {
|
||||
|
||||
if (!enabledAlgos.contains("RSASSA-PSS") ||
|
||||
!com.wolfssl.wolfcrypt.FeatureDetect.RsaPssEnabled()) {
|
||||
|
|
@ -1819,9 +1817,13 @@ public class WolfCryptSignatureTest {
|
|||
}
|
||||
|
||||
private void testRsaPssMaxSaltLengths()
|
||||
throws NoSuchProviderException, NoSuchAlgorithmException,
|
||||
SignatureException, InvalidKeyException,
|
||||
InvalidAlgorithmParameterException {
|
||||
throws Exception {
|
||||
|
||||
if (!FeatureDetect.RsaPssLongSaltEnabled()) {
|
||||
System.out.println("\tSkipping max salt lengths, " +
|
||||
"WOLFSSL_PSS_LONG_SALT not compiled in");
|
||||
return;
|
||||
}
|
||||
|
||||
String message = "Testing maximum salt lengths";
|
||||
byte[] messageBytes = message.getBytes();
|
||||
|
|
@ -2093,10 +2095,10 @@ public class WolfCryptSignatureTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testRsaPssMultipleUpdates()
|
||||
public void testRsaPssPreservesCallerParameters()
|
||||
throws NoSuchProviderException, NoSuchAlgorithmException,
|
||||
SignatureException, InvalidKeyException,
|
||||
InvalidAlgorithmParameterException {
|
||||
InvalidAlgorithmParameterException, Exception {
|
||||
|
||||
if (!enabledAlgos.contains("RSASSA-PSS") ||
|
||||
!com.wolfssl.wolfcrypt.FeatureDetect.RsaPssEnabled()) {
|
||||
|
|
@ -2104,6 +2106,122 @@ public class WolfCryptSignatureTest {
|
|||
return;
|
||||
}
|
||||
|
||||
byte[] data = "PSS parameter test".getBytes();
|
||||
|
||||
/* RFC 4055 style parameters, MGF1 inner digest and salt length
|
||||
* both differ from the message digest */
|
||||
PSSParameterSpec spec = new PSSParameterSpec(
|
||||
"SHA-256", "MGF1", MGF1ParameterSpec.SHA1, 20, 1);
|
||||
|
||||
Signature signer = Signature.getInstance("RSASSA-PSS", "wolfJCE");
|
||||
signer.setParameter(spec);
|
||||
|
||||
/* Parameters must survive the internal digest initialization */
|
||||
AlgorithmParameters algParams = signer.getParameters();
|
||||
assertNotNull(algParams);
|
||||
PSSParameterSpec effective =
|
||||
algParams.getParameterSpec(PSSParameterSpec.class);
|
||||
|
||||
assertEquals("Message digest should be preserved",
|
||||
"SHA-256", effective.getDigestAlgorithm());
|
||||
assertEquals("Salt length should be preserved",
|
||||
20, effective.getSaltLength());
|
||||
assertTrue("MGF parameters should be MGF1ParameterSpec",
|
||||
effective.getMGFParameters() instanceof MGF1ParameterSpec);
|
||||
assertEquals("MGF1 inner digest should be preserved", "SHA-1",
|
||||
((MGF1ParameterSpec)effective.getMGFParameters())
|
||||
.getDigestAlgorithm());
|
||||
|
||||
signer.initSign(rsaPair.getPrivate());
|
||||
signer.update(data);
|
||||
byte[] signature = signer.sign();
|
||||
|
||||
Signature verifier = Signature.getInstance("RSASSA-PSS", "wolfJCE");
|
||||
verifier.setParameter(spec);
|
||||
verifier.initVerify(rsaPair.getPublic());
|
||||
verifier.update(data);
|
||||
assertTrue("wolfJCE should verify its own PSS signature",
|
||||
verifier.verify(signature));
|
||||
|
||||
/* Signature must also verify under another provider using the same
|
||||
* parameters, proving the requested values were actually used */
|
||||
try {
|
||||
Signature interop =
|
||||
Signature.getInstance("RSASSA-PSS", "SunRsaSign");
|
||||
interop.setParameter(spec);
|
||||
interop.initVerify(rsaPair.getPublic());
|
||||
interop.update(data);
|
||||
assertTrue("SunRsaSign should verify with requested params",
|
||||
interop.verify(signature));
|
||||
|
||||
} catch (NoSuchProviderException | NoSuchAlgorithmException e) {
|
||||
/* SunRsaSign RSASSA-PSS not available, skip interop check */
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRsaPssRejectsNonMgf1ParameterSpec()
|
||||
throws NoSuchProviderException, NoSuchAlgorithmException,
|
||||
SignatureException, InvalidKeyException {
|
||||
|
||||
if (!enabledAlgos.contains("RSASSA-PSS") ||
|
||||
!com.wolfssl.wolfcrypt.FeatureDetect.RsaPssEnabled()) {
|
||||
/* Skip if RSA-PSS not enabled at JCE or native level */
|
||||
return;
|
||||
}
|
||||
|
||||
Signature signer = Signature.getInstance("RSASSA-PSS", "wolfJCE");
|
||||
|
||||
try {
|
||||
signer.setParameter(new PSSParameterSpec("SHA-256", "MGF1",
|
||||
new ECGenParameterSpec("secp256r1"), 32, 1));
|
||||
fail("Should reject MGF parameters that are not MGF1ParameterSpec");
|
||||
|
||||
} catch (InvalidAlgorithmParameterException e) {
|
||||
/* expected */
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRsaPssRejectsUnsupportedMgf1Digest()
|
||||
throws NoSuchProviderException, NoSuchAlgorithmException,
|
||||
SignatureException, InvalidKeyException {
|
||||
|
||||
if (!enabledAlgos.contains("RSASSA-PSS") ||
|
||||
!com.wolfssl.wolfcrypt.FeatureDetect.RsaPssEnabled()) {
|
||||
/* Skip if RSA-PSS not enabled at JCE or native level */
|
||||
return;
|
||||
}
|
||||
|
||||
Signature signer = Signature.getInstance("RSASSA-PSS", "wolfJCE");
|
||||
|
||||
try {
|
||||
signer.setParameter(new PSSParameterSpec(
|
||||
"SHA-256", "MGF1", new MGF1ParameterSpec("MD5"), 32, 1));
|
||||
fail("Should reject unsupported MGF1 inner digest");
|
||||
|
||||
} catch (InvalidAlgorithmParameterException e) {
|
||||
/* expected */
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRsaPssMultipleUpdates()
|
||||
throws Exception {
|
||||
|
||||
if (!enabledAlgos.contains("RSASSA-PSS") ||
|
||||
!com.wolfssl.wolfcrypt.FeatureDetect.RsaPssEnabled()) {
|
||||
/* Skip if RSA-PSS not enabled at JCE or native level */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Uses the maximum salt length for each digest */
|
||||
if (!FeatureDetect.RsaPssLongSaltEnabled()) {
|
||||
System.out.println(
|
||||
"\tSkipping, WOLFSSL_PSS_LONG_SALT not compiled in");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Create test data */
|
||||
byte[] data = new byte[100];
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
|
|
|
|||
|
|
@ -32,13 +32,16 @@ import java.io.File;
|
|||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import com.wolfssl.wolfcrypt.WolfCrypt;
|
||||
import com.wolfssl.wolfcrypt.WolfCryptError;
|
||||
import com.wolfssl.wolfcrypt.WolfCryptException;
|
||||
|
||||
/**
|
||||
|
|
@ -539,6 +542,83 @@ public class WolfCryptTest {
|
|||
WolfCrypt.certPemToDer(invalidPem);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pad a PEM buffer out to the requested total size with newlines. The
|
||||
* PEM block stays parseable, so only the input size limit can reject it.
|
||||
*/
|
||||
private static byte[] padPem(byte[] pem, int totalSz) {
|
||||
|
||||
if (totalSz < pem.length) {
|
||||
throw new IllegalArgumentException(
|
||||
"totalSz must be >= PEM length");
|
||||
}
|
||||
|
||||
byte[] padded = new byte[totalSz];
|
||||
|
||||
Arrays.fill(padded, (byte)'\n');
|
||||
System.arraycopy(pem, 0, padded, 0, pem.length);
|
||||
|
||||
return padded;
|
||||
}
|
||||
|
||||
/* Input over the native size limit should be rejected before any large
|
||||
* native buffer is allocated, even though PEM block itself is valid. */
|
||||
@Test
|
||||
public void testCertPemToDerOversizedInput() throws Exception {
|
||||
|
||||
if (!fileExists(clientCertPem)) {
|
||||
System.out.println("Skipping: test file not found");
|
||||
return;
|
||||
}
|
||||
|
||||
/* One byte past the inclusive limit is the first rejected size */
|
||||
int[] tooBig = { 1024 * 1024 + 1, 2 * 1024 * 1024 };
|
||||
for (int size : tooBig) {
|
||||
try {
|
||||
WolfCrypt.certPemToDer(
|
||||
padPem(readFile(clientCertPem), size));
|
||||
fail("Should reject PEM input of " + size + " bytes");
|
||||
|
||||
} catch (WolfCryptException e) {
|
||||
/* expected */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeyPemToDerOversizedInput() throws Exception {
|
||||
|
||||
if (!fileExists(clientKeyPem)) {
|
||||
System.out.println("Skipping: test file not found");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
WolfCrypt.keyPemToDer(
|
||||
padPem(readFile(clientKeyPem), 2 * 1024 * 1024), null);
|
||||
fail("Should reject PEM input larger than the size limit");
|
||||
|
||||
} catch (WolfCryptException e) {
|
||||
/* expected */
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCertPemToDerAtSizeLimitStillWorks() throws Exception {
|
||||
|
||||
if (!fileExists(clientCertPem) || !fileExists(clientCertDer)) {
|
||||
System.out.println("Skipping: test files not found");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Exactly at the limit, which is inclusive, must still convert */
|
||||
byte[] der = WolfCrypt.certPemToDer(
|
||||
padPem(readFile(clientCertPem), 1024 * 1024));
|
||||
|
||||
assertArrayEquals("DER output should match expected",
|
||||
readFile(clientCertDer), der);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCertPemToDerOutputSmallerThanInput() throws Exception {
|
||||
|
||||
|
|
@ -576,6 +656,91 @@ public class WolfCryptTest {
|
|||
(der[0] & 0xFF) == 0x30);
|
||||
}
|
||||
|
||||
/* AES-256 encrypted PKCS#8 RSA-2048 private key, password below.
|
||||
* This is a test key only, so safe to ship here with password. */
|
||||
private static final String encKeyPassword = "wolfsslpassword";
|
||||
private static final String encKeyPem =
|
||||
"-----BEGIN ENCRYPTED PRIVATE KEY-----\n" +
|
||||
"MIIFNTBfBgkqhkiG9w0BBQ0wUjAxBgkqhkiG9w0BBQwwJAQQHgyFAxVE0s7p+tCz\n" +
|
||||
"Ay/aHQICCAAwDAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEECOvnAgfjJoG6IIy\n" +
|
||||
"nLVImwoEggTQBSNWX1HfO8gbANTzqFTQIj3A1VFMYgx7ddVXfr++W79iR6R+KiSP\n" +
|
||||
"eYrWfOveMXbnjdzgQRUuN0dytG61ygulXaYYsDH4OAJW3iMne7zGoLXQ4yCM4xhU\n" +
|
||||
"+bvju6I7Q6vpg1/gRqUEDeOA78PjBbvGKA7Als1xdr2/IzP2U389svDacKZV3pC6\n" +
|
||||
"6af4+6HrRIJxxfVYzXHk4J1E0bQNv9qYm+T026aG5W9ucOFbXL/ZQK5s3WygpiKJ\n" +
|
||||
"ERRkam+KX4kVFkj58+3Z6LO9N8FVohQuoLODxzaMsOqWEUPoIaNZTYZHB6rFOidN\n" +
|
||||
"ncGg2TOYo5I6E++aaol+JU+UqJ56hSAow2Wot3OM9Lq+XPkZJxsHLywM0yjv2ayg\n" +
|
||||
"BKwsiEDhDfSIvbXVo7d4LaGfIJRa02/I9KyWBW9u73rfadehqcF2DQcEcY/Vo9fu\n" +
|
||||
"iZ0gqFiKRJtlODaSgsqXiZZAEhJrB/TkXsINWCCKrQXEc2ZrUlTYlNiF8+9u/jwG\n" +
|
||||
"HokqdkFbJ+RxX4hyLbWXlfxOm90B98mQWz6l3SSgTxfX3Tty45oWpAZUzB7xD796\n" +
|
||||
"Ginjjlan9kHiNC0zNcmD4K17tsk7zowYNb07hn11GYLx33qX3D64kdfzaKGbmaDx\n" +
|
||||
"qxBKysNM7LP9tNt+tAnboPx3da9TlCjc9n2AZl6n1S3jYqev3H09NUGPQFWp6zgl\n" +
|
||||
"wFsompYmJ+loNioBWcws6Rjnb8LRXlK8aSeGJmFSH/Z9RlpCy9Yp/sg6ky56garS\n" +
|
||||
"IyRmmIDrU/cyjVOFVONKS5Epx46ioUbI7k7/EMkMRAan59JTrE4ss/aQvCGMdxZn\n" +
|
||||
"kfqSdCusHWgK4IO5kSg++bNpPIv654WUE3xSTqvm5zg17ClI1II0mTHHxvuBNFUw\n" +
|
||||
"hgoAbkCUtWZ1Y/WV6qOpJs0bWC16rLsopebAAoajIGDEDdVl6AeDQ7rFiIP9AD04\n" +
|
||||
"szr8MBPvuo3dQB0hAwxK3VJ33fyBXVtEyz+suFPBDubhB4knQNXKj6895UWdje1p\n" +
|
||||
"BksIQ53bN3Bsfk4QC+Rqoo68+gXrAD0tPt00i8Bcb0GUhwyJcrvS8lvQ+tMfSBff\n" +
|
||||
"EEHS1i5YrPXizFa2/mjm44wPetGr023UkMUI0IYEI0SVlnIAybbUJRt6DSCa9wwz\n" +
|
||||
"/nakascm7U9gkSZfhxtvb2D/iN0zH/Jd49U+trLAcXDIetJd/SodUXzF12qwFfJY\n" +
|
||||
"S0I5ZCW/4opND7fHb9oE56Zt7U9+Ijs0qllkppo5L8kjgSDuiRkAn4FHG9viuo9p\n" +
|
||||
"H+9bWvn4Os2Mo7dGMuQf8TDaNkjtQdrz14pYrQcp6fRt87610dcx2q1Z8dGFtg3D\n" +
|
||||
"FXMxYSFITIAI3jzpjETpSXkIpKk3+6KKWhTahwng6BJXm4FLuv5/7QQrau8dIsjC\n" +
|
||||
"2BkclkgPSTs2VEL0OzvgQtWOUIgy3jysyuwcJf3dHUg7J5wlixzh+eLvbpsx7zsB\n" +
|
||||
"Qx0a7DAgn0yi9LiRTqMUsK8ELXgtBJlaUlgClhjzDqMlSKDj+0tuWkuEgkR7nvNj\n" +
|
||||
"d8WDJJJXRCZtqDp4bTACEpPJMOB8pytOSg45LCubmvZ26YG5FdPCbIN94vRiohzw\n" +
|
||||
"wbCMgU3cacMLX9x8gTvJptRmAeedp97wCoqdHGiebkFgAbNMjNHFrio=\n" +
|
||||
"-----END ENCRYPTED PRIVATE KEY-----\n";
|
||||
|
||||
/**
|
||||
* Decrypt the embedded key, or return null when the native build lacks
|
||||
* PKCS#8 encryption, PBKDF2, or AES-256-CBC support.
|
||||
*/
|
||||
private static byte[] decryptEncKeyPem() throws Exception {
|
||||
try {
|
||||
return WolfCrypt.keyPemToDer(
|
||||
encKeyPem.getBytes("UTF-8"), encKeyPassword);
|
||||
|
||||
} catch (WolfCryptException e) {
|
||||
if (e.getError() == WolfCryptError.NOT_COMPILED_IN) {
|
||||
return null;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeyPemToDerWithPassword() throws Exception {
|
||||
|
||||
byte[] der = decryptEncKeyPem();
|
||||
if (der == null) {
|
||||
System.out.println("Skipping: encrypted PKCS#8 not compiled in");
|
||||
return;
|
||||
}
|
||||
|
||||
assertEquals("DER should start with SEQUENCE tag",
|
||||
0x30, der[0] & 0xFF);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeyPemToDerWrongPassword() throws Exception {
|
||||
|
||||
/* Confirm the correct password works first, otherwise the negative
|
||||
* case below could pass for the wrong reason */
|
||||
if (decryptEncKeyPem() == null) {
|
||||
System.out.println("Skipping: encrypted PKCS#8 not compiled in");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
WolfCrypt.keyPemToDer(
|
||||
encKeyPem.getBytes("UTF-8"), "wrongpassword");
|
||||
fail("Should reject an incorrect password");
|
||||
|
||||
} catch (WolfCryptException e) {
|
||||
/* expected */
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = WolfCryptException.class)
|
||||
public void testPubKeyPemToDerNullInput() throws Exception {
|
||||
WolfCrypt.pubKeyPemToDer(null);
|
||||
|
|
@ -592,6 +757,45 @@ public class WolfCryptTest {
|
|||
WolfCrypt.pubKeyPemToDer(invalidPem);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a SubjectPublicKeyInfo PEM. No standalone public key PEM ships
|
||||
* under examples/certs, so one is generated here.
|
||||
*/
|
||||
private static byte[] generatePubKeyPem() throws Exception {
|
||||
|
||||
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
|
||||
kpg.initialize(2048);
|
||||
byte[] spki = kpg.generateKeyPair().getPublic().getEncoded();
|
||||
|
||||
StringBuilder sb = new StringBuilder("-----BEGIN PUBLIC KEY-----\n");
|
||||
String b64 = Base64.getEncoder().encodeToString(spki);
|
||||
for (int i = 0; i < b64.length(); i += 64) {
|
||||
sb.append(b64, i, Math.min(i + 64, b64.length())).append('\n');
|
||||
}
|
||||
sb.append("-----END PUBLIC KEY-----\n");
|
||||
|
||||
return sb.toString().getBytes("UTF-8");
|
||||
}
|
||||
|
||||
@Test(expected = WolfCryptException.class)
|
||||
public void testPubKeyPemToDerOversizedInput() throws Exception {
|
||||
WolfCrypt.pubKeyPemToDer(
|
||||
padPem(generatePubKeyPem(), 2 * 1024 * 1024));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPubKeyPemToDerAtSizeLimitStillWorks() throws Exception {
|
||||
|
||||
byte[] pem = generatePubKeyPem();
|
||||
byte[] expectedDer = WolfCrypt.pubKeyPemToDer(pem);
|
||||
|
||||
/* Exactly at the limit, which is inclusive, must still convert */
|
||||
byte[] der = WolfCrypt.pubKeyPemToDer(padPem(pem, 1024 * 1024));
|
||||
|
||||
assertArrayEquals("DER output should match unpadded conversion",
|
||||
expectedDer, der);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeyDerHasValidAsn1Structure() throws Exception {
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue