Расшифровать информацию о закрытом ключе не удается только в Linux, где был создан файл, но работает в Windows (epki.decryptPrivateKeyInfo)

Мы сгенерировали пары закрытого и открытого ключей и преобразовали закрытый в формат PEM pkcs8:

openssl genrsa -out psp_api_incoming_private.pem 2048 && openssl rsa -in psp_api_incoming_private.pem -pubout > psp_api_incoming_public.pem
openssl pkcs8 -topk8 -in psp_api_incoming_private.pem -out psp_api_incoming_private_key.pem

Мы скопировали частное для отладки для компьютера с Windows и скопировали в другую папку Linux, где мы назначили им sudo chmod tomcat:tomcat права доступа.

На машине Windows мы смогли запустить приведенный ниже код, чтобы сгенерировать знак с этим закрытым ключом, который был расшифрован с помощью нашего открытого ключа на другой стороне:

Signature signatureSHA256Java = Signature.getInstance("RSASSA-PSS");
signatureSHA256Java.setParameter(new PSSParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, 32, 1));
signatureSHA256Java.initSign(stringToPrivateKey(connectionData.getPathToKey(), connectionData.getPassForKey()));
signatureSHA256Java.update(IOUtils.toByteArray(request.getEntity().getContent()));
byte[] signSHA256Java = signatureSHA256Java.sign();

    
static public PrivateKey stringToPrivateKey(String s, String password)
                throws IOException, PKCSException {
            PrivateKeyInfo pki;
            Security.addProvider(new BouncyCastleProvider());
            try (PEMParser pemParser = new PEMParser(new StringReader(s))) {
                Object o = pemParser.readObject();
                if (o instanceof PKCS8EncryptedPrivateKeyInfo) { // encrypted private key in pkcs8-format
                    logger.info("key in pkcs8 encoding");
                    PKCS8EncryptedPrivateKeyInfo epki = (PKCS8EncryptedPrivateKeyInfo) o;
                    logger.info("epki:" + epki.getEncryptionAlgorithm().getAlgorithm());
                    JcePKCSPBEInputDecryptorProviderBuilder builder =
                            new JcePKCSPBEInputDecryptorProviderBuilder().setProvider("BC");
                    InputDecryptorProvider idp = builder.build(password.toCharArray());
                    pki = epki.decryptPrivateKeyInfo(idp); //<-- here we fail
                } else if (o instanceof PEMEncryptedKeyPair) { // encrypted private key in pkcs8-format
                    logger.info("key in pkcs1 encoding");
                    PEMEncryptedKeyPair epki = (PEMEncryptedKeyPair) o;
                    PEMKeyPair pkp = epki.decryptKeyPair(new BcPEMDecryptorProvider(password.toCharArray()));
                    pki = pkp.getPrivateKeyInfo();
                } else if (o instanceof PEMKeyPair) { // unencrypted private key
                    logger.info("key unencrypted");
                    PEMKeyPair pkp = (PEMKeyPair) o;
                    pki = pkp.getPrivateKeyInfo();
                } else {
                    throw new PKCSException("Invalid encrypted private key class: " + o.getClass().getName());
                }
                JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
                return converter.getPrivateKey(pki);
            }
}

И тогда я получаю следующее исключение:

ERROR c.o.s.w.s.i.WebServiceExceptionHandler - org.bouncycastle.pkcs.PKCSException: unable to read encrypted data: Error finalising cipher
java.lang.RuntimeException: org.bouncycastle.pkcs.PKCSException: unable to read encrypted data: Error finalising cipher
        at com.openpayment.provider.mpc.qr.service.MpcQRServiceImpl.getPrivateKey(MpcQRServiceImpl.java:348)
        at com.openpayment.provider.mpc.qr.service.MpcQRServiceImpl.access$500(MpcQRServiceImpl.java:68)
        at com.openpayment.provider.mpc.qr.service.MpcQRServiceImpl$ProviderHttpClient.post(MpcQRServiceImpl.java:272)
        at com.openpayment.provider.mpc.qr.service.MpcQRServiceImpl.getSubscriberInfo(MpcQRServiceImpl.java:162)
        at com.openpayment.provider.mpc.qr.MpcQRProviderImpl.getSubscriberStatus(MpcQRProviderImpl.java:113)
        at com.openpayment.impl.service.PaymentServiceImpl.getInfo(PaymentServiceImpl.java:193)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
        at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
        at org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:69)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
        at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
        at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:283)
        at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
        at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
        at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
        at com.sun.proxy.$Proxy281.getInfo(Unknown Source)
        at com.openpayment.site.web.service.PaymentResource.getPropertiesInfo(PaymentResource.java:110)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
        at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
        at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133)
        at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:854)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:765)
        at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967)
        at com.openpayment.site.web.service.init.SiteWebDispatcherServlet.doDispatch(SiteWebDispatcherServlet.java:48)
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901)
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
        at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:655)
        at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:764)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:228)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:163)
        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:190)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:163)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:317)
        at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127)
        at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
        at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:114)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
        at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
        at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
        at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
        at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
        at org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter.doFilter(OAuth2AuthenticationProcessingFilter.java:176)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
        at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
        at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:66)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
        at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
        at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
        at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:214)
        at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:177)
        at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:347)
        at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:263)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:190)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:163)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
        at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:687)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357)
        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:382)
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:893)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1723)
        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.base/java.lang.Thread.run(Thread.java:829)
