Ernesto Schiavo
Ernesto Schiavo

Reputation: 1060

Can't verify signature ECDSA key

I'm trying to create a key pair with ECDSA algorithm using javascript, sign a message and then verify it server-side (with Java).

sample javascript code:

    // help function
    function _arrayBufferToBase64( buffer ) {
        var binary = '';
        var bytes = new Uint8Array( buffer );
        var len = bytes.byteLength;
        for (var i = 0; i < len; i++) {
            binary += String.fromCharCode( bytes[ i ] );
        }
        return window.btoa( binary );
    }
    const encodedMessage = new TextEncoder().encode('test');

    // keys generation
    crypto.subtle.generateKey({
        name: 'ECDSA',
        namedCurve: 'P-256'
    }, true, ['sign','verify']).then((keys) => {

        console.log('keys:', keys);
        console.log('[....] public key export');

        // public key export
        crypto.subtle.exportKey('spki', keys.publicKey).then(result => {

            console.log('publicKey byte:', result);
            console.log('publicKey base64:', _arrayBufferToBase64(result));
  
            // sign
            crypto.subtle.sign({
                    name: 'ECDSA',
                    hash: {name:'SHA-256'}
                },
                keys.privateKey,
                encodedMessage
                ).then(signature => {

                    console.log('signature byte:', signature);
                    console.log('signature base64:', _arrayBufferToBase64(signature));
          
                    // verify
                    crypto.subtle.verify({
                            name: 'ECDSA',
                            hash: {name: 'SHA-256'}
                        },
                        keys.publicKey,
                        signature,
                        encodedMessage
                    ).then(verified => {
                         console.log('signature verified?', verified);
                    });
                });
         });
     });

For example:


    publicKey base64: MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAERhn3GM1eQodijVDdy0Zb+ERjvWZ6idQw8PbaFfTzPdA7oNFh8+AHlni91snTS5Sk2o4Xw9CCnlvHP3uWVyCIvA==
    signature base64: nAHPhibM3txezQ53O3C4vMlbQnMI75ILcaamqBezNS51c6wB2ZHwwk/4phgbp+aFlM9omvxaH0fdzJpCT0s/Zg==
    signature verified? true

and everything is fine.

The problem is I can't verify the same message both using Java or this website:

enter image description here

Java code:

