Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ecdsa not support #6

Open
zhangpan-soft opened this issue Aug 27, 2024 · 0 comments
Open

ecdsa not support #6

zhangpan-soft opened this issue Aug 27, 2024 · 0 comments

Comments

@zhangpan-soft
Copy link

ECDSA is not supported under Default JWS SignerFactory, or under Default JWS Verifit Factory
We can implement a Signer and Verfit

import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.JWSSigner;
import com.nimbusds.jose.crypto.impl.BaseJWSProvider;
import com.nimbusds.jose.jwk.Curve;
import com.nimbusds.jose.jwk.OctetKeyPair;
import com.nimbusds.jose.util.Base64URL;
import lombok.SneakyThrows;

import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.Set;

public class EdDSASigner extends BaseJWSProvider implements JWSSigner {
    private final Signature signature;
    private final String algorithmName;

    public static final Set<JWSAlgorithm> SUPPORTED_ALGORITHMS;
    public static final Set<Curve> SUPPORTED_CURVES;

    static {
        SUPPORTED_ALGORITHMS = Set.of(JWSAlgorithm.EdDSA, JWSAlgorithm.Ed25519, JWSAlgorithm.Ed448);
        SUPPORTED_CURVES = Set.of(Curve.Ed25519, Curve.Ed448);
    }

    @SneakyThrows
    public EdDSASigner(OctetKeyPair keyPair) {
        super(SUPPORTED_ALGORITHMS);
        algorithmName = keyPair.getAlgorithm().getName();
        signature = Signature.getInstance(algorithmName, "BC");
        PrivateKey privateKey = getPrivateKey(keyPair.getD().decode());
        signature.initSign(privateKey, new SecureRandom());
    }

    // 从 Base64 编码的字符串转换回 PrivateKey 对象
    public PrivateKey getPrivateKey(byte[] bytes) throws Exception {
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes);
        KeyFactory keyFactory = KeyFactory.getInstance(algorithmName, "BC");
        return keyFactory.generatePrivate(keySpec);
    }

    @Override
    @SneakyThrows
    public Base64URL sign(JWSHeader jwsHeader, byte[] bytes) throws JOSEException {
        signature.update(bytes);
        byte[] signatureBytes = signature.sign();
        return new Base64URL(Base64.getUrlEncoder().encodeToString(signatureBytes));
    }
}

import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.JWSVerifier;
import com.nimbusds.jose.crypto.impl.BaseJWSProvider;
import com.nimbusds.jose.crypto.impl.CriticalHeaderParamsDeferral;
import com.nimbusds.jose.jwk.OctetKeyPair;
import com.nimbusds.jose.util.Base64URL;
import lombok.SneakyThrows;

import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Set;

public class EdDSAVerifier extends BaseJWSProvider implements JWSVerifier {
    public static Set<JWSAlgorithm> SUPPORTED_ALGORITHMS;
    private Signature signature;
    private String algorithmName = "EdDSA";

    private final CriticalHeaderParamsDeferral critPolicy = new CriticalHeaderParamsDeferral();

    static {
        SUPPORTED_ALGORITHMS = Set.of(JWSAlgorithm.EdDSA, JWSAlgorithm.Ed25519, JWSAlgorithm.Ed448);
    }

    @SneakyThrows
    public EdDSAVerifier(OctetKeyPair keyPair) {
        super(SUPPORTED_ALGORITHMS);
        algorithmName = keyPair.getAlgorithm().getName();
        signature = Signature.getInstance(algorithmName, "BC");
        signature.initVerify(getPublicKey(keyPair));
    }

    private PublicKey getPublicKey(OctetKeyPair keyPair) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException {
        byte[] decodedX = keyPair.getDecodedX();
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decodedX);
        KeyFactory keyFactory = KeyFactory.getInstance(algorithmName, "BC");
        return keyFactory.generatePublic(keySpec);
    }

    @Override
    @SneakyThrows
    public boolean verify(JWSHeader jwsHeader, byte[] bytes, Base64URL base64URL) throws JOSEException {
        // Check for unrecognized "crit" properties
        if (! critPolicy.headerPasses(jwsHeader)) {
            return false;
        }
        signature.update(bytes);
        return signature.verify(base64URL.decode());
    }
}