Caused by: org.bouncycastle.pkcs.PKCSException: unable to read encrypted data: Error finalising cipher
        at org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo.decryptPrivateKeyInfo(Unknown Source)
        at com.openpayment.provider.mpc.qr.service.MpcQRServiceImpl.stringToPrivateKey(MpcQRServiceImpl.java:380)
        at com.openpayment.provider.mpc.qr.service.MpcQRServiceImpl.getPrivateKey(MpcQRServiceImpl.java:346)
        ... 95 common frames omitted
Caused by: org.bouncycastle.crypto.io.InvalidCipherTextIOException: Error finalising cipher
        at org.bouncycastle.jcajce.io.CipherInputStream.finaliseCipher(Unknown Source)
        at org.bouncycastle.jcajce.io.CipherInputStream.nextChunk(Unknown Source)
        at org.bouncycastle.jcajce.io.CipherInputStream.read(Unknown Source)
        at org.bouncycastle.util.io.Streams.pipeAll(Unknown Source)
        at org.bouncycastle.util.io.Streams.readAll(Unknown Source)
        ... 98 common frames omitted
Caused by: javax.crypto.BadPaddingException: pad block corrupted
        at org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher$BufferedGenericBlockCipher.doFinal(Unknown Source)
        at org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineDoFinal(Unknown Source)
        at java.base/javax.crypto.Cipher.doFinal(Cipher.java:2085)
        ... 103 common frames omitted

В большинстве вопросов по этой ошибке указано, что причиной является неправильный пароль, поэтому я пытаюсь:

openssl rsa -noout -in YOUR_PRIVATE_KEY_FILE.pem -passin "pass:YOUR_PASSWORD"

С последним расположением файла Linux, и это подтверждает, что пароль именно то, что я ожидаю.

Java, на которой работают приложения Tomcat, — это 64-разрядная серверная виртуальная машина OpenJDK AdoptOpenJDK-11.0.11+9

Так что теперь я понятия не имею, что еще может быть не так.

Я ничего не знаю о java, но часто это проблема кодировки, возможно, один из разделителей строк, когда что-то работает в Windows и не работает в Linux. Откуда берутся данные для connectionData, может в пароле болтается ^M?

tink 08.04.2023 20:50

@tink нет, причина была не в этом. Похоже, я понял это, и это связано с кодом криптопакета Java.

Eljah 08.04.2023 22:07
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
2
80
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Чтобы заставить его работать, я внес следующие изменения:

static public PrivateKey stringToPrivateKey(String s, String password)
            throws IOException, PKCSException {
        PrivateKeyInfo pki;
        BouncyCastleProvider bouncyCastleProvider = new BouncyCastleProvider(); //INITIALISED PROVIDER OBJECT
        Security.addProvider(bouncyCastleProvider);
        try (PEMParser pemParser = new PEMParser(new StringReader(s))) {
            Object o = pemParser.readObject();
            if (o instanceof PKCS8EncryptedPrivateKeyInfo) { // encrypted private key in pkcs8-format
                logger.info("key in pkcs8 encoding");
                PKCS8EncryptedPrivateKeyInfo epki = (PKCS8EncryptedPrivateKeyInfo) o;
                logger.info("epki:" + epki.getEncryptionAlgorithm().getAlgorithm());
                JcePKCSPBEInputDecryptorProviderBuilder builder =
                        new JcePKCSPBEInputDecryptorProviderBuilder().
                                setProvider(bouncyCastleProvider); // USED THIS OBJECT HERE INSTEAD OF STRING PARAMETER "BC
                InputDecryptorProvider idp = builder.build(password.toCharArray());
                pki = epki.decryptPrivateKeyInfo(idp);
            } else if (o instanceof PEMEncryptedKeyPair) { // encrypted private key in pkcs8-format
                logger.info("key in pkcs1 encoding");
                PEMEncryptedKeyPair epki = (PEMEncryptedKeyPair) o;
                PEMKeyPair pkp = epki.decryptKeyPair(new BcPEMDecryptorProvider(password.toCharArray()));
                pki = pkp.getPrivateKeyInfo();
            } else if (o instanceof PEMKeyPair) { // unencrypted private key
                logger.info("key unencrypted");
                PEMKeyPair pkp = (PEMKeyPair) o;
                pki = pkp.getPrivateKeyInfo();
            } else {
                throw new PKCSException("Invalid encrypted private key class: " + o.getClass().getName());
            }
            JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider(bouncyCastleProvider); // USED THIS OBJECT HERE INSTEAD OF STRING PARAMETER "BC"
            return converter.getPrivateKey(pki);
        }
    }

Я понятия не имею, почему это изменение сделало трюк. BouncyCastles bcpkix-jdk15on не может быть отлажен (и реализация отладки не предусмотрена, как для bcprov(-debug)-jdk15on). Так что я понятия не имею, что у него изменилось во внутренностях.

Не могли бы вы подытожить, что вы изменили, чтобы читателям не приходилось посимвольно сравнивать два плотных фрагмента кода? Если это всего лишь одно изменение на setProvider, то это не очень удовлетворительный ответ, потому что это не должно было дать вам другого результата. На веб-сайте bouncycastle есть отладочные файлы bcprov, и вы можете легко создать свою собственную отладочную версию bcpkix или любой другой библиотеки bc, кроме bcprov.

President James K. Polk 08.04.2023 22:52

@PresidentJamesK.Polk, но использование setProvider с объектом класса Provider, а не константой String помогло.

Eljah 09.04.2023 07:10

Другие вопросы по теме