diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/key/constants/HashAlgorithm.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/key/constants/HashAlgorithm.java new file mode 100644 index 00000000..a57bd3a2 --- /dev/null +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/key/constants/HashAlgorithm.java @@ -0,0 +1,57 @@ +package com.github.nagyesta.lowkeyvault.model.v7_2.key.constants; + +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.DERNull; +import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x509.DigestInfo; +import org.springframework.util.Assert; + +import java.io.IOException; +import java.lang.reflect.Array; +import java.security.spec.MGF1ParameterSpec; +import java.security.spec.PSSParameterSpec; +import java.util.Optional; + +@SuppressWarnings("checkstyle:JavadocVariable") +public enum HashAlgorithm { + SHA256("SHA-256", 32, NISTObjectIdentifiers.id_sha256), + SHA384("SHA-384", 48, NISTObjectIdentifiers.id_sha384), + SHA512("SHA-512", 64, NISTObjectIdentifiers.id_sha512); + + private final String algorithmName; + private final ASN1ObjectIdentifier algorithmIdentifier; + private final int digestLength; + + HashAlgorithm(final String algorithmName, final int digestLength, final ASN1ObjectIdentifier algorithmIdentifier) { + this.algorithmName = algorithmName; + this.algorithmIdentifier = algorithmIdentifier; + this.digestLength = digestLength; + } + + public String getAlgorithmName() { + return algorithmName; + } + + public byte[] encodeDigest(final byte[] digest) throws IOException { + return new DigestInfo(new AlgorithmIdentifier(algorithmIdentifier, DERNull.INSTANCE), digest).getEncoded(); + } + + public PSSParameterSpec getPssParameter() { + return new PSSParameterSpec( + algorithmName, + "MGF1", + new MGF1ParameterSpec(algorithmName), + digestLength, + PSSParameterSpec.TRAILER_FIELD_BC + ); + } + + public void verifyDigestLength(final byte[] digest) { + final int length = Optional.ofNullable(digest) + .map(Array::getLength) + .orElseThrow(() -> new IllegalArgumentException("Digest is null.")); + Assert.isTrue(digestLength == length, + "This algorithm does not support digest length: " + length + ". Expected: " + digestLength); + } +} diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/key/constants/SignatureAlgorithm.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/key/constants/SignatureAlgorithm.java index d9b1bd75..53f5f51d 100644 --- a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/key/constants/SignatureAlgorithm.java +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/key/constants/SignatureAlgorithm.java @@ -3,76 +3,116 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonValue; +import com.github.nagyesta.lowkeyvault.service.key.util.KeyGenUtil; +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.Signature; import java.util.Arrays; @SuppressWarnings("checkstyle:JavadocVariable") public enum SignatureAlgorithm { - ES256("ES256", "NONEwithECDSAinP1363Format", KeyType.EC) { + ES256("ES256", "NONEwithECDSAinP1363Format", KeyType.EC, HashAlgorithm.SHA256) { @Override public boolean isCompatibleWithCurve(final KeyCurveName keyCurveName) { return KeyCurveName.P_256 == keyCurveName; } - @SuppressWarnings("checkstyle:MagicNumber") @Override - public boolean supportsDigestLength(final int digestLength) { - return digestLength == 32; + public Signature getSignatureInstance() throws GeneralSecurityException { + return Signature.getInstance(getAlg()); } }, - ES256K("ES256K", "NONEwithECDSAinP1363Format", KeyType.EC) { + ES256K("ES256K", "NONEwithECDSAinP1363Format", KeyType.EC, HashAlgorithm.SHA256) { @Override public boolean isCompatibleWithCurve(final KeyCurveName keyCurveName) { return KeyCurveName.P_256K == keyCurveName; } - @SuppressWarnings("checkstyle:MagicNumber") @Override - public boolean supportsDigestLength(final int digestLength) { - return digestLength == 32; + public Signature getSignatureInstance() throws GeneralSecurityException { + return Signature.getInstance(getAlg()); } }, - ES384("ES384", "NONEwithECDSAinP1363Format", KeyType.EC) { + ES384("ES384", "NONEwithECDSAinP1363Format", KeyType.EC, HashAlgorithm.SHA384) { @Override public boolean isCompatibleWithCurve(final KeyCurveName keyCurveName) { return KeyCurveName.P_384 == keyCurveName; } - @SuppressWarnings("checkstyle:MagicNumber") @Override - public boolean supportsDigestLength(final int digestLength) { - return digestLength == 48; + public Signature getSignatureInstance() throws GeneralSecurityException { + return Signature.getInstance(getAlg()); } }, - ES512("ES512", "NONEwithECDSAinP1363Format", KeyType.EC) { + ES512("ES512", "NONEwithECDSAinP1363Format", KeyType.EC, HashAlgorithm.SHA512) { @Override public boolean isCompatibleWithCurve(final KeyCurveName keyCurveName) { return KeyCurveName.P_521 == keyCurveName; } - @SuppressWarnings("checkstyle:MagicNumber") @Override - public boolean supportsDigestLength(final int digestLength) { - return digestLength == 64; + public Signature getSignatureInstance() throws GeneralSecurityException { + return Signature.getInstance(getAlg()); } }, - PS256("PS256", "SHA256withRSAandMGF1", KeyType.RSA), - PS384("PS384", "SHA384withRSAandMGF1", KeyType.RSA), - PS512("PS512", "SHA512withRSAandMGF1", KeyType.RSA), - RS256("RS256", "SHA256withRSA", KeyType.RSA), - RS384("RS384", "SHA384withRSA", KeyType.RSA), - RS512("RS512", "SHA512withRSA", KeyType.RSA); + PS256("PS256", "NONEwithRSAandMGF1", KeyType.RSA, HashAlgorithm.SHA256) { + @Override + public Signature getSignatureInstance() throws GeneralSecurityException { + final Signature signature = super.getSignatureInstance(); + signature.setParameter(getHashAlgorithm().getPssParameter()); + return signature; + } + }, + PS384("PS384", "NONEwithRSAandMGF1", KeyType.RSA, HashAlgorithm.SHA384) { + @Override + public Signature getSignatureInstance() throws GeneralSecurityException { + final Signature signature = super.getSignatureInstance(); + signature.setParameter(getHashAlgorithm().getPssParameter()); + return signature; + } + }, + PS512("PS512", "NONEwithRSAandMGF1", KeyType.RSA, HashAlgorithm.SHA512) { + @Override + public Signature getSignatureInstance() throws GeneralSecurityException { + final Signature signature = super.getSignatureInstance(); + signature.setParameter(getHashAlgorithm().getPssParameter()); + return signature; + } + }, + RS256("RS256", "NoneWithRSA", KeyType.RSA, HashAlgorithm.SHA256) { + @Override + public byte[] transformDigest(final byte[] digest) throws IOException { + return getHashAlgorithm().encodeDigest(digest); + } + }, + RS384("RS384", "NoneWithRSA", KeyType.RSA, HashAlgorithm.SHA384) { + @Override + public byte[] transformDigest(final byte[] digest) throws IOException { + return getHashAlgorithm().encodeDigest(digest); + } + }, + RS512("RS512", "NoneWithRSA", KeyType.RSA, HashAlgorithm.SHA512) { + @Override + public byte[] transformDigest(final byte[] digest) throws IOException { + return getHashAlgorithm().encodeDigest(digest); + } + }; private final String value; private final String alg; private final KeyType compatibleType; + private final HashAlgorithm hashAlgorithm; SignatureAlgorithm(final String value, - final String alg, final KeyType compatibleType) { + final String alg, + final KeyType compatibleType, + final HashAlgorithm hashAlgorithm) { this.value = value; this.alg = alg; this.compatibleType = compatibleType; + this.hashAlgorithm = hashAlgorithm; } @JsonCreator @@ -101,8 +141,17 @@ public boolean isCompatibleWithCurve(final KeyCurveName keyCurveName) { } @JsonIgnore - public boolean supportsDigestLength(final int digestLength) { - return false; + public byte[] transformDigest(final byte[] digest) throws IOException { + return digest; } + @JsonIgnore + public HashAlgorithm getHashAlgorithm() { + return hashAlgorithm; + } + + @JsonIgnore + public Signature getSignatureInstance() throws GeneralSecurityException { + return Signature.getInstance(getAlg(), KeyGenUtil.BOUNCY_CASTLE_PROVIDER); + } } diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/EcKeyVaultKeyEntity.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/EcKeyVaultKeyEntity.java index 75c56498..01bfb04f 100644 --- a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/EcKeyVaultKeyEntity.java +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/EcKeyVaultKeyEntity.java @@ -8,13 +8,11 @@ import org.springframework.lang.NonNull; import org.springframework.util.Assert; -import java.lang.reflect.Array; import java.security.KeyPair; -import java.security.Signature; import java.security.interfaces.ECPrivateKey; import java.security.interfaces.ECPublicKey; import java.util.List; -import java.util.Optional; +import java.util.concurrent.Callable; import static com.github.nagyesta.lowkeyvault.service.key.util.KeyGenUtil.generateEc; @@ -87,37 +85,19 @@ public byte[] decryptToBytes(final byte[] encrypted, final EncryptionAlgorithm e @Override public byte[] signBytes(final byte[] digest, final SignatureAlgorithm signatureAlgorithm) { - Assert.state(getOperations().contains(KeyOperation.SIGN), getId() + " does not have SIGN operation assigned."); - Assert.state(isEnabled(), getId() + " is not enabled."); + validateGenericSignOrVerifyInputs(digest, signatureAlgorithm, KeyOperation.SIGN); Assert.state(signatureAlgorithm.isCompatibleWithCurve(getKeyCurveName()), getId() + " is not using the right key curve."); - final int length = Optional.ofNullable(digest) - .map(Array::getLength) - .orElseThrow(() -> new IllegalArgumentException("Digest is null.")); - Assert.isTrue(signatureAlgorithm.supportsDigestLength(length), getId() + " does not support digest length: " + length + "."); - return doCrypto(() -> { - final Signature ecSign = Signature.getInstance(signatureAlgorithm.getAlg()); - ecSign.initSign(getKey().getPrivate()); - ecSign.update(digest); - return ecSign.sign(); - }, "Cannot sign message.", log); + final Callable signCallable = signCallable(digest, signatureAlgorithm, getKey().getPrivate()); + return doCrypto(signCallable, "Cannot sign message.", log); } @Override public boolean verifySignedBytes(final byte[] digest, final SignatureAlgorithm signatureAlgorithm, final byte[] signature) { - Assert.state(getOperations().contains(KeyOperation.VERIFY), getId() + " does not have VERIFY operation assigned."); - Assert.state(isEnabled(), getId() + " is not enabled."); + validateGenericSignOrVerifyInputs(digest, signatureAlgorithm, KeyOperation.VERIFY); Assert.state(signatureAlgorithm.isCompatibleWithCurve(getKeyCurveName()), getId() + " is not using the right key curve."); - final int length = Optional.ofNullable(digest) - .map(Array::getLength) - .orElseThrow(() -> new IllegalArgumentException("Digest is null.")); - Assert.isTrue(signatureAlgorithm.supportsDigestLength(length), getId() + " does not support digest length: " + length + "."); - return doCrypto(() -> { - final Signature ecVerify = Signature.getInstance(signatureAlgorithm.getAlg()); - ecVerify.initVerify(getKey().getPublic()); - ecVerify.update(digest); - return ecVerify.verify(signature); - }, "Cannot verify digest message.", log); + final Callable verifyCallable = verifyCallable(digest, signatureAlgorithm, signature, getKey().getPublic()); + return doCrypto(verifyCallable, "Cannot verify digest message.", log); } } diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/KeyVaultKeyEntity.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/KeyVaultKeyEntity.java index a284c22b..fdd9c3db 100644 --- a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/KeyVaultKeyEntity.java +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/KeyVaultKeyEntity.java @@ -1,6 +1,7 @@ package com.github.nagyesta.lowkeyvault.service.key.impl; import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.KeyOperation; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.SignatureAlgorithm; import com.github.nagyesta.lowkeyvault.service.common.impl.KeyVaultBaseEntity; import com.github.nagyesta.lowkeyvault.service.exception.CryptoException; import com.github.nagyesta.lowkeyvault.service.key.ReadOnlyKeyVaultKeyEntity; @@ -10,6 +11,9 @@ import org.slf4j.Logger; import org.springframework.util.Assert; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.Signature; import java.util.Collections; import java.util.List; import java.util.concurrent.Callable; @@ -84,4 +88,31 @@ protected R doCrypto(final Callable task, final String message, final Log } } + protected void validateGenericSignOrVerifyInputs( + final byte[] digest, final SignatureAlgorithm signatureAlgorithm, final KeyOperation keyOperation) { + Assert.state(getOperations().contains(keyOperation), + getId() + " does not have " + keyOperation.name() + " operation assigned."); + Assert.state(isEnabled(), getId() + " is not enabled."); + signatureAlgorithm.getHashAlgorithm().verifyDigestLength(digest); + } + + protected Callable signCallable( + final byte[] digest, final SignatureAlgorithm signatureAlgorithm, final PrivateKey privateKey) { + return () -> { + final Signature sign = signatureAlgorithm.getSignatureInstance(); + sign.initSign(privateKey); + sign.update(signatureAlgorithm.transformDigest(digest)); + return sign.sign(); + }; + } + + protected Callable verifyCallable( + final byte[] digest, final SignatureAlgorithm signatureAlgorithm, final byte[] signature, final PublicKey publicKey) { + return () -> { + final Signature verify = signatureAlgorithm.getSignatureInstance(); + verify.initVerify(publicKey); + verify.update(signatureAlgorithm.transformDigest(digest)); + return verify.verify(signature); + }; + } } diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/RsaKeyVaultKeyEntity.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/RsaKeyVaultKeyEntity.java index d791dda2..c34e5b66 100644 --- a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/RsaKeyVaultKeyEntity.java +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/RsaKeyVaultKeyEntity.java @@ -15,9 +15,9 @@ import javax.crypto.Cipher; import java.math.BigInteger; import java.security.KeyPair; -import java.security.Signature; import java.security.interfaces.RSAPrivateCrtKey; import java.security.interfaces.RSAPublicKey; +import java.util.concurrent.Callable; import static com.github.nagyesta.lowkeyvault.service.key.util.KeyGenUtil.generateRsa; @@ -125,27 +125,17 @@ public byte[] decryptToBytes(@NonNull final byte[] encrypted, @NonNull final Enc @Override public byte[] signBytes(final byte[] digest, final SignatureAlgorithm signatureAlgorithm) { - Assert.state(getOperations().contains(KeyOperation.SIGN), getId() + " does not have SIGN operation assigned."); - Assert.state(isEnabled(), getId() + " is not enabled."); - return doCrypto(() -> { - final Signature rsaSign = Signature.getInstance(signatureAlgorithm.getAlg(), KeyGenUtil.BOUNCY_CASTLE_PROVIDER); - rsaSign.initSign(getKey().getPrivate()); - rsaSign.update(digest); - return rsaSign.sign(); - }, "Cannot sign message.", log); + validateGenericSignOrVerifyInputs(digest, signatureAlgorithm, KeyOperation.SIGN); + final Callable signCallable = signCallable(digest, signatureAlgorithm, getKey().getPrivate()); + return doCrypto(signCallable, "Cannot sign message.", log); } @Override public boolean verifySignedBytes(final byte[] digest, final SignatureAlgorithm signatureAlgorithm, final byte[] signature) { - Assert.state(getOperations().contains(KeyOperation.VERIFY), getId() + " does not have VERIFY operation assigned."); - Assert.state(isEnabled(), getId() + " is not enabled."); - return doCrypto(() -> { - final Signature rsaVerify = Signature.getInstance(signatureAlgorithm.getAlg(), KeyGenUtil.BOUNCY_CASTLE_PROVIDER); - rsaVerify.initVerify(getKey().getPublic()); - rsaVerify.update(digest); - return rsaVerify.verify(signature); - }, "Cannot verify digest message.", log); + validateGenericSignOrVerifyInputs(digest, signatureAlgorithm, KeyOperation.VERIFY); + final Callable verifyCallable = verifyCallable(digest, signatureAlgorithm, signature, getKey().getPublic()); + return doCrypto(verifyCallable, "Cannot verify digest message.", log); } } diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/HashUtil.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/HashUtil.java new file mode 100644 index 00000000..1deacb74 --- /dev/null +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/HashUtil.java @@ -0,0 +1,26 @@ +package com.github.nagyesta.lowkeyvault; + +import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.HashAlgorithm; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public final class HashUtil { + + private HashUtil() { + } + + public static byte[] hash(final byte[] data, final HashAlgorithm algorithm) { + try { + return hash(data, algorithm.getAlgorithmName()); + } catch (final NoSuchAlgorithmException e) { + throw new IllegalArgumentException(e); + } + } + + private static byte[] hash(final byte[] data, final String algorithm) throws NoSuchAlgorithmException { + final MessageDigest md = MessageDigest.getInstance(algorithm); + md.update(data); + return md.digest(); + } +} diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_2/KeyCryptoControllerTest.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_2/KeyCryptoControllerTest.java index 142c4e53..6ec85b2c 100644 --- a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_2/KeyCryptoControllerTest.java +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_2/KeyCryptoControllerTest.java @@ -1,15 +1,13 @@ package com.github.nagyesta.lowkeyvault.controller.v7_2; +import com.github.nagyesta.lowkeyvault.HashUtil; import com.github.nagyesta.lowkeyvault.mapper.common.registry.KeyConverterRegistry; import com.github.nagyesta.lowkeyvault.mapper.v7_2.key.KeyEntityToV72KeyItemModelConverter; import com.github.nagyesta.lowkeyvault.mapper.v7_2.key.KeyEntityToV72KeyVersionItemModelConverter; import com.github.nagyesta.lowkeyvault.mapper.v7_2.key.KeyEntityToV72ModelConverter; import com.github.nagyesta.lowkeyvault.model.common.ApiConstants; import com.github.nagyesta.lowkeyvault.model.v7_2.key.*; -import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.EncryptionAlgorithm; -import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.KeyOperation; -import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.KeyType; -import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.SignatureAlgorithm; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.*; import com.github.nagyesta.lowkeyvault.model.v7_2.key.request.CreateKeyRequest; import com.github.nagyesta.lowkeyvault.model.v7_2.key.request.KeyOperationsParameters; import com.github.nagyesta.lowkeyvault.model.v7_2.key.request.KeySignParameters; @@ -192,7 +190,7 @@ void testSignAndVerifyShouldReturnTrueWhenKeyAndVersionIsFoundAndCalledInSequenc .thenReturn(RESPONSE); final KeySignParameters keySignParameters = new KeySignParameters(); keySignParameters.setAlgorithm(SignatureAlgorithm.PS256); - keySignParameters.setValue(ENCODER.encodeToString(clearText.getBytes(StandardCharsets.UTF_8))); + keySignParameters.setValue(ENCODER.encodeToString(HashUtil.hash(clearText.getBytes(StandardCharsets.UTF_8), HashAlgorithm.SHA256))); //when final ResponseEntity signature = underTest @@ -204,7 +202,7 @@ void testSignAndVerifyShouldReturnTrueWhenKeyAndVersionIsFoundAndCalledInSequenc final KeyVerifyParameters verifyParameters = new KeyVerifyParameters(); verifyParameters.setAlgorithm(SignatureAlgorithm.PS256); - verifyParameters.setDigest(ENCODER.encodeToString(clearText.getBytes(StandardCharsets.UTF_8))); + verifyParameters.setDigest(ENCODER.encodeToString(HashUtil.hash(clearText.getBytes(StandardCharsets.UTF_8), HashAlgorithm.SHA256))); verifyParameters.setValue(signature.getBody().getValue()); final ResponseEntity actual = underTest .verify(KEY_NAME_1, KEY_VERSION_3, HTTPS_LOCALHOST_8443, verifyParameters); diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_3/KeyCryptoControllerTest.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_3/KeyCryptoControllerTest.java index 2c00667c..62cd6b84 100644 --- a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_3/KeyCryptoControllerTest.java +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_3/KeyCryptoControllerTest.java @@ -1,15 +1,13 @@ package com.github.nagyesta.lowkeyvault.controller.v7_3; +import com.github.nagyesta.lowkeyvault.HashUtil; import com.github.nagyesta.lowkeyvault.mapper.common.registry.KeyConverterRegistry; import com.github.nagyesta.lowkeyvault.mapper.v7_2.key.KeyEntityToV72KeyItemModelConverter; import com.github.nagyesta.lowkeyvault.mapper.v7_2.key.KeyEntityToV72KeyVersionItemModelConverter; import com.github.nagyesta.lowkeyvault.mapper.v7_2.key.KeyEntityToV72ModelConverter; import com.github.nagyesta.lowkeyvault.model.common.ApiConstants; import com.github.nagyesta.lowkeyvault.model.v7_2.key.*; -import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.EncryptionAlgorithm; -import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.KeyOperation; -import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.KeyType; -import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.SignatureAlgorithm; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.*; import com.github.nagyesta.lowkeyvault.model.v7_2.key.request.CreateKeyRequest; import com.github.nagyesta.lowkeyvault.model.v7_2.key.request.KeyOperationsParameters; import com.github.nagyesta.lowkeyvault.model.v7_2.key.request.KeySignParameters; @@ -196,7 +194,7 @@ void testSignAndVerifyShouldReturnTrueWhenKeyAndVersionIsFoundAndCalledInSequenc .thenReturn(RESPONSE); final KeySignParameters keySignParameters = new KeySignParameters(); keySignParameters.setAlgorithm(SignatureAlgorithm.PS256); - keySignParameters.setValue(ENCODER.encodeToString(clearText.getBytes(StandardCharsets.UTF_8))); + keySignParameters.setValue(ENCODER.encodeToString(HashUtil.hash(clearText.getBytes(StandardCharsets.UTF_8), HashAlgorithm.SHA256))); //when final ResponseEntity signature = underTest @@ -208,7 +206,7 @@ void testSignAndVerifyShouldReturnTrueWhenKeyAndVersionIsFoundAndCalledInSequenc final KeyVerifyParameters verifyParameters = new KeyVerifyParameters(); verifyParameters.setAlgorithm(SignatureAlgorithm.PS256); - verifyParameters.setDigest(ENCODER.encodeToString(clearText.getBytes(StandardCharsets.UTF_8))); + verifyParameters.setDigest(ENCODER.encodeToString(HashUtil.hash(clearText.getBytes(StandardCharsets.UTF_8), HashAlgorithm.SHA256))); verifyParameters.setValue(signature.getBody().getValue()); final ResponseEntity actual = underTest .verify(KEY_NAME_1, KEY_VERSION_3, HTTPS_LOCALHOST_8443, verifyParameters); diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/v7_2/key/constants/SignatureAlgorithmTest.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/v7_2/key/constants/SignatureAlgorithmTest.java index 875571e9..edb3fae7 100644 --- a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/v7_2/key/constants/SignatureAlgorithmTest.java +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/v7_2/key/constants/SignatureAlgorithmTest.java @@ -58,19 +58,6 @@ void testIsCompatibleWithCurveShouldReturnFalseInCaseOfRsaAlgorithm() { Assertions.assertFalse(actual); } - - @Test - void testSupportsDigestLengthShouldReturnFalseInCaseOfRsaAlgorithm() { - //given - final SignatureAlgorithm underTest = SignatureAlgorithm.PS256; - - //when - final boolean actual = underTest.supportsDigestLength(0); - - //then - Assertions.assertFalse(actual); - } - @ParameterizedTest @MethodSource("valueProvider") void testForValueShouldReturnExpectedAlgorithmWhenCalled(final String inout, final SignatureAlgorithm expected) { @@ -82,17 +69,4 @@ void testForValueShouldReturnExpectedAlgorithmWhenCalled(final String inout, fin //then Assertions.assertEquals(expected, actual); } - - @ParameterizedTest - @MethodSource("digestLengthProvider") - void testSupportsDigestLengthShouldReturnTrueOnlyWhenCalledWithExpectedDigest( - final SignatureAlgorithm underTest, final int input, final boolean expected) { - //given - - //when - final boolean actual = underTest.supportsDigestLength(input); - - //then - Assertions.assertEquals(expected, actual); - } } diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/impl/EcKeyVaultKeyEntityTest.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/impl/EcKeyVaultKeyEntityTest.java index 5ef09e97..08c4aa40 100644 --- a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/impl/EcKeyVaultKeyEntityTest.java +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/impl/EcKeyVaultKeyEntityTest.java @@ -1,12 +1,11 @@ package com.github.nagyesta.lowkeyvault.service.key.impl; -import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.KeyCurveName; -import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.KeyOperation; -import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.KeyType; -import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.SignatureAlgorithm; +import com.github.nagyesta.lowkeyvault.HashUtil; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.*; import com.github.nagyesta.lowkeyvault.service.key.id.VersionedKeyEntityId; import com.github.nagyesta.lowkeyvault.service.vault.VaultFake; import com.github.nagyesta.lowkeyvault.service.vault.impl.VaultFakeImpl; +import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -15,11 +14,10 @@ import org.junit.jupiter.params.provider.NullSource; import java.nio.charset.StandardCharsets; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; +import java.security.Signature; import java.util.Arrays; import java.util.List; -import java.util.Map; import java.util.stream.Stream; import static com.github.nagyesta.lowkeyvault.TestConstants.*; @@ -29,15 +27,6 @@ class EcKeyVaultKeyEntityTest { - private static final String SHA_256 = "SHA-256"; - private static final String SHA_384 = "SHA-384"; - private static final String SHA_512 = "SHA-512"; - private static final Map HASH_ALGORITHMS = Map.of( - SignatureAlgorithm.ES256, SHA_256, - SignatureAlgorithm.ES256K, SHA_256, - SignatureAlgorithm.ES384, SHA_384, - SignatureAlgorithm.ES512, SHA_512); - public static Stream invalidValueProvider() { return Stream.builder() .add(Arguments.of(null, mock(VaultFake.class), KeyCurveName.P_256)) @@ -61,13 +50,26 @@ public static Stream stringSignSource() { .build())); } - public static Stream digestSource() { + public static Stream byteArraySource() { final Object bytes = DEFAULT_VAULT.getBytes(StandardCharsets.UTF_8); return Stream.builder() .add(Arguments.of(bytes)) .build(); } + public static Stream signDigestSource() { + final byte[] bytes = DEFAULT_VAULT.getBytes(StandardCharsets.UTF_8); + final byte[] sha256Digest = HashUtil.hash(bytes, HashAlgorithm.SHA256); + final byte[] sha384Digest = HashUtil.hash(bytes, HashAlgorithm.SHA384); + final byte[] sha512Digest = HashUtil.hash(bytes, HashAlgorithm.SHA512); + return Stream.builder() + .add(Arguments.of(bytes, sha256Digest, KeyCurveName.P_256, SignatureAlgorithm.ES256, "SHA256withPLAIN-ECDSA")) + .add(Arguments.of(bytes, sha256Digest, KeyCurveName.P_256K, SignatureAlgorithm.ES256K, "SHA256withPLAIN-ECDSA")) + .add(Arguments.of(bytes, sha384Digest, KeyCurveName.P_384, SignatureAlgorithm.ES384, "SHA384withPLAIN-ECDSA")) + .add(Arguments.of(bytes, sha512Digest, KeyCurveName.P_521, SignatureAlgorithm.ES512, "SHA512withPLAIN-ECDSA")) + .build(); + } + @ParameterizedTest @MethodSource("invalidValueProvider") void testConstructorShouldThrowExceptionWhenCalledWithNull( @@ -106,8 +108,8 @@ void testSignThenVerifyShouldReturnTrueWhenVerificationAndSignAreUsingTheSameDat VERSIONED_KEY_ENTITY_ID_1_VERSION_1, vaultFake, keyCurveName, false); underTest.setOperations(List.of(KeyOperation.SIGN, KeyOperation.VERIFY)); underTest.setEnabled(true); - final byte[] cipherSign = hash(clearSign.getBytes(StandardCharsets.UTF_8), algorithm); - final byte[] cipherVerify = hash(clearVerify.getBytes(StandardCharsets.UTF_8), algorithm); + final byte[] cipherSign = HashUtil.hash(clearSign.getBytes(StandardCharsets.UTF_8), algorithm.getHashAlgorithm()); + final byte[] cipherVerify = HashUtil.hash(clearVerify.getBytes(StandardCharsets.UTF_8), algorithm.getHashAlgorithm()); //when final byte[] signature = underTest.signBytes(cipherSign, algorithm); @@ -117,6 +119,26 @@ void testSignThenVerifyShouldReturnTrueWhenVerificationAndSignAreUsingTheSameDat Assertions.assertEquals(clearSign.equals(clearVerify), actual); } + @ParameterizedTest + @MethodSource("signDigestSource") + void testSignShouldProduceTheExpectedSignatureWhenCalledWithValidData( + final byte[] original, final byte[] digest, final KeyCurveName keyCurveName, + final SignatureAlgorithm algorithm, final String verifyAlgorithm) throws Exception { + //given + final VaultFake vaultFake = new VaultFakeImpl(HTTPS_LOWKEY_VAULT); + final EcKeyVaultKeyEntity underTest = new EcKeyVaultKeyEntity( + VERSIONED_KEY_ENTITY_ID_1_VERSION_1, vaultFake, keyCurveName, false); + underTest.setOperations(List.of(KeyOperation.SIGN, KeyOperation.VERIFY)); + underTest.setEnabled(true); + + //when + final byte[] signature = underTest.signBytes(digest, algorithm); + + //then + final boolean actual = checkSignature(underTest.getKey().getPublic(), signature, original, verifyAlgorithm); + Assertions.assertTrue(actual); + } + @Test void testSignShouldThrowExceptionWhenOperationIsNotAllowed() { //given @@ -125,7 +147,7 @@ void testSignShouldThrowExceptionWhenOperationIsNotAllowed() { VERSIONED_KEY_ENTITY_ID_1_VERSION_1, vaultFake, KeyCurveName.P_256, false); underTest.setOperations(List.of(KeyOperation.VERIFY)); underTest.setEnabled(true); - final byte[] digest = hash(DEFAULT_VAULT.getBytes(StandardCharsets.UTF_8), SignatureAlgorithm.ES256); + final byte[] digest = HashUtil.hash(DEFAULT_VAULT.getBytes(StandardCharsets.UTF_8), SignatureAlgorithm.ES256.getHashAlgorithm()); //when Assertions.assertThrows(IllegalStateException.class, @@ -142,7 +164,7 @@ void testVerifyShouldThrowExceptionWhenOperationIsNotAllowed() { VERSIONED_KEY_ENTITY_ID_1_VERSION_1, vaultFake, KeyCurveName.P_256, false); underTest.setOperations(List.of(KeyOperation.SIGN)); underTest.setEnabled(true); - final byte[] digest = hash(DEFAULT_VAULT.getBytes(StandardCharsets.UTF_8), SignatureAlgorithm.ES256); + final byte[] digest = HashUtil.hash(DEFAULT_VAULT.getBytes(StandardCharsets.UTF_8), SignatureAlgorithm.ES256.getHashAlgorithm()); //when final byte[] signature = underTest.signBytes(digest, SignatureAlgorithm.ES256); @@ -160,7 +182,7 @@ void testSignShouldThrowExceptionWhenOperationIsNotEnabled() { VERSIONED_KEY_ENTITY_ID_1_VERSION_1, vaultFake, KeyCurveName.P_256, false); underTest.setOperations(List.of(KeyOperation.SIGN, KeyOperation.VERIFY)); underTest.setEnabled(false); - final byte[] digest = hash(DEFAULT_VAULT.getBytes(StandardCharsets.UTF_8), SignatureAlgorithm.ES256); + final byte[] digest = HashUtil.hash(DEFAULT_VAULT.getBytes(StandardCharsets.UTF_8), SignatureAlgorithm.ES256.getHashAlgorithm()); //when Assertions.assertThrows(IllegalStateException.class, @@ -177,7 +199,7 @@ void testVerifyShouldThrowExceptionWhenKeyIsNotEnabled() { VERSIONED_KEY_ENTITY_ID_1_VERSION_1, vaultFake, KeyCurveName.P_256, false); underTest.setOperations(List.of(KeyOperation.SIGN, KeyOperation.VERIFY)); underTest.setEnabled(true); - final byte[] digest = hash(DEFAULT_VAULT.getBytes(StandardCharsets.UTF_8), SignatureAlgorithm.ES256); + final byte[] digest = HashUtil.hash(DEFAULT_VAULT.getBytes(StandardCharsets.UTF_8), SignatureAlgorithm.ES256.getHashAlgorithm()); //when final byte[] signature = underTest.signBytes(digest, SignatureAlgorithm.ES256); @@ -196,7 +218,7 @@ void testSignShouldThrowExceptionWhenKeyCurveIsNotCompatible() { VERSIONED_KEY_ENTITY_ID_1_VERSION_1, vaultFake, KeyCurveName.P_256, false); underTest.setOperations(List.of(KeyOperation.SIGN, KeyOperation.VERIFY)); underTest.setEnabled(true); - final byte[] digest = hash(DEFAULT_VAULT.getBytes(StandardCharsets.UTF_8), SignatureAlgorithm.ES256); + final byte[] digest = HashUtil.hash(DEFAULT_VAULT.getBytes(StandardCharsets.UTF_8), SignatureAlgorithm.ES256.getHashAlgorithm()); //when Assertions.assertThrows(IllegalStateException.class, @@ -213,7 +235,7 @@ void testVerifyShouldThrowExceptionWhenWhenKeyCurveIsNotCompatible() { VERSIONED_KEY_ENTITY_ID_1_VERSION_1, vaultFake, KeyCurveName.P_256, false); underTest.setOperations(List.of(KeyOperation.SIGN, KeyOperation.VERIFY)); underTest.setEnabled(true); - final byte[] digest = hash(DEFAULT_VAULT.getBytes(StandardCharsets.UTF_8), SignatureAlgorithm.ES256); + final byte[] digest = HashUtil.hash(DEFAULT_VAULT.getBytes(StandardCharsets.UTF_8), SignatureAlgorithm.ES256.getHashAlgorithm()); //when final byte[] signature = underTest.signBytes(digest, SignatureAlgorithm.ES256); @@ -224,7 +246,7 @@ void testVerifyShouldThrowExceptionWhenWhenKeyCurveIsNotCompatible() { } @ParameterizedTest - @MethodSource("digestSource") + @MethodSource("byteArraySource") @NullSource void testSignShouldThrowExceptionWhenWhenDigestSizeIsNotCompatible(final byte[] digest) { //given @@ -242,7 +264,7 @@ void testSignShouldThrowExceptionWhenWhenDigestSizeIsNotCompatible(final byte[] } @ParameterizedTest - @MethodSource("digestSource") + @MethodSource("byteArraySource") @NullSource void testVerifyShouldThrowExceptionWhenWhenDigestSizeIsNotCompatible(final byte[] digest) { //given @@ -251,7 +273,7 @@ void testVerifyShouldThrowExceptionWhenWhenDigestSizeIsNotCompatible(final byte[ VERSIONED_KEY_ENTITY_ID_1_VERSION_1, vaultFake, KeyCurveName.P_256, false); underTest.setOperations(List.of(KeyOperation.SIGN, KeyOperation.VERIFY)); underTest.setEnabled(true); - final byte[] hash = hash(DEFAULT_VAULT.getBytes(StandardCharsets.UTF_8), SignatureAlgorithm.ES256); + final byte[] hash = HashUtil.hash(DEFAULT_VAULT.getBytes(StandardCharsets.UTF_8), SignatureAlgorithm.ES256.getHashAlgorithm()); //when final byte[] signature = underTest.signBytes(hash, SignatureAlgorithm.ES256); @@ -278,13 +300,12 @@ void testKeyCreationInputShouldReturnOriginalParameters() { Assertions.assertEquals(keyCurveName, value.getKeyParameter()); } - private byte[] hash(final byte[] text, final SignatureAlgorithm algorithm) { - try { - final MessageDigest md = MessageDigest.getInstance(HASH_ALGORITHMS.get(algorithm)); - md.update(text); - return md.digest(); - } catch (final NoSuchAlgorithmException e) { - throw new IllegalArgumentException(e.getMessage(), e); - } + private boolean checkSignature( + final PublicKey publicKey, final byte[] signatureToCheck, + final byte[] originalDigest, final String algName) throws Exception { + final Signature sig = Signature.getInstance(algName, new BouncyCastleProvider()); + sig.initVerify(publicKey); + sig.update(originalDigest); + return sig.verify(signatureToCheck); } } diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/impl/RsaKeyVaultKeyEntityTest.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/impl/RsaKeyVaultKeyEntityTest.java index f3134c65..56e99aae 100644 --- a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/impl/RsaKeyVaultKeyEntityTest.java +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/impl/RsaKeyVaultKeyEntityTest.java @@ -1,12 +1,11 @@ package com.github.nagyesta.lowkeyvault.service.key.impl; -import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.EncryptionAlgorithm; -import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.KeyOperation; -import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.KeyType; -import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.SignatureAlgorithm; +import com.github.nagyesta.lowkeyvault.HashUtil; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.*; import com.github.nagyesta.lowkeyvault.service.key.id.VersionedKeyEntityId; import com.github.nagyesta.lowkeyvault.service.vault.VaultFake; import com.github.nagyesta.lowkeyvault.service.vault.impl.VaultFakeImpl; +import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -15,6 +14,8 @@ import java.math.BigInteger; import java.nio.charset.StandardCharsets; +import java.security.PublicKey; +import java.security.Signature; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; @@ -61,6 +62,22 @@ public static Stream stringSignSource() { .build()); } + @SuppressWarnings("checkstyle:MagicNumber") + public static Stream signDigestSource() { + final byte[] bytes = DEFAULT_VAULT.getBytes(StandardCharsets.UTF_8); + final byte[] sha256Digest = HashUtil.hash(bytes, HashAlgorithm.SHA256); + final byte[] sha384Digest = HashUtil.hash(bytes, HashAlgorithm.SHA384); + final byte[] sha512Digest = HashUtil.hash(bytes, HashAlgorithm.SHA512); + return Stream.builder() + .add(Arguments.of(bytes, sha256Digest, 2048, SignatureAlgorithm.RS256, "SHA256withRSA")) + .add(Arguments.of(bytes, sha384Digest, 3072, SignatureAlgorithm.RS384, "SHA384withRSA")) + .add(Arguments.of(bytes, sha512Digest, 4096, SignatureAlgorithm.RS512, "SHA512withRSA")) + .add(Arguments.of(bytes, sha256Digest, 2048, SignatureAlgorithm.PS256, "SHA256withRSAandMGF1")) + .add(Arguments.of(bytes, sha384Digest, 3072, SignatureAlgorithm.PS384, "SHA384withRSAandMGF1")) + .add(Arguments.of(bytes, sha512Digest, 4096, SignatureAlgorithm.PS512, "SHA512withRSAandMGF1")) + .build(); + } + @ParameterizedTest @MethodSource("invalidValueProvider") void testConstructorShouldThrowExceptionWhenCalledWithNull( @@ -171,13 +188,35 @@ void testSignThenVerifyShouldReturnTrueWhenVerificationAndSignAreUsingTheSameDat underTest.setEnabled(true); //when - final byte[] signature = underTest.signBytes(clearSign.getBytes(StandardCharsets.UTF_8), algorithm); - final boolean actual = underTest.verifySignedBytes(clearVerify.getBytes(StandardCharsets.UTF_8), algorithm, signature); + final byte[] signHash = HashUtil.hash(clearSign.getBytes(StandardCharsets.UTF_8), algorithm.getHashAlgorithm()); + final byte[] verifyHash = HashUtil.hash(clearVerify.getBytes(StandardCharsets.UTF_8), algorithm.getHashAlgorithm()); + final byte[] signature = underTest.signBytes(signHash, algorithm); + final boolean actual = underTest.verifySignedBytes(verifyHash, algorithm, signature); //then Assertions.assertEquals(clearSign.equals(clearVerify), actual); } + @ParameterizedTest + @MethodSource("signDigestSource") + void testSignShouldProduceTheExpectedSignatureWhenCalledWithValidData( + final byte[] original, final byte[] digest, final int keySize, + final SignatureAlgorithm algorithm, final String verifyAlgorithm) throws Exception { + //given + final VaultFake vaultFake = new VaultFakeImpl(HTTPS_LOWKEY_VAULT); + final RsaKeyVaultKeyEntity underTest = new RsaKeyVaultKeyEntity( + VERSIONED_KEY_ENTITY_ID_1_VERSION_1, vaultFake, keySize, null, false); + underTest.setOperations(List.of(KeyOperation.SIGN, KeyOperation.VERIFY)); + underTest.setEnabled(true); + + //when + final byte[] signature = underTest.signBytes(digest, algorithm); + + //then + final boolean actual = checkSignature(underTest.getKey().getPublic(), signature, original, verifyAlgorithm); + Assertions.assertTrue(actual); + } + @Test void testSignShouldThrowExceptionWhenOperationIsNotAllowed() { //given @@ -204,7 +243,9 @@ void testVerifyShouldThrowExceptionWhenOperationIsNotAllowed() { underTest.setEnabled(true); //when - final byte[] signature = underTest.signBytes(DEFAULT_VAULT.getBytes(StandardCharsets.UTF_8), SignatureAlgorithm.PS256); + final byte[] signature = underTest.signBytes( + HashUtil.hash(DEFAULT_VAULT.getBytes(StandardCharsets.UTF_8), HashAlgorithm.SHA256), + SignatureAlgorithm.PS256); Assertions.assertThrows(IllegalStateException.class, () -> underTest.verifySignedBytes(DEFAULT_VAULT.getBytes(StandardCharsets.UTF_8), SignatureAlgorithm.PS256, signature)); @@ -237,7 +278,9 @@ void testVerifyShouldThrowExceptionWhenKeyIsNotEnabled() { underTest.setEnabled(true); //when - final byte[] signature = underTest.signBytes(DEFAULT_VAULT.getBytes(StandardCharsets.UTF_8), SignatureAlgorithm.PS256); + final byte[] signature = underTest.signBytes( + HashUtil.hash(DEFAULT_VAULT.getBytes(StandardCharsets.UTF_8), HashAlgorithm.SHA256), + SignatureAlgorithm.PS256); underTest.setEnabled(false); Assertions.assertThrows(IllegalStateException.class, () -> underTest.verifySignedBytes(DEFAULT_VAULT.getBytes(StandardCharsets.UTF_8), SignatureAlgorithm.PS256, signature)); @@ -263,4 +306,13 @@ void testKeyCreationInputShouldReturnOriginalParameters() { Assertions.assertEquals(keySize, value.getKeyParameter()); Assertions.assertEquals(publicExponent, value.getPublicExponent()); } + + private boolean checkSignature( + final PublicKey publicKey, final byte[] signatureToCheck, + final byte[] originalDigest, final String algName) throws Exception { + final Signature sig = Signature.getInstance(algName, new BouncyCastleProvider()); + sig.initVerify(publicKey); + sig.update(originalDigest); + return sig.verify(signatureToCheck); + } } diff --git a/lowkey-vault-docker/src/test/java/com/github/nagyesta/lowkeyvault/steps/KeysStepDefs.java b/lowkey-vault-docker/src/test/java/com/github/nagyesta/lowkeyvault/steps/KeysStepDefs.java index 03deceee..74bcd1cf 100644 --- a/lowkey-vault-docker/src/test/java/com/github/nagyesta/lowkeyvault/steps/KeysStepDefs.java +++ b/lowkey-vault-docker/src/test/java/com/github/nagyesta/lowkeyvault/steps/KeysStepDefs.java @@ -338,9 +338,7 @@ public void theCreatedKeyIsUsedToEncryptClearTextWithAlgorithm(final byte[] text public void theCreatedKeyIsUsedToSignClearTextWithAlgorithm(final byte[] text, final SignatureAlgorithm algorithm) { final String keyId = context.getLastResult().getKey().getId(); context.setCryptographyClient(context.getProvider().getCryptoClient(keyId, context.getCryptoServiceVersion())); - final byte[] digest = hash(text, algorithm.toString()); - final SignResult signResult = context.getCryptographyClient() - .sign(algorithm, digest); + final SignResult signResult = context.getCryptographyClient().signData(algorithm, text); context.setSignatureResult(signResult.getSignature()); } @@ -378,18 +376,16 @@ public void theEncryptedValueIsDecryptedUsingTheOriginalOctKeyWithAlgorithm(fina public void theSignValueIsVerifiedWithAlgorithm(final byte[] text, final SignatureAlgorithm algorithm) { final String keyId = context.getLastResult().getKey().getId(); context.setCryptographyClient(context.getProvider().getCryptoClient(keyId, context.getCryptoServiceVersion())); - final byte[] digest = hash(text, algorithm.toString()); - final VerifyResult verifyResult = context.getCryptographyClient().verify(algorithm, digest, context.getSignatureResult()); + final VerifyResult verifyResult = context.getCryptographyClient().verifyData(algorithm, text, context.getSignatureResult()); context.setVerifyResult(verifyResult.isValid()); } @And("the EC signature of {clearText} is verified using the original public key with {signAlgorithm}") public void theEcSignValueIsVerifiedUsingOriginalPublicKeyWithAlgorithm(final byte[] text, final SignatureAlgorithm algorithm) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { - final byte[] digest = hash(text, algorithm.toString()); - final Signature ecVerify = Signature.getInstance(getAsymmetricSignatureAlgorithm(algorithm)); + final Signature ecVerify = Signature.getInstance(getAsymmetricSignatureAlgorithm(algorithm), BOUNCY_CASTLE_PROVIDER); ecVerify.initVerify(context.getKeyPair().getPublic()); - ecVerify.update(digest); + ecVerify.update(text); final boolean result = ecVerify.verify(context.getSignatureResult()); context.setVerifyResult(result); } @@ -397,10 +393,9 @@ public void theEcSignValueIsVerifiedUsingOriginalPublicKeyWithAlgorithm(final by @And("the RSA signature of {clearText} is verified using the original public key with {signAlgorithm}") public void theRsaSignValueIsVerifiedUsingOriginalPublicKeyWithAlgorithm(final byte[] text, final SignatureAlgorithm algorithm) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { - final byte[] digest = hash(text, algorithm.toString()); final Signature rsaVerify = Signature.getInstance(getAsymmetricSignatureAlgorithm(algorithm), BOUNCY_CASTLE_PROVIDER); rsaVerify.initVerify(context.getKeyPair().getPublic()); - rsaVerify.update(digest); + rsaVerify.update(text); final boolean result = rsaVerify.verify(context.getSignatureResult()); context.setVerifyResult(result); } @@ -452,16 +447,6 @@ public void theKeyIsRotated(final String name) { context.addFetchedKey(name, keyVaultKey); } - private byte[] hash(final byte[] text, final String algorithm) { - try { - final MessageDigest md = MessageDigest.getInstance("SHA-" + algorithm.substring(2, 5)); - md.update(text); - return md.digest(); - } catch (final NoSuchAlgorithmException e) { - throw new IllegalArgumentException(e.getMessage(), e); - } - } - private byte[] getIv() { return "iv-parameter-val".getBytes(StandardCharsets.UTF_8); } @@ -532,13 +517,13 @@ private String getSymmetricalEncryptionAlgName(final EncryptionAlgorithm algorit private String getAsymmetricSignatureAlgorithm(final SignatureAlgorithm algorithm) { if (algorithm == ES256) { - return "NONEwithECDSAinP1363Format"; + return "SHA256withPLAIN-ECDSA"; } else if (algorithm == ES256K) { - return "NONEwithECDSAinP1363Format"; + return "SHA256withPLAIN-ECDSA"; } else if (algorithm == ES384) { - return "NONEwithECDSAinP1363Format"; + return "SHA384withPLAIN-ECDSA"; } else if (algorithm == ES512) { - return "NONEwithECDSAinP1363Format"; + return "SHA512withPLAIN-ECDSA"; } else if (algorithm == PS256) { return "SHA256withRSAandMGF1"; } else if (algorithm == PS384) { diff --git a/lowkey-vault-testcontainers/src/main/java/com/github/nagyesta/lowkeyvault/testcontainers/LowkeyVaultContainerBuilder.java b/lowkey-vault-testcontainers/src/main/java/com/github/nagyesta/lowkeyvault/testcontainers/LowkeyVaultContainerBuilder.java index c5746bce..f2f18c25 100644 --- a/lowkey-vault-testcontainers/src/main/java/com/github/nagyesta/lowkeyvault/testcontainers/LowkeyVaultContainerBuilder.java +++ b/lowkey-vault-testcontainers/src/main/java/com/github/nagyesta/lowkeyvault/testcontainers/LowkeyVaultContainerBuilder.java @@ -40,9 +40,6 @@ public LowkeyVaultContainerBuilder vaultNames(final Set vaultNames) { if (vaultNames == null) { throw new IllegalArgumentException("Vault names collection cannot be null."); } - if (vaultNames.contains(null)) { - throw new IllegalArgumentException("Vault names collection cannot contain null."); - } this.vaultNames = Set.copyOf(vaultNames); return this; } diff --git a/lowkey-vault-testcontainers/src/test/java/com/github/nagyesta/lowkeyvault/testcontainers/LowkeyVaultContainerVanillaTest.java b/lowkey-vault-testcontainers/src/test/java/com/github/nagyesta/lowkeyvault/testcontainers/LowkeyVaultContainerVanillaTest.java index abba9b66..6b2831c5 100644 --- a/lowkey-vault-testcontainers/src/test/java/com/github/nagyesta/lowkeyvault/testcontainers/LowkeyVaultContainerVanillaTest.java +++ b/lowkey-vault-testcontainers/src/test/java/com/github/nagyesta/lowkeyvault/testcontainers/LowkeyVaultContainerVanillaTest.java @@ -34,7 +34,6 @@ class LowkeyVaultContainerVanillaTest extends AbstractLowkeyVaultContainerTest { public static Stream invalidDataProvider() { return Stream.builder() .add(Arguments.of((Collection) null)) - .add(Arguments.of(Collections.singleton(null))) .add(Arguments.of(Collections.singleton(""))) .add(Arguments.of(Collections.singleton(" "))) .add(Arguments.of(Collections.singleton("- -")))