From fe07d76b9c60b10f87213cb4e6eb7e6198d641e9 Mon Sep 17 00:00:00 2001 From: SMan Date: Wed, 26 Jan 2022 03:23:23 -0600 Subject: [PATCH] feat(jans-auth-server): extending crypto support sub pr4 (#670) * feat: extending crypto support, sub pr4; #142; * feat: extending crypto support, sub pr4, fixes; #142/#326; * feat: extending crypto support, sub pr4, eddsa_signer has been added; #142/#326; * fix: jans-auth-server: extending crypto support, sub pr4; fixes; #142/#326; * fix: jans-auth-server: extending crypto support, sub pr4; fixes; #142/#326; * feat: jans-auth-server: temp commit for sonar; fixes #365; * feat: jans-auth-server: temp commit for sonar; fixes #365; * feat: jans-auth-server: fix sonar warns; #365; * feat: jans-auth-server: temp commit for sonar; fixes #365; * feat: jans-auth-server: temp commit for sonar; fixes #365; * feat: jans-auth-server: temp commit for sonar; fixes #365; * feat: jans-auth-server: temp commit for sonar; fixes #365; * feat: jans-auth-server: temp commit for sonar; fixes #365; --- .../io/jans/as/client/util/KeyGenerator.java | 9 +- .../as/model/crypto/AuthCryptoProvider.java | 2 +- .../io/jans/as/model/crypto/Certificate.java | 81 +++++++++-- .../crypto/signature/EDDSAKeyFactory.java | 27 ++-- .../crypto/signature/SignatureAlgorithm.java | 16 ++- .../java/io/jans/as/model/jwk/Algorithm.java | 5 +- .../io/jans/as/model/jws/ECDSASigner.java | 57 ++++---- .../io/jans/as/model/jws/EDDSASigner.java | 135 ++++++++++++++++++ .../java/io/jans/as/model/util/HashUtil.java | 69 ++++++--- .../java/io/jans/as/model/util/JwtUtil.java | 32 +++-- .../io/jans/as/model/util/HashUtilTest.java | 26 ++-- 11 files changed, 348 insertions(+), 111 deletions(-) create mode 100644 jans-auth-server/model/src/main/java/io/jans/as/model/jws/EDDSASigner.java diff --git a/jans-auth-server/client/src/main/java/io/jans/as/client/util/KeyGenerator.java b/jans-auth-server/client/src/main/java/io/jans/as/client/util/KeyGenerator.java index 58b4f00557f..4ef570c1b4f 100644 --- a/jans-auth-server/client/src/main/java/io/jans/as/client/util/KeyGenerator.java +++ b/jans-auth-server/client/src/main/java/io/jans/as/client/util/KeyGenerator.java @@ -51,9 +51,9 @@ * Command example: * java -cp bcprov-jdk15on-1.54.jar:.jar:bcpkix-jdk15on-1.54.jar:commons-cli-1.2.jar:commons-codec-1.5.jar:commons-lang-2.6.jar:jettison-1.3.jar:log4j-1.2.14.jar:oxauth-model.jar:oxauth.jar KeyGenerator -h *

- * KeyGenerator -sig_keys RS256 RS384 RS512 ES256 ES256K ES384 ES512 PS256 PS384 PS512 Ed25519 Ed448 -enc_keys RSA1_5 RSA-OAEP ECDH-ES ECDH-ES+A128KW ECDH-ES+A192KW ECDH-ES+A256KW -keystore /Users/JAVIER/tmp/mykeystore.jks -keypasswd secret -dnname "CN=Jans Auth CA Certificates" -expiration 365 + * KeyGenerator -sig_keys RS256 RS384 RS512 ES256 ES256K ES384 ES512 PS256 PS384 PS512 EdDSA -enc_keys RSA1_5 RSA-OAEP RSA-OAEP-256 ECDH-ES ECDH-ES+A128KW ECDH-ES+A192KW ECDH-ES+A256KW -keystore /Users/JAVIER/tmp/mykeystore.jks -keypasswd secret -dnname "CN=Jans Auth CA Certificates" -expiration 365 *

- * KeyGenerator -sig_keys RS256 RS384 RS512 ES256 ES256K ES384 ES512 PS256 PS384 PS512 Ed25519 Ed448 -ox11 https://ce.gluu.info:8443/oxeleven/rest/generateKey -expiration 365 -at xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + * KeyGenerator -sig_keys RS256 RS384 RS512 ES256 ES256K ES384 ES512 PS256 PS384 PS512 EdDSA -ox11 https://ce.gluu.info:8443/oxeleven/rest/generateKey -expiration 365 -at xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx * * @author Javier Rojas Blum * @author Yuriy Movchan @@ -97,11 +97,11 @@ public Cli(String[] args) { this.args = args; Option signingKeysOption = new Option(SIGNING_KEYS, true, - "Signature keys to generate (RS256 RS384 RS512 ES256 ES256K ES384 ES512 PS256 PS384 PS512 Ed25519 Ed448)."); + "Signature keys to generate (RS256 RS384 RS512 ES256 ES256K ES384 ES512 PS256 PS384 PS512 EdDSA)."); signingKeysOption.setArgs(Option.UNLIMITED_VALUES); Option encryptionKeysOption = new Option(ENCRYPTION_KEYS, true, - "Encryption keys to generate (RSA1_5 RSA-OAEP ECDH-ES ECDH-ES+A128KW ECDH-ES+A192KW ECDH-ES+A256KW)."); + "Encryption keys to generate (RSA1_5 RSA-OAEP RSA-OAEP-256 ECDH-ES ECDH-ES+A128KW ECDH-ES+A192KW ECDH-ES+A256KW)."); encryptionKeysOption.setArgs(Option.UNLIMITED_VALUES); options.addOption(signingKeysOption); @@ -114,6 +114,7 @@ public Cli(String[] args) { options.addOption(EXPIRATION, true, "Expiration in days."); options.addOption(EXPIRATION_HOURS, true, "Expiration in hours."); options.addOption(KEY_LENGTH, true, "Key length"); + options.addOption(TEST_PROP_FILE, true, "Tests property file."); options.addOption(HELP, false, "Show help."); } diff --git a/jans-auth-server/model/src/main/java/io/jans/as/model/crypto/AuthCryptoProvider.java b/jans-auth-server/model/src/main/java/io/jans/as/model/crypto/AuthCryptoProvider.java index aff7d06c7c6..9eb30f31a47 100644 --- a/jans-auth-server/model/src/main/java/io/jans/as/model/crypto/AuthCryptoProvider.java +++ b/jans-auth-server/model/src/main/java/io/jans/as/model/crypto/AuthCryptoProvider.java @@ -461,7 +461,7 @@ private JSONObject generateKeySignature(Algorithm algorithm, Long expirationTime break; } case ED: { - EdDSAParameterSpec edSpec = new EdDSAParameterSpec(signatureAlgorithm.getName()); + EdDSAParameterSpec edSpec = new EdDSAParameterSpec(signatureAlgorithm.getCurve().getAlias()); keyGen = KeyPairGenerator.getInstance(signatureAlgorithm.getName(), "BC"); keyGen.initialize(edSpec, new SecureRandom()); break; diff --git a/jans-auth-server/model/src/main/java/io/jans/as/model/crypto/Certificate.java b/jans-auth-server/model/src/main/java/io/jans/as/model/crypto/Certificate.java index d649b02031f..fa2e14da464 100644 --- a/jans-auth-server/model/src/main/java/io/jans/as/model/crypto/Certificate.java +++ b/jans-auth-server/model/src/main/java/io/jans/as/model/crypto/Certificate.java @@ -7,10 +7,12 @@ package io.jans.as.model.crypto; import io.jans.as.model.crypto.signature.ECDSAPublicKey; +import io.jans.as.model.crypto.signature.EDDSAPublicKey; import io.jans.as.model.crypto.signature.RSAPublicKey; import io.jans.as.model.crypto.signature.SignatureAlgorithm; import io.jans.as.model.util.StringUtils; import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; +import org.bouncycastle.jcajce.provider.asymmetric.edec.BCEdDSAPublicKey; import org.bouncycastle.jcajce.provider.asymmetric.rsa.BCRSAPublicKey; import org.bouncycastle.openssl.jcajce.JcaPEMWriter; import org.json.JSONArray; @@ -21,61 +23,113 @@ import java.util.Arrays; /** + * Certificate, uses RSA, EcDSA, EdDSA. + * * @author Javier Rojas Blum - * @version June 29, 2016 + * @author Sergey Manoylo + * @version September 13, 2021 */ public class Certificate { private final SignatureAlgorithm signatureAlgorithm; private final X509Certificate x509Certificate; + /** + * Constructor. + * + * @param signatureAlgorithm Signature algorithm (RS256, RS384, RS512, ES256, ES256K, ES384, ES512, PS256, PS384, PS512, EDDSA/Ed25519). + * @param x509Certificate X509 certificate. + */ public Certificate(SignatureAlgorithm signatureAlgorithm, X509Certificate x509Certificate) { this.signatureAlgorithm = signatureAlgorithm; this.x509Certificate = x509Certificate; } + /** + * Returns Public Key from X509 Certificate. + * + * @return Public Key from X509 Certificate. + */ public PublicKey getPublicKey() { + if(x509Certificate == null) { + return null; + } PublicKey publicKey = null; - - if (x509Certificate != null && x509Certificate.getPublicKey() instanceof BCRSAPublicKey) { + if (x509Certificate.getPublicKey() instanceof BCRSAPublicKey) { BCRSAPublicKey jcersaPublicKey = (BCRSAPublicKey) x509Certificate.getPublicKey(); publicKey = new RSAPublicKey(jcersaPublicKey.getModulus(), jcersaPublicKey.getPublicExponent()); - } else if (x509Certificate != null && x509Certificate.getPublicKey() instanceof BCECPublicKey) { + } else if (x509Certificate.getPublicKey() instanceof BCECPublicKey) { BCECPublicKey jceecPublicKey = (BCECPublicKey) x509Certificate.getPublicKey(); publicKey = new ECDSAPublicKey(signatureAlgorithm, jceecPublicKey.getQ().getXCoord().toBigInteger(), jceecPublicKey.getQ().getYCoord().toBigInteger()); - } + } else if (x509Certificate.getPublicKey() instanceof BCEdDSAPublicKey) { + BCEdDSAPublicKey jceedPublicKey = (BCEdDSAPublicKey) x509Certificate.getPublicKey(); + publicKey = new EDDSAPublicKey(signatureAlgorithm, jceedPublicKey.getEncoded()); + } return publicKey; } + /** + * Returns RSA Public Key from X509 Certificate. + * + * @return RSA Public Key from X509 Certificate. + */ public RSAPublicKey getRsaPublicKey() { + if(x509Certificate == null) { + return null; + } RSAPublicKey rsaPublicKey = null; - - if (x509Certificate != null && x509Certificate.getPublicKey() instanceof BCRSAPublicKey) { + if (x509Certificate.getPublicKey() instanceof BCRSAPublicKey) { BCRSAPublicKey publicKey = (BCRSAPublicKey) x509Certificate.getPublicKey(); - + rsaPublicKey = new RSAPublicKey(publicKey.getModulus(), publicKey.getPublicExponent()); + } else if (x509Certificate.getPublicKey() instanceof java.security.interfaces.RSAPublicKey) { + java.security.interfaces.RSAPublicKey publicKey = (java.security.interfaces.RSAPublicKey) x509Certificate + .getPublicKey(); rsaPublicKey = new RSAPublicKey(publicKey.getModulus(), publicKey.getPublicExponent()); } - return rsaPublicKey; } + /** + * Returns ECDSA Public Key from X509 Certificate. + * + * @return ECDSA Public Key from X509 Certificate. + */ public ECDSAPublicKey getEcdsaPublicKey() { + if(x509Certificate == null) { + return null; + } ECDSAPublicKey ecdsaPublicKey = null; - - if (x509Certificate != null && x509Certificate.getPublicKey() instanceof BCECPublicKey) { + if (x509Certificate.getPublicKey() instanceof BCECPublicKey) { BCECPublicKey publicKey = (BCECPublicKey) x509Certificate.getPublicKey(); - ecdsaPublicKey = new ECDSAPublicKey(signatureAlgorithm, publicKey.getQ().getXCoord().toBigInteger(), publicKey.getQ().getYCoord().toBigInteger()); + } else if (x509Certificate.getPublicKey() instanceof java.security.interfaces.ECPublicKey) { + java.security.interfaces.ECPublicKey publicKey = (java.security.interfaces.ECPublicKey) x509Certificate + .getPublicKey(); + ecdsaPublicKey = new ECDSAPublicKey(signatureAlgorithm, publicKey.getW().getAffineX(), + publicKey.getW().getAffineY()); } - return ecdsaPublicKey; } + /** + * Returns EDDSA Public Key from X509 Certificate. + * + * @return EDDSA Public Key from X509 Certificate. + */ + public EDDSAPublicKey getEddsaPublicKey() { + EDDSAPublicKey eddsaPublicKey = null; + if (x509Certificate != null && x509Certificate.getPublicKey() instanceof BCEdDSAPublicKey) { + BCEdDSAPublicKey publicKey = (BCEdDSAPublicKey) x509Certificate.getPublicKey(); + eddsaPublicKey = new EDDSAPublicKey(signatureAlgorithm, publicKey.getEncoded()); + } + return eddsaPublicKey; + } + public JSONArray toJSONArray() throws JSONException { String cert = toString(); @@ -99,4 +153,5 @@ public String toString() { return StringUtils.EMPTY_STRING; } } + } \ No newline at end of file diff --git a/jans-auth-server/model/src/main/java/io/jans/as/model/crypto/signature/EDDSAKeyFactory.java b/jans-auth-server/model/src/main/java/io/jans/as/model/crypto/signature/EDDSAKeyFactory.java index 89b23202d3f..b699f6fe221 100644 --- a/jans-auth-server/model/src/main/java/io/jans/as/model/crypto/signature/EDDSAKeyFactory.java +++ b/jans-auth-server/model/src/main/java/io/jans/as/model/crypto/signature/EDDSAKeyFactory.java @@ -16,7 +16,6 @@ import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters; import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters; -import org.bouncycastle.crypto.params.Ed448PublicKeyParameters; import org.bouncycastle.crypto.util.PrivateKeyInfoFactory; import org.bouncycastle.jcajce.provider.asymmetric.edec.BCEdDSAPrivateKey; import org.bouncycastle.jcajce.provider.asymmetric.edec.BCEdDSAPublicKey; @@ -82,7 +81,7 @@ public EDDSAKeyFactory(final SignatureAlgorithm signatureAlgorithm, final String } this.signatureAlgorithm = signatureAlgorithm; - EdDSAParameterSpec edSpec = new EdDSAParameterSpec(signatureAlgorithm.getName()); + EdDSAParameterSpec edSpec = new EdDSAParameterSpec(signatureAlgorithm.getCurve().getName()); KeyPairGenerator keyGen = KeyPairGenerator.getInstance(signatureAlgorithm.getName(), DEF_BC); keyGen.initialize(edSpec, new SecureRandom()); @@ -213,23 +212,13 @@ public static EDDSAPrivateKey createEDDSAPrivateKeyFromDecodedKey(final Signatur */ private static byte[] getEncodedPubKey(final SignatureAlgorithm signatureAlgorithm, final byte[] decodedPublicKey) throws SignatureException { byte[] encodedPubKey = null; - switch (signatureAlgorithm) { - case EDDSA: - case ED25519: { - encodedPubKey = new byte[Ed25519Prefix.length + Ed25519PublicKeyParameters.KEY_SIZE]; - System.arraycopy(Ed25519Prefix, 0, encodedPubKey, 0, Ed25519Prefix.length); - System.arraycopy(decodedPublicKey, 0, encodedPubKey, Ed25519Prefix.length, decodedPublicKey.length); - break; - } - case ED448: { - encodedPubKey = new byte[Ed448Prefix.length + Ed448PublicKeyParameters.KEY_SIZE]; - System.arraycopy(Ed448Prefix, 0, encodedPubKey, 0, Ed448Prefix.length); - System.arraycopy(decodedPublicKey, 0, encodedPubKey, Ed448Prefix.length, decodedPublicKey.length); - break; - } - default: { - throw new SignatureException(String.format("Wrong type of the signature algorithm (SignatureAlgorithm): %s", signatureAlgorithm.toString())); - } + if (signatureAlgorithm == SignatureAlgorithm.EDDSA) { + encodedPubKey = new byte[Ed25519Prefix.length + Ed25519PublicKeyParameters.KEY_SIZE]; + System.arraycopy(Ed25519Prefix, 0, encodedPubKey, 0, Ed25519Prefix.length); + System.arraycopy(decodedPublicKey, 0, encodedPubKey, Ed25519Prefix.length, decodedPublicKey.length); + } + else { + throw new SignatureException(String.format("Wrong type of the signature algorithm (SignatureAlgorithm): %s", signatureAlgorithm.toString())); } return encodedPubKey; } diff --git a/jans-auth-server/model/src/main/java/io/jans/as/model/crypto/signature/SignatureAlgorithm.java b/jans-auth-server/model/src/main/java/io/jans/as/model/crypto/signature/SignatureAlgorithm.java index 884fd6775b6..6c4e059b2f1 100644 --- a/jans-auth-server/model/src/main/java/io/jans/as/model/crypto/signature/SignatureAlgorithm.java +++ b/jans-auth-server/model/src/main/java/io/jans/as/model/crypto/signature/SignatureAlgorithm.java @@ -16,6 +16,16 @@ import java.util.List; /** + * Signature Algorithms. + * + * JWS digital signature and MAC "alg" (algorithm) values + * (RFC 7518, A.1. Digital Signature/MAC Algorithm Identifier + * Cross-Reference). + * + * CFRG Elliptic Curve Diffie-Hellman (ECDH) and Signatures + * in JSON Object Signing and Encryption (JOSE) signature + * algorithm "Ed25519". + * * @author Javier Rojas Blum * @author Sergey Manoylo * @version October 26, 2021 @@ -41,9 +51,7 @@ public enum SignatureAlgorithm { PS384(SignatureAlgorithm.DEF_PS384, AlgorithmFamily.RSA, SignatureAlgorithm.DEF_SHA384WITHRSAANDMGF1, JWSAlgorithm.PS384), PS512(SignatureAlgorithm.DEF_PS512, AlgorithmFamily.RSA, SignatureAlgorithm.DEF_SHA512WITHRSAANDMGF1, JWSAlgorithm.PS512), - ED25519(SignatureAlgorithm.DEF_ED25519, AlgorithmFamily.ED, SignatureAlgorithm.DEF_ED25519, EllipticEdvardsCurve.ED_25519, JWSAlgorithm.EdDSA), - ED448(SignatureAlgorithm.DEF_ED448, AlgorithmFamily.ED, SignatureAlgorithm.DEF_ED448, EllipticEdvardsCurve.ED_448, JWSAlgorithm.EdDSA), - EDDSA(SignatureAlgorithm.DEF_EDDDSA, AlgorithmFamily.ED, SignatureAlgorithm.DEF_ED25519, EllipticEdvardsCurve.ED_25519, JWSAlgorithm.EdDSA); + EDDSA(SignatureAlgorithm.DEF_EDDDSA, AlgorithmFamily.ED, SignatureAlgorithm.DEF_EDDDSA, EllipticEdvardsCurve.ED_25519, JWSAlgorithm.EdDSA); public static final String DEF_HS256 = "HS256"; public static final String DEF_HS384 = "HS384"; @@ -62,8 +70,6 @@ public enum SignatureAlgorithm { public static final String DEF_PS384 = "PS384"; public static final String DEF_PS512 = "PS512"; - public static final String DEF_ED25519 = "Ed25519"; - public static final String DEF_ED448 = "Ed448"; public static final String DEF_EDDDSA = "EdDSA"; public static final String DEF_HMACSHA256 = "HMACSHA256"; diff --git a/jans-auth-server/model/src/main/java/io/jans/as/model/jwk/Algorithm.java b/jans-auth-server/model/src/main/java/io/jans/as/model/jwk/Algorithm.java index f1493c02114..2b2d9507b24 100644 --- a/jans-auth-server/model/src/main/java/io/jans/as/model/jwk/Algorithm.java +++ b/jans-auth-server/model/src/main/java/io/jans/as/model/jwk/Algorithm.java @@ -39,8 +39,7 @@ public enum Algorithm { PS384("PS384", "id_token PS384 Sign Key", "Signature Key: RSASSA-PSS using SHA-384 and MGF1 with SHA-384", Use.SIGNATURE, AlgorithmFamily.RSA, RSAKeyFactory.DEF_KEYLENGTH), PS512("PS512", "id_token PS512 Sign Key", "Signature Key: RSASSA-PSS using SHA-512 and MGF1 with SHA-512", Use.SIGNATURE, AlgorithmFamily.RSA, RSAKeyFactory.DEF_KEYLENGTH), - ED25519("Ed25519", "id_token Ed25519 Sign Key", "Signature Key: EDDSA using Ed25519 with SHA-512", Use.SIGNATURE, AlgorithmFamily.ED, 256), - ED448("Ed448", "id_token Ed448 Sign Key", "Signature Key: EDDSA using Ed448 with SHA-3/SHAKE256", Use.SIGNATURE, AlgorithmFamily.ED, 456), + EDDSA("EdDSA", "id_token EdDSA Sign Key", "Signature Key: EdDSA using Ed25519 with SHA-512", Use.SIGNATURE, AlgorithmFamily.ED, 256), // Encryption RSA1_5("RSA1_5", "id_token RSA1_5 Encryption Key", "Encryption Key: RSAES-PKCS1-v1_5", @@ -133,7 +132,7 @@ public int getKeyLength() { } public boolean canGenerateKeys() { // based on currently supported generator, see io.jans.as.model.crypto.AuthCryptoProvider.generateKeyEncryption - return family == AlgorithmFamily.RSA || family == AlgorithmFamily.EC; + return family == AlgorithmFamily.RSA || family == AlgorithmFamily.EC || family == AlgorithmFamily.ED; } /** diff --git a/jans-auth-server/model/src/main/java/io/jans/as/model/jws/ECDSASigner.java b/jans-auth-server/model/src/main/java/io/jans/as/model/jws/ECDSASigner.java index 2136f2b9f1f..e59ebc3f1fe 100644 --- a/jans-auth-server/model/src/main/java/io/jans/as/model/jws/ECDSASigner.java +++ b/jans-auth-server/model/src/main/java/io/jans/as/model/jws/ECDSASigner.java @@ -25,32 +25,55 @@ import java.security.PublicKey; import java.security.Signature; import java.security.SignatureException; -import java.security.spec.InvalidKeySpecException; /** + * Implementing the AbstractJwsSigner, that uses ECDSA for signing. + * * @author Javier Rojas Blum - * @version July 31, 2016 + * @author Sergey Manoylo + * @version September 13, 2021 */ public class ECDSASigner extends AbstractJwsSigner { private ECDSAPrivateKey ecdsaPrivateKey; private ECDSAPublicKey ecdsaPublicKey; + /** + * Constructor. + * + * @param signatureAlgorithm signature algorithm. + * @param ecdsaPrivateKey ecdsa private key. + */ public ECDSASigner(SignatureAlgorithm signatureAlgorithm, ECDSAPrivateKey ecdsaPrivateKey) { super(signatureAlgorithm); this.ecdsaPrivateKey = ecdsaPrivateKey; } + /** + * Constructor. + * + * @param signatureAlgorithm signature algorithm. + * @param ecdsaPublicKey ecdsa public key. + */ public ECDSASigner(SignatureAlgorithm signatureAlgorithm, ECDSAPublicKey ecdsaPublicKey) { super(signatureAlgorithm); this.ecdsaPublicKey = ecdsaPublicKey; } + /** + * Constructor. + * + * @param signatureAlgorithm signature algorithm. + * @param certificate certificate (uses RSA, EcDSA, EdDSA). + */ public ECDSASigner(SignatureAlgorithm signatureAlgorithm, io.jans.as.model.crypto.Certificate certificate) { super(signatureAlgorithm); this.ecdsaPublicKey = certificate.getEcdsaPublicKey(); } + /** + * Generating a signature, using URL safe based format. + */ @Override public String generateSignature(String signingInput) throws SignatureException { if (getSignatureAlgorithm() == null) { @@ -86,6 +109,9 @@ public String generateSignature(String signingInput) throws SignatureException { } } + /** + * Validating a signature. + */ @Override public boolean validateSignature(String signingInput, String signature) throws SignatureException { if (getSignatureAlgorithm() == null) { @@ -97,26 +123,7 @@ public boolean validateSignature(String signingInput, String signature) throws S if (signingInput == null) { throw new SignatureException("The signing input is null"); } - - String algorithm; - String curve; - switch (getSignatureAlgorithm()) { - case ES256: - algorithm = "SHA256WITHECDSA"; - curve = "P-256"; - break; - case ES384: - algorithm = "SHA384WITHECDSA"; - curve = "P-384"; - break; - case ES512: - algorithm = "SHA512WITHECDSA"; - curve = "P-521"; - break; - default: - throw new SignatureException("Unsupported signature algorithm"); - } - + SignatureAlgorithm signatureAlgorithm = getSignatureAlgorithm(); try { byte[] sigBytes = Base64Util.base64urldecode(signature); if (AlgorithmFamily.EC.equals(getSignatureAlgorithm().getFamily())) { @@ -124,7 +131,7 @@ public boolean validateSignature(String signingInput, String signature) throws S } byte[] sigInBytes = signingInput.getBytes(StandardCharsets.UTF_8); - ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec(curve); + ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec(signatureAlgorithm.getCurve().getAlias()); ECPoint pointQ = ecSpec.getCurve().createPoint(ecdsaPublicKey.getX(), ecdsaPublicKey.getY()); ECPublicKeySpec publicKeySpec = new ECPublicKeySpec(pointQ, ecSpec); @@ -132,12 +139,10 @@ public boolean validateSignature(String signingInput, String signature) throws S KeyFactory keyFactory = KeyFactory.getInstance("ECDSA", "BC"); PublicKey publicKey = keyFactory.generatePublic(publicKeySpec); - Signature sig = Signature.getInstance(algorithm, "BC"); + Signature sig = Signature.getInstance(signatureAlgorithm.getAlgorithm(), "BC"); sig.initVerify(publicKey); sig.update(sigInBytes); return sig.verify(sigBytes); - } catch (InvalidKeySpecException e) { - throw new SignatureException(e); } catch (Exception e) { throw new SignatureException(e); } diff --git a/jans-auth-server/model/src/main/java/io/jans/as/model/jws/EDDSASigner.java b/jans-auth-server/model/src/main/java/io/jans/as/model/jws/EDDSASigner.java new file mode 100644 index 00000000000..15de5675e7a --- /dev/null +++ b/jans-auth-server/model/src/main/java/io/jans/as/model/jws/EDDSASigner.java @@ -0,0 +1,135 @@ +/* + * Janssen Project software is available under the Apache License (2004). See http://www.apache.org/licenses/ for full text. + * + * Copyright (c) 2022, Janssen Project + */ +package io.jans.as.model.jws; + +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Signature; +import java.security.SignatureException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +import org.bouncycastle.jcajce.provider.asymmetric.edec.BCEdDSAPrivateKey; +import org.bouncycastle.jcajce.provider.asymmetric.edec.BCEdDSAPublicKey; + +import io.jans.as.model.crypto.Certificate; +import io.jans.as.model.crypto.signature.AlgorithmFamily; +import io.jans.as.model.crypto.signature.EDDSAPrivateKey; +import io.jans.as.model.crypto.signature.EDDSAPublicKey; +import io.jans.as.model.crypto.signature.SignatureAlgorithm; +import io.jans.as.model.util.Base64Util; + +/** + * Implementing the AbstractJwsSigner, that uses EDDSA for signing. + * + * @author Sergey Manoylo + * @version January 20, 2021 + */ +public class EDDSASigner extends AbstractJwsSigner { + + private EDDSAPrivateKey eddsaPrivateKey; + private EDDSAPublicKey eddsaPublicKey; + + /** + * Constructor. + * + * @param signatureAlgorithm signature algorithm. + * @param eddsaPrivateKey eddsa private key. + */ + public EDDSASigner(SignatureAlgorithm signatureAlgorithm, EDDSAPrivateKey eddsaPrivateKey) { + super(signatureAlgorithm); + this.eddsaPrivateKey = eddsaPrivateKey; + } + + /** + * Constructor. + * + * @param signatureAlgorithm signature algorithm. + * @param eddsaPublicKey eddsa public key. + */ + public EDDSASigner(SignatureAlgorithm signatureAlgorithm, EDDSAPublicKey eddsaPublicKey) { + super(signatureAlgorithm); + this.eddsaPublicKey = eddsaPublicKey; + } + + /** + * Constructor. + * + * @param signatureAlgorithm signature algorithm. + * @param certificate certificate (uses RSA, EcDSA, EdDSA). + */ + public EDDSASigner(SignatureAlgorithm signatureAlgorithm, Certificate certificate) { + super(signatureAlgorithm); + this.eddsaPublicKey = certificate.getEddsaPublicKey(); + } + + /** + * Generating a signature, + * using URL safe based format. + */ + @Override + public String generateSignature(String signingInput) throws SignatureException { + SignatureAlgorithm signatureAlgorithm = getSignatureAlgorithm(); + if (signatureAlgorithm == null) { + throw new SignatureException("The signature algorithm is null"); + } + if (!signatureAlgorithm.getFamily().equals(AlgorithmFamily.ED)) { + throw new SignatureException(String.format("Wrong value of the signature algorithm: %s", signatureAlgorithm.getFamily().toString())); + } + if (eddsaPrivateKey == null) { + throw new SignatureException("The EDDSA private key is null"); + } + if (signingInput == null) { + throw new SignatureException("The signing input is null"); + } + try { + PKCS8EncodedKeySpec privateKeySpec = eddsaPrivateKey.getPrivateKeySpec(); + java.security.KeyFactory keyFactory = java.security.KeyFactory.getInstance(signatureAlgorithm.getName()); + BCEdDSAPrivateKey privateKey = (BCEdDSAPrivateKey) keyFactory.generatePrivate(privateKeySpec); + Signature signer = Signature.getInstance(signatureAlgorithm.getName(), "BC"); + signer.initSign(privateKey); + signer.update(signingInput.getBytes()); + byte[] signature = signer.sign(); + return Base64Util.base64urlencode(signature); + } catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidKeySpecException | InvalidKeyException + | IllegalArgumentException e) { + throw new SignatureException(e); + } + } + + /** + * Validating a signature. + */ + @Override + public boolean validateSignature(String signingInput, String signature) throws SignatureException { + SignatureAlgorithm signatureAlgorithm = getSignatureAlgorithm(); + if (signatureAlgorithm == null) { + throw new SignatureException("The signature algorithm is null"); + } + if (!signatureAlgorithm.getFamily().equals(AlgorithmFamily.ED)) { + throw new SignatureException(String.format("Wrong value of the signature algorithm: %s", signatureAlgorithm.getFamily().toString())); + } + if (eddsaPublicKey == null) { + throw new SignatureException("The EDDSA public key is null"); + } + if (signingInput == null) { + throw new SignatureException("The signing input is null"); + } + try { + X509EncodedKeySpec publicKeySpec = eddsaPublicKey.getPublicKeySpec(); + java.security.KeyFactory keyFactory = java.security.KeyFactory.getInstance(signatureAlgorithm.getName()); + BCEdDSAPublicKey publicKey = (BCEdDSAPublicKey) keyFactory.generatePublic(publicKeySpec); + Signature virifier = Signature.getInstance(signatureAlgorithm.getName(), "BC"); + virifier.initVerify(publicKey); + virifier.update(signingInput.getBytes()); + return virifier.verify(Base64Util.base64urldecode(signature)); + } catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidKeySpecException | InvalidKeyException | IllegalArgumentException e) { + throw new SignatureException(e); + } + } +} diff --git a/jans-auth-server/model/src/main/java/io/jans/as/model/util/HashUtil.java b/jans-auth-server/model/src/main/java/io/jans/as/model/util/HashUtil.java index 17e1609f14e..50c85ffe50a 100644 --- a/jans-auth-server/model/src/main/java/io/jans/as/model/util/HashUtil.java +++ b/jans-auth-server/model/src/main/java/io/jans/as/model/util/HashUtil.java @@ -6,41 +6,71 @@ package io.jans.as.model.util; -import io.jans.as.model.crypto.signature.SignatureAlgorithm; import org.apache.log4j.Logger; +import io.jans.as.model.crypto.signature.SignatureAlgorithm; + /** + * Hash Tool, that calculates Hash Code, using Hashing Algorithm, defined by the Signature Algorithm. + * I.e. Hashing Algorithm depends on the Signature Algorithm. + * * @author Yuriy Zabrovarnyy + * @author Sergey Manoylo + * @version December 5, 2021 */ public class HashUtil { private static final Logger log = Logger.getLogger(HashUtil.class); + /** + * Constructor. + */ private HashUtil() { } + /** + * Calculates Hash Code, using Hashing Algorithm, defined by used Signature Algorithm. + * + * @param input Input string, whose hash code is being calculated. + * @param signatureAlgorithm Signature Algorithm. + * @return Hash Code, using algorithm, defined by used Signature Algorithm. + */ public static String getHash(String input, SignatureAlgorithm signatureAlgorithm) { try { - final byte[] digest; - if (signatureAlgorithm == SignatureAlgorithm.HS256 || - signatureAlgorithm == SignatureAlgorithm.RS256 || - signatureAlgorithm == SignatureAlgorithm.PS256 || - signatureAlgorithm == SignatureAlgorithm.ES256) { - digest = JwtUtil.getMessageDigestSHA256(input); - } else if (signatureAlgorithm == SignatureAlgorithm.HS384 || - signatureAlgorithm == SignatureAlgorithm.RS384 || - signatureAlgorithm == SignatureAlgorithm.PS384 || - signatureAlgorithm == SignatureAlgorithm.ES384) { - digest = JwtUtil.getMessageDigestSHA384(input); - } else if (signatureAlgorithm == SignatureAlgorithm.HS512 || - signatureAlgorithm == SignatureAlgorithm.RS512 || - signatureAlgorithm == SignatureAlgorithm.PS512 || - signatureAlgorithm == SignatureAlgorithm.ES512) { - digest = JwtUtil.getMessageDigestSHA512(input); - } else { // Default + byte[] digest = null; + if (signatureAlgorithm != null) { + switch (signatureAlgorithm) { + case HS256: + case RS256: + case PS256: + case ES256: + case ES256K: { + digest = JwtUtil.getMessageDigestSHA256(input); + break; + } + case HS384: + case RS384: + case PS384: + case ES384: { + digest = JwtUtil.getMessageDigestSHA384(input); + break; + } + case HS512: + case RS512: + case PS512: + case ES512: + case EDDSA: { + digest = JwtUtil.getMessageDigestSHA512(input); + break; + } + default: { + digest = JwtUtil.getMessageDigestSHA256(input); + break; + } + } + } else { digest = JwtUtil.getMessageDigestSHA256(input); } - if (digest != null) { byte[] lefMostHalf = new byte[digest.length / 2]; System.arraycopy(digest, 0, lefMostHalf, 0, lefMostHalf.length); @@ -49,7 +79,6 @@ public static String getHash(String input, SignatureAlgorithm signatureAlgorithm } catch (Exception e) { log.error("Failed to calculate hash.", e); } - return null; } } diff --git a/jans-auth-server/model/src/main/java/io/jans/as/model/util/JwtUtil.java b/jans-auth-server/model/src/main/java/io/jans/as/model/util/JwtUtil.java index 7e26ad88ec3..c9cfbe418cb 100644 --- a/jans-auth-server/model/src/main/java/io/jans/as/model/util/JwtUtil.java +++ b/jans-auth-server/model/src/main/java/io/jans/as/model/util/JwtUtil.java @@ -9,9 +9,12 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsonorg.JsonOrgModule; import io.jans.as.model.crypto.Certificate; +import io.jans.as.model.crypto.signature.AlgorithmFamily; import io.jans.as.model.crypto.signature.ECDSAPublicKey; +import io.jans.as.model.crypto.signature.EDDSAPublicKey; import io.jans.as.model.crypto.signature.RSAPublicKey; import io.jans.as.model.crypto.signature.SignatureAlgorithm; +import io.jans.as.model.exception.InvalidParameterException; import io.jans.as.model.jwt.Jwt; import io.jans.util.StringHelper; import org.bouncycastle.jce.provider.X509CertificateObject; @@ -48,9 +51,13 @@ import static io.jans.as.model.jwk.JWKParameter.Y; /** + * Utility class (can't be instantiated), that provides suite of additional functions, + * which can be used, during JWT/JWE processing. + * * @author Javier Rojas Blum * @author Yuriy Movchan - * @version December 8, 2018 + * @author Sergey Manoylo + * @version December 5, 2021 */ public class JwtUtil { @@ -131,7 +138,8 @@ public static io.jans.as.model.crypto.PublicKey getPublicKey( jsonPublicKey = jsonKeyValue.getJSONObject(PUBLIC_KEY); } - if (signatureAlgorithm == SignatureAlgorithm.RS256 || signatureAlgorithm == SignatureAlgorithm.RS384 || signatureAlgorithm == SignatureAlgorithm.RS512) { + AlgorithmFamily algorithmFamily = signatureAlgorithm.getFamily(); + if (algorithmFamily == AlgorithmFamily.RSA) { String exp = jsonPublicKey.getString(EXPONENT); String mod = jsonPublicKey.getString(MODULUS); @@ -139,7 +147,7 @@ public static io.jans.as.model.crypto.PublicKey getPublicKey( BigInteger modulus = new BigInteger(1, Base64Util.base64urldecode(mod)); publicKey = new RSAPublicKey(modulus, publicExponent); - } else if (signatureAlgorithm == SignatureAlgorithm.ES256 || signatureAlgorithm == SignatureAlgorithm.ES384 || signatureAlgorithm == SignatureAlgorithm.ES512) { + } else if(algorithmFamily == AlgorithmFamily.EC) { String xx = jsonPublicKey.getString(X); String yy = jsonPublicKey.getString(Y); @@ -147,9 +155,17 @@ public static io.jans.as.model.crypto.PublicKey getPublicKey( BigInteger y = new BigInteger(1, Base64Util.base64urldecode(yy)); publicKey = new ECDSAPublicKey(signatureAlgorithm, x, y); + } else if(algorithmFamily == AlgorithmFamily.ED) { + String xx = jsonPublicKey.getString(X); + + BigInteger x = new BigInteger(1, Base64Util.base64urldecode(xx)); + + publicKey = new EDDSAPublicKey(signatureAlgorithm, x.toByteArray()); + } else { + throw new InvalidParameterException("Wrong value of the AlgorithmFamily: algorithmFamily = " + algorithmFamily); } - if (publicKey != null && jsonKeyValue.has(CERTIFICATE_CHAIN)) { + if (jsonKeyValue.has(CERTIFICATE_CHAIN)) { final String BEGIN = "-----BEGIN CERTIFICATE-----"; final String END = "-----END CERTIFICATE-----"; @@ -161,10 +177,10 @@ public static io.jans.as.model.crypto.PublicKey getPublicKey( io.jans.as.model.crypto.Certificate certificate = new Certificate(signatureAlgorithm, cert); publicKey.setCertificate(certificate); } - if (publicKey != null) { - publicKey.setKeyId(resultKeyId); - publicKey.setSignatureAlgorithm(signatureAlgorithm); - } + + publicKey.setKeyId(resultKeyId); + publicKey.setSignatureAlgorithm(signatureAlgorithm); + } catch (Exception ex) { log.error(ex.getMessage(), ex); } diff --git a/jans-auth-server/model/src/test/java/io/jans/as/model/util/HashUtilTest.java b/jans-auth-server/model/src/test/java/io/jans/as/model/util/HashUtilTest.java index b23bb50fbe2..f01f39017d9 100644 --- a/jans-auth-server/model/src/test/java/io/jans/as/model/util/HashUtilTest.java +++ b/jans-auth-server/model/src/test/java/io/jans/as/model/util/HashUtilTest.java @@ -27,25 +27,27 @@ public class HashUtilTest { @Test public void s256Hash() { - assertEquals("hhNHO19gwnEguTE5SAK-GA", HashUtil.getHash(INPUT, SignatureAlgorithm.ES256)); - assertEquals("hhNHO19gwnEguTE5SAK-GA", HashUtil.getHash(INPUT, SignatureAlgorithm.HS256)); - assertEquals("hhNHO19gwnEguTE5SAK-GA", HashUtil.getHash(INPUT, SignatureAlgorithm.PS256)); - assertEquals("hhNHO19gwnEguTE5SAK-GA", HashUtil.getHash(INPUT, SignatureAlgorithm.RS256)); + assertEquals(HashUtil.getHash(INPUT, SignatureAlgorithm.ES256), "hhNHO19gwnEguTE5SAK-GA"); + assertEquals(HashUtil.getHash(INPUT, SignatureAlgorithm.HS256), "hhNHO19gwnEguTE5SAK-GA"); + assertEquals(HashUtil.getHash(INPUT, SignatureAlgorithm.ES256K), "hhNHO19gwnEguTE5SAK-GA"); + assertEquals(HashUtil.getHash(INPUT, SignatureAlgorithm.PS256), "hhNHO19gwnEguTE5SAK-GA"); + assertEquals(HashUtil.getHash(INPUT, SignatureAlgorithm.RS256), "hhNHO19gwnEguTE5SAK-GA"); } @Test public void s384Hash() { - assertEquals("W-f-EBbMtR-505d5wk4m78wd6qn1vQkZ", HashUtil.getHash(INPUT, SignatureAlgorithm.ES384)); - assertEquals("W-f-EBbMtR-505d5wk4m78wd6qn1vQkZ", HashUtil.getHash(INPUT, SignatureAlgorithm.HS384)); - assertEquals("W-f-EBbMtR-505d5wk4m78wd6qn1vQkZ", HashUtil.getHash(INPUT, SignatureAlgorithm.PS384)); - assertEquals("W-f-EBbMtR-505d5wk4m78wd6qn1vQkZ", HashUtil.getHash(INPUT, SignatureAlgorithm.RS384)); + assertEquals(HashUtil.getHash(INPUT, SignatureAlgorithm.ES384), "W-f-EBbMtR-505d5wk4m78wd6qn1vQkZ"); + assertEquals(HashUtil.getHash(INPUT, SignatureAlgorithm.HS384), "W-f-EBbMtR-505d5wk4m78wd6qn1vQkZ"); + assertEquals(HashUtil.getHash(INPUT, SignatureAlgorithm.PS384), "W-f-EBbMtR-505d5wk4m78wd6qn1vQkZ"); + assertEquals(HashUtil.getHash(INPUT, SignatureAlgorithm.RS384), "W-f-EBbMtR-505d5wk4m78wd6qn1vQkZ"); } @Test public void s512Hash() { - assertEquals("CCmNwrkP_FbnPPpQ5f96xpXTDuzHSeGd3jGZ_JrPJo4", HashUtil.getHash(INPUT, SignatureAlgorithm.ES512)); - assertEquals("CCmNwrkP_FbnPPpQ5f96xpXTDuzHSeGd3jGZ_JrPJo4", HashUtil.getHash(INPUT, SignatureAlgorithm.HS512)); - assertEquals("CCmNwrkP_FbnPPpQ5f96xpXTDuzHSeGd3jGZ_JrPJo4", HashUtil.getHash(INPUT, SignatureAlgorithm.PS512)); - assertEquals("CCmNwrkP_FbnPPpQ5f96xpXTDuzHSeGd3jGZ_JrPJo4", HashUtil.getHash(INPUT, SignatureAlgorithm.RS512)); + assertEquals(HashUtil.getHash(INPUT, SignatureAlgorithm.ES512), "CCmNwrkP_FbnPPpQ5f96xpXTDuzHSeGd3jGZ_JrPJo4"); + assertEquals(HashUtil.getHash(INPUT, SignatureAlgorithm.HS512), "CCmNwrkP_FbnPPpQ5f96xpXTDuzHSeGd3jGZ_JrPJo4"); + assertEquals(HashUtil.getHash(INPUT, SignatureAlgorithm.PS512), "CCmNwrkP_FbnPPpQ5f96xpXTDuzHSeGd3jGZ_JrPJo4"); + assertEquals(HashUtil.getHash(INPUT, SignatureAlgorithm.RS512), "CCmNwrkP_FbnPPpQ5f96xpXTDuzHSeGd3jGZ_JrPJo4"); + assertEquals(HashUtil.getHash(INPUT, SignatureAlgorithm.EDDSA), "CCmNwrkP_FbnPPpQ5f96xpXTDuzHSeGd3jGZ_JrPJo4"); } }