import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSSigner;
import com.nimbusds.jose.crypto.ECDSASigner;
import com.nimbusds.jose.crypto.Ed25519Signer;
import com.nimbusds.jose.crypto.MACSigner;
import com.nimbusds.jose.crypto.RSASSASigner;
import com.nimbusds.jose.jca.JCAContext;
import com.nimbusds.jose.jwk.*;
import com.nimbusds.jose.produce.JWSSignerFactory;

import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;

public class JWSSignerFactoryImpl implements JWSSignerFactory {

    /**
     * The JCA context.
     */
    private final JCAContext jcaContext = new JCAContext();

    /**
     * The supported JWS algorithms.
     */
    public static final Set<JWSAlgorithm> SUPPORTED_ALGORITHMS;


    static {
        Set<JWSAlgorithm> algs = new LinkedHashSet<>();
        algs.addAll(MACSigner.SUPPORTED_ALGORITHMS);
        algs.addAll(RSASSASigner.SUPPORTED_ALGORITHMS);
        algs.addAll(ECDSASigner.SUPPORTED_ALGORITHMS);
        algs.addAll(EdDSASigner.SUPPORTED_ALGORITHMS);
        algs.addAll(Ed25519Signer.SUPPORTED_ALGORITHMS);
        SUPPORTED_ALGORITHMS = Collections.unmodifiableSet(algs);
    }

    @Override
    public Set<JWSAlgorithm> supportedJWSAlgorithms() {
        return SUPPORTED_ALGORITHMS;
    }

    @Override
    public JCAContext getJCAContext() {
        return jcaContext;
    }

    @Override
    public JWSSigner createJWSSigner(final JWK key) throws JOSEException {

        if (!key.isPrivate()) { // can't create a signer without the private key
            throw JWKException.expectedPrivate();
        }

        if (key.getKeyUse() != null && ! KeyUse.SIGNATURE.equals(key.getKeyUse())) {
            throw new JWKException("The JWK use must be sig (signature) or unspecified");
        }

        JWSSigner signer;

        // base this just on the key type (+ curve) alone without the algorithm check
        if (key instanceof OctetSequenceKey) {
            signer = new MACSigner((OctetSequenceKey)key);
        } else if (key instanceof RSAKey) {
            signer = new RSASSASigner((RSAKey)key);
        } else if (key instanceof ECKey && ECDSASigner.SUPPORTED_CURVES.contains(((ECKey) key).getCurve())) {
            signer = new ECDSASigner((ECKey)key);
        } else if (key instanceof OctetKeyPair && EdDSASigner.SUPPORTED_CURVES.contains(((OctetKeyPair) key).getCurve())) {
            signer = new EdDSASigner((OctetKeyPair)key);
        } else if (key instanceof OctetKeyPair && Ed25519Signer.SUPPORTED_CURVES.contains(((OctetKeyPair) key).getCurve())) {
            signer = new Ed25519Signer((OctetKeyPair)key);
        } else {
            throw new JOSEException("Unsupported JWK type and / or curve");
        }

        // Apply JCA context
        signer.getJCAContext().setSecureRandom(jcaContext.getSecureRandom());
        signer.getJCAContext().setProvider(jcaContext.getProvider());

        return signer;
    }