private boolean receiver(String message, String algorithm, String signature, String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, UnsupportedEncodingException, SignatureException {

    Signature ecdsaVerify = Signature.getInstance(algorithm);

    //import org.apache.commons.codec.binary.Base64;
    byte[] decodedPk = Base64.decodeBase64(new String(publicKey).getBytes("UTF-8"));
    EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(decodedPk);

    KeyFactory keyFactory = KeyFactory.getInstance("EC");
    PublicKey pk = keyFactory.generatePublic(publicKeySpec);
    
    ecdsaVerify.initVerify(pk);
    
    byte[] decodedString = new String(message).getBytes("UTF-8");
    byte[] decodedSignature = Base64.decodeBase64(new String(signature).getBytes("UTF-8"));

    ecdsaVerify.update(decodedString);
    boolean result = ecdsaVerify.verify(decodedSignature);

    return result;
}

Where


    String message = "test";
    String algorithm = "SHA256withECDSA";
    String signature = "nAHPhibM3txezQ53O3C4vMlbQnMI75ILcaamqBezNS51c6wB2ZHwwk/4phgbp+aFlM9omvxaH0fdzJpCT0s/Zg==";
    String publicKey = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAERhn3GM1eQodijVDdy0Zb+ERjvWZ6idQw8PbaFfTzPdA7oNFh8+AHlni91snTS5Sk2o4Xw9CCnlvHP3uWVyCIvA==";

And I got this exception:


    [err] java.security.SignatureException: Could not verify signature
    [err]   at sun.security.ec.ECDSASignature.engineVerify(ECDSASignature.java:325)
    [err]   at java.security.Signature$Delegate.engineVerify(Signature.java:1223)
    [err]   at java.security.Signature.verify(Signature.java:656)
    [err]   at it.bpc.bpwebrs.application.services.IamService.receiver(IamService.java:2740)
    [err]   at it.bpc.bpwebrs.application.services.IamService.elaboraAbilitazioni(IamService.java:186)
    [err]   at it.bpc.bpwebrs.infrastructure.servicesImpl.IamResource.eseguiServizioElaborazione(IamResource.java:127)
    [err]   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    [err]   at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    [err]   at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    [err]   at java.lang.reflect.Method.invoke(Method.java:498)
    [err]   at it.bpc.bpwebrs.dispatcher.DispatcherResource.richiamaServizioRichiesto(DispatcherResource.java:447)
    [err]   at it.bpc.bpwebrs.dispatcher.DispatcherResource.postJSONRequest(DispatcherResource.java:227)
    [err]   at sun.reflect.GeneratedMethodAccessor77.invoke(Unknown Source)
    [err]   at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    [err]   at java.lang.reflect.Method.invoke(Method.java:498)
    [err]   at org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory$1.invoke(ResourceMethodInvocationHandlerFactory.java:81)
    [err]   at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run(AbstractJavaResourceMethodDispatcher.java:144)
    [err]   at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:161)
    [err]   at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$ResponseOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:160)
    [err]   at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:99)
    [err]   at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:389)
    [err]   at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:347)
    [err]   at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:102)
    [err]   at org.glassfish.jersey.server.ServerRuntime$2.run(ServerRuntime.java:326)
    [err]   at org.glassfish.jersey.internal.Errors$1.call(Errors.java:271)
    [err]   at org.glassfish.jersey.internal.Errors$1.call(Errors.java:267)
    [err]   at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
    [err]   at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
    [err]   at org.glassfish.jersey.internal.Errors.process(Errors.java:267)
    [err]   at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:317)
    [err]   at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:305)
    [err]   at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:1154)
    [err]   at org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:473)
    [err]   at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:427)
    [err]   at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:388)
    [err]   at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:341)
    [err]   at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:228)
    [err]   at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1285)
    [err]   at [internal classes]
    [err]   at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    [err]   at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:118)
    [err]   at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:84)
    [err]   at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    [err]   at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113)
    [err]   at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    [err]   at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:103)
    [err]   at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    [err]   at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:154)
    [err]   at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    [err]   at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45)
    [err]   at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    [err]   at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilter(BasicAuthenticationFilter.java:150)
    [err]   at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    [err]   at org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter.doFilter(DefaultLoginPageGeneratingFilter.java:155)
    [err]   at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    [err]   at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:199)
    [err]   at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    [err]   at org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter.doFilter(OAuth2AuthenticationProcessingFilter.java:176)
    [err]   at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    [err]   at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110)
    [err]   at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    [err]   at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50)
    [err]   at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    [err]   at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    [err]   at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
    [err]   at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    [err]   at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
    [err]   at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
    [err]   at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:344)
    [err]   at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:261)
    [err]   at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:207)
    [err]   at [internal classes]
    [err]   at it.bpc.bpwebrs.infrastructure.security.cors.CorsFilter.doFilter(CorsFilter.java:180)
    [err]   at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:207)
    [err]   at [internal classes]
    [err]   at it.bpc.bpwebrs.infrastructure.filters.MDCFilter.doFilter(MDCFilter.java:49)
    [err]   at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:207)
    [err]   at [internal classes]
    [err]   at it.bpc.bpwebrs.utilities.infrastructure.EncodingFilter.doFilter(EncodingFilter.java:30)
    [err]   at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:207)
    [err]   at [internal classes]
    [err]   at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    [err]   at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    [err]   at java.lang.Thread.run(Thread.java:748)
    [err] Caused by: java.security.SignatureException: Invalid encoding for signature
    [err]   at sun.security.ec.ECDSASignature.decodeSignature(ECDSASignature.java:400)
    [err]   at sun.security.ec.ECDSASignature.engineVerify(ECDSASignature.java:322)
    [err]   ... 92 more
    [err] Caused by: java.io.IOException: Sequence tag error
    [err]   at sun.security.util.DerInputStream.getSequence(DerInputStream.java:330)
    [err]   at sun.security.ec.ECDSASignature.decodeSignature(ECDSASignature.java:376)
    [err]   

... 93 more

What am'I doing wrong?

Upvotes: 4

Views: 1578

Answers (1)

Topaco
Topaco

Reputation: 49400

There are different formats for an ECDSA signature, on the one hand the IEEE P1363 (r|s) format, on the other hand ASN.1/DER encoded.

The relationship between the two formats is explained here.

WebCrypto applies the IEEE P1363 format, the website and the SHA256withECDSA algorithm in the Java code use ASN.1/DER, which is the reason for the issue.

The SunEC provider also supports IEEE P1363, for this the algorithm to be applied is SHA256withECDSAinP1363Format. With this the verification works with the Java code.

Edit:

Alternatively, BouncyCastle can be used, which supports both formats:

import org.bouncycastle.jce.provider.BouncyCastleProvider;
...
Security.addProvider(new BouncyCastleProvider());
Signature ecdsaVerify = Signature.getInstance("SHA256withPlain-ECDSA", "BC"); //IEEE P1363
...

Upvotes: 3

Related Questions