Мы сгенерировали пары закрытого и открытого ключей и преобразовали закрытый в формат 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
Так что теперь я понятия не имею, что еще может быть не так.
@tink нет, причина была не в этом. Похоже, я понял это, и это связано с кодом криптопакета Java.
Чтобы заставить его работать, я внес следующие изменения:
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);
}
}
Я понятия не имею, почему это изменение сделало трюк. BouncyCastle
s bcpkix-jdk15on
не может быть отлажен (и реализация отладки не предусмотрена, как для bcprov(-debug)-jdk15on
). Так что я понятия не имею, что у него изменилось во внутренностях.
Не могли бы вы подытожить, что вы изменили, чтобы читателям не приходилось посимвольно сравнивать два плотных фрагмента кода? Если это всего лишь одно изменение на setProvider
, то это не очень удовлетворительный ответ, потому что это не должно было дать вам другого результата. На веб-сайте bouncycastle есть отладочные файлы bcprov, и вы можете легко создать свою собственную отладочную версию bcpkix или любой другой библиотеки bc, кроме bcprov.
@PresidentJamesK.Polk, но использование setProvider
с объектом класса Provider
, а не константой String
помогло.
Я ничего не знаю о java, но часто это проблема кодировки, возможно, один из разделителей строк, когда что-то работает в Windows и не работает в Linux. Откуда берутся данные для connectionData, может в пароле болтается
^M
?