    @Override
    public JWSSigner createJWSSigner(final JWK key, final JWSAlgorithm alg) throws JOSEException {

        if (!key.isPrivate()) { // can't create a signer without the private key
            throw JWKException.expectedPrivate();
        }

        if (key.getKeyUse() != null && ! KeyUse.SIGNATURE.equals(key.getKeyUse())) {
            throw new JWKException("The JWK use must be sig (signature) or unspecified");
        }

        JWSSigner signer;


        if (
                MACSigner.SUPPORTED_ALGORITHMS.contains(alg) &&
                        key instanceof OctetSequenceKey) {

            signer = new MACSigner((OctetSequenceKey)key);

        } else if (
                RSASSASigner.SUPPORTED_ALGORITHMS.contains(alg) &&
                        key instanceof RSAKey) {

            signer = new RSASSASigner((RSAKey)key);

        } else if (
                ECDSASigner.SUPPORTED_ALGORITHMS.contains(alg) &&
                        key instanceof ECKey &&
                        ECDSASigner.SUPPORTED_CURVES.contains(((ECKey) key).getCurve())) {

            signer = new ECDSASigner((ECKey)key);

        } else if (
                EdDSASigner.SUPPORTED_ALGORITHMS.contains(alg) &&
                        key instanceof OctetKeyPair
        ){
            signer = new EdDSASigner((OctetKeyPair)key);
        } else if (
                Ed25519Signer.SUPPORTED_ALGORITHMS.contains(alg) &&
                        key instanceof OctetKeyPair &&
                        Ed25519Signer.SUPPORTED_CURVES.contains(((OctetKeyPair) key).getCurve())) {

            signer = new Ed25519Signer((OctetKeyPair)key);

        } else {
            throw new JOSEException("Unsupported JWK type, JWK curve and / or JWS algorithm");
        }

        // Apply JCA context
        signer.getJCAContext().setSecureRandom(jcaContext.getSecureRandom());
        signer.getJCAContext().setProvider(jcaContext.getProvider());

        return signer;
    }
}
import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.ECDSAVerifier;
import com.nimbusds.jose.crypto.MACVerifier;
import com.nimbusds.jose.crypto.RSASSAVerifier;
import com.nimbusds.jose.jca.JCAContext;
import com.nimbusds.jose.jwk.*;

import javax.crypto.SecretKey;
import java.security.PublicKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;

public class JWSVerifierFactoryImpl {

    /**
     * The supported JWS algorithms.
     */
    public static final Set<JWSAlgorithm> SUPPORTED_ALGORITHMS;


    static {
        Set<JWSAlgorithm> algs = new LinkedHashSet<>();
        algs.addAll(MACVerifier.SUPPORTED_ALGORITHMS);
        algs.addAll(RSASSAVerifier.SUPPORTED_ALGORITHMS);
        algs.addAll(ECDSAVerifier.SUPPORTED_ALGORITHMS);
        algs.addAll(EdDSAVerifier.SUPPORTED_ALGORITHMS);
        SUPPORTED_ALGORITHMS = Collections.unmodifiableSet(algs);
    }


    /**
     * The JCA context.
     */
    private final JCAContext jcaContext = new JCAContext();


    public Set<JWSAlgorithm> supportedJWSAlgorithms() {

        return SUPPORTED_ALGORITHMS;
    }


    public JCAContext getJCAContext() {

        return jcaContext;
    }


    public JWSVerifier createJWSVerifier(final JWSHeader header, final JWK key)
            throws JOSEException {

        JWSVerifier verifier;

        if (MACVerifier.SUPPORTED_ALGORITHMS.contains(header.getAlgorithm())) {

            if (!(key instanceof OctetSequenceKey macKey)) {
                throw new KeyTypeException(SecretKey.class);
            }

            verifier = new MACVerifier(macKey);

        } else if (RSASSAVerifier.SUPPORTED_ALGORITHMS.contains(header.getAlgorithm())) {

            if (!(key instanceof RSAKey rsaKey)) {
                throw new KeyTypeException(RSAPublicKey.class);
            }

            verifier = new RSASSAVerifier(rsaKey);

        } else if (ECDSAVerifier.SUPPORTED_ALGORITHMS.contains(header.getAlgorithm())) {

            if (!(key instanceof ECKey ecKey)) {
                throw new KeyTypeException(ECPublicKey.class);
            }

            verifier = new ECDSAVerifier(ecKey);

        } else if (EdDSAVerifier.SUPPORTED_ALGORITHMS.contains(header.getAlgorithm())) {
            if (!(key instanceof OctetKeyPair keyPair)){
                throw new KeyTypeException(PublicKey.class);
            }
            verifier = new EdDSAVerifier(keyPair);
        } else {

            throw new JOSEException("Unsupported JWS algorithm: " + header.getAlgorithm());
        }

        // Apply JCA context, SecureRandom expensive and not needed for verification (iss #385)
        verifier.getJCAContext().setProvider(jcaContext.getProvider());

        return verifier;
    }
}

example

public class JWTGenerate {

    static {
        // 注册 Bouncy Castle 提供者
        if (Security.getProvider("BC") == null) {
            Security.addProvider(new BouncyCastleProvider());
        }
    }

    @SneakyThrows
    public static String generate(JWK jwk, JWTClaimsSet claimsSet, String... headers) {
        Map<String, Object> headersMap = new HashMap<>();
        if (headers != null && headers.length > 0) {
            if (headers.length % 2 != 0) {
                throw new IllegalArgumentException("headers length must be even");
            }
            for (int i = 0; i < headers.length; i += 2) {
                headersMap.put(headers[i], headers[i + 1]);
            }
        }
        JWSHeader header = new JWSHeader.Builder(getAlgorithm(jwk))
                .base64URLEncodePayload(true)
                .keyID(jwk.getKeyID())
                .customParams(headersMap)
                .build();
        JWSObject jwsObject = new JWSObject(header, claimsSet.toPayload());
        JWSSigner signer = new JWSSignerFactoryImpl().createJWSSigner(jwk);

        jwsObject.sign(signer);
        return jwsObject.serialize();
    }

    @SneakyThrows
    public static JWSAlgorithm getAlgorithm(JWK jwk) {
        Algorithm alg = jwk.getAlgorithm();
        if (alg == null) {
            throw new IllegalArgumentException("JWK does not contain an algorithm");
        }
        return JWSAlgorithm.parse(alg.getName());
    }

    @SneakyThrows
    public static void main(String[] args) throws NoSuchAlgorithmException, NoSuchProviderException, JOSEException, InvalidKeySpecException, InvalidKeyException {
        // 确保提供者已经注册
        if (Security.getProvider("BC") == null) {
            throw new IllegalStateException("Bouncy Castle provider not found");
        }

        KeyPair keyPair = KeyPairGenerator.getInstance("ED448", "BC").generateKeyPair();
        JWK jwk = new OctetKeyPair.Builder(Curve.Ed448,new Base64URL(Base64.getUrlEncoder().encodeToString(keyPair.getPublic().getEncoded()))).d(new Base64URL(Base64.getUrlEncoder().encodeToString(keyPair.getPrivate().getEncoded()))).keyID(UUID.randomUUID().toString().toUpperCase()).algorithm(JWSAlgorithm.EdDSA).build();
        boolean contains = OctetKeyPair.SUPPORTED_CURVES.contains(Curve.Ed448);
        System.out.println(contains);
//        JWK jwk = new OctetKeyPairGenerator(Curve.Ed448).keyID(UUID.randomUUID().toString().toUpperCase()).algorithm(JWSAlgorithm.EdDSA).generate();
        String generate = generate(jwk, new JWTClaimsSet.Builder().jwtID(UUID.randomUUID().toString().toUpperCase()).subject("1").audience(List.of("web", "app")).issuer("example.com").expirationTime(new Date(System.currentTimeMillis() + 1000 * 60)).build());
        System.out.println(generate);

        JWSObject jwsObject = JWSObject.parse(generate);
        JWSVerifier jwtsVerifier = new JWSVerifierFactoryImpl().createJWSVerifier(jwsObject.getHeader(),jwk);
        boolean verify = jwsObject.verify(jwtsVerifier);
        System.out.println(verify);
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant