JwtAccessTokenConverter: Unable to create an RSA verifier from verifierKey - spring-boot

I have an authorization server Spring Boot project using Spring Cloud OAuth2. I'm using these beans for JWT:
#Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter tokenConverter = new JwtAccessTokenConverter();
tokenConverter.setSigningKey("my-test-jwt-secret");
return tokenConverter;
}
#Bean
public JwtTokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
Login apparently is working fine, but when I run the project, I'm getting this warning:
WARN 16804 --- [main] o.s.s.o.p.t.s.JwtAccessTokenConverter : Unable to create an RSA verifier from verifierKey (ignoreable if using MAC)
How do I get rid of this warning?

A JwtAccessTokenConverter can be configured to use either a MAC key or a RSA key pair for signature generation and verification.
As the message provided in the warning states, you are probably using a MAC key and not a RSA key pair. As a consequence, it probably will not suppose a problem, but I am afraid that you cannot get rid of the warning due to the way in which the library is implemented.
As you can see in the source code of JwtAccessTokenConverter, the warning is issued when trying creating a RsaVerifier for signature verification:
SignatureVerifier verifier = new MacSigner(verifierKey);
try {
verifier = new RsaVerifier(verifierKey);
}
catch (Exception e) {
logger.warn("Unable to create an RSA verifier from verifierKey (ignoreable if using MAC)");
}
The exception is raised in the RsaVerifier constructor because it is trying parsing the verification key as a RSA public key, when probably you are using a MAC key instead:
public RsaVerifier(String key) {
this(RsaKeyHelper.parsePublicKey(key.trim()), RsaSigner.DEFAULT_ALGORITHM);
}
Here, RsaKeyHelper will unsuccessfully try parsing the provided key as neither a ssh nor pem key, because it actually is not that type of key.
The value of this verification key is assumed to be the same provided as signing key as argument of the setSigningKey method for MAC keys.
If you are actually working with RSA keys you can use the setVerifierKey or setKeyPair methods to provide the cryptographic RSA material.

Related

Problem with kerberos iis authentication in spring boot application on windows

i'a trying to deploy my jar spring boot application on windows but get error : [Krb5LoginModule] authentication failed
KrbException: Cannot locate default realm
In my localhost, everything is OK with the authentication but whene i deploy the jar in the production server i got the error even if both windows are in the same campany doamin.
the system administrator told me that for other application, the authentication is based on Kerberos and iis so the ticket exchange for authentication is very easy.
Here's my security config :
#Bean
public KerberosAuthenticationProvider kerberosAuthenticationProvider() {
KerberosAuthenticationProvider provider =
new KerberosAuthenticationProvider();
SunJaasKerberosClient client = new SunJaasKerberosClient();
client.setDebug(true);
provider.setKerberosClient(client);
provider.setUserDetailsService(dummyUserDetailsService());
return provider;
}
#Bean
public SpnegoEntryPoint spnegoEntryPoint() {
//return new SpnegoEntryPoint("/login");
return new SpnegoEntryPoint();
}
#Bean
public SpnegoAuthenticationProcessingFilter spnegoAuthenticationProcessingFilter(
AuthenticationManager authenticationManager) {
SpnegoAuthenticationProcessingFilter filter =
new SpnegoAuthenticationProcessingFilter();
filter.setAuthenticationManager(authenticationManager);
return filter;
}
#Bean
public KerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider() {
KerberosServiceAuthenticationProvider provider =
new KerberosServiceAuthenticationProvider();
provider.setTicketValidator(sunJaasKerberosTicketValidator());
provider.setUserDetailsService(dummyUserDetailsService());
return provider;
}
#Bean
public SunJaasKerberosTicketValidator sunJaasKerberosTicketValidator() {
SunJaasKerberosTicketValidator ticketValidator =
new SunJaasKerberosTicketValidator();
ticketValidator.setServicePrincipal("HTTP/localhost#fgao.fr");
ticketValidator.setKeyTabLocation(new
FileSystemResource("c:\\user\\tomcat.keytab"));
ticketValidator.setDebug(true);
return ticketValidator;
}
#Bean
public DummyUserDetailsService dummyUserDetailsService() {
return new DummyUserDetailsService();
}
I don't know if i have to specify the keytab file because on windows no keytab or kb5.conf file is needed so the c:\user\tomcat.keytab file is empty.
Can someone help me with this please
You will need a Keytab file.
Keytab file contains keys which are required by kerberos module to decrypt the incoming kerberos token.
Keytab file is not out of the box present as it is specific to a user account in AD.It has to be generated by your system admin and give it to you.
You will need a service user (dedicated for your application). Generate keytab for it.
Copy it on your application server and specify its path in spring.
Check ktpass command on windows for more details about creating keytab.
You may also need to check for krb5 conf file, what it contains and how you can specify it inside Spring.

Unable to decrypt error using jasypt with spring boot

I am using spring boot:2.2.2.RELEASE when i tried to add jasypt functionality to hide my password i got the following error
Unable to decrypt: ENC(MyEncryptedPass). Decryption of Properties failed, make sure encryption/decryption passwords match
i used the command line to encrypt the password and decrypt it and it works fine so i am sure my encryption and decryption passwords are exact but i get this error when i try to launch my spring application. So any help (•–•)
As from version 3.0.0 of jasypt-spring-boot, the default encryption/decryption algorithm has changed to PBEWITHHMACSHA512ANDAES_256
The change can be found here: https://github.com/ulisesbocchio/jasypt-spring-boot#update-11242019-version-300-release-includes
To decrypt previously encrypted values, add these two values in your properties:
jasypt.encryptor.algorithm=PBEWithMD5AndDES
jasypt.encryptor.iv-generator-classname=org.jasypt.iv.NoIvGenerator
I was also facing the same issue. Initially, I was encrypting using jasypt CLI and putting the same value in the property file. But by default property of com.github.ulisesbocchio jar is different from CLI. Try to use the below code for encryption.
private static StringEncryptor stringEncryptor() {
PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
SimpleStringPBEConfig config = new SimpleStringPBEConfig();
config.setPassword(password);
config.setAlgorithm("PBEWithMD5AndDES");
config.setKeyObtentionIterations("1000");
config.setPoolSize("1");
config.setProviderName("SunJCE");
config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");
config.setIvGeneratorClassName("org.jasypt.iv.RandomIvGenerator");
config.setStringOutputType("base64");
encryptor.setConfig(config);
return encryptor;
}
private static String encrypt(String text) {
StringEncryptor textEncryptor = stringEncryptor();
String encryptedText = textEncryptor.encrypt(text);
return encryptedText;
}
private static String decrypt(String text) {
StringEncryptor textEncryptor = stringEncryptor();
String decryptedText = textEncryptor.decrypt(text);
return decryptedText;
}
public static void main(String[] args) {
System.out.println(encrypt("StackOverFlow"));
}
Ok the solution was to add this configuration to my project.properties file
jasypt.encryptor.iv-generator-classname=org.jasypt.iv.NoIvGenerator
cause probably i am using algorithm = PBEWithMD5AndDES which doesn't require initialization vector. But of course that's just my explanation which doesn't mean anything :''D.

How to use p12 client certificate with spring feign client

I have a Spring Boot application that calls a remote service.
This remote web service provided me a p12 file that should authenticate my application.
How do I configure my feign client to use the p12 certificate ?
I've tried settings these properties:
-Djavax.net.ssl.keyStore=path_to_cert.p12 -Djavax.net.ssl.keyStorePassword=xxx -Djavax.net.ssl.keyStoreType=PKCS12
But it doesn't change anything, I still get this error:
sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
I could finally manage to do it with a lot of blind trial and error.
The problem is, by default, the feign builder builds feign clients with null SSLSocketFactory:
org.springframework.cloud.openfeign.FeignClientsConfiguration#feignBuilder:
#Bean
#Scope("prototype")
#ConditionalOnMissingBean
public Feign.Builder feignBuilder(Retryer retryer) {
return Feign.builder().retryer(retryer);
}
feign.Feign.Builder:
public static class Builder {
// ...
private Client client = new Client.Default(null, null);
So, I had to define this bean in a #Configuration:
#Bean
#Profile({"prod", "docker"})
public Feign.Builder feignBuilder() {
return Feign.builder()
.retryer(Retryer.NEVER_RETRY)
.client(new Client.Default(getSSLSocketFactory(), null));
with this method: (can't remember source)
SSLSocketFactory getSSLSocketFactory() {
char[] allPassword = keyStorePassword.toCharArray();
SSLContext sslContext = null;
try {
sslContext = SSLContextBuilder
.create()
.setKeyStoreType(keyStoreType)
.loadKeyMaterial(ResourceUtils.getFile(keyStore), allPassword, allPassword)
.build();
} catch (Exception e) { /* *** */ }
return sslContext.getSocketFactory();
}
Now, it works for me, I debugged though the feign client calls and the sslSocketFactory is correctly passed to the underlying connection.
In case you wish to achieve the above effect programmatically without using keytool, you can do the following:
class CustomFeignConfiguration {
private val log = Logger.getLogger(this.javaClass.name)
#Value("\${client_p12_base64_encoded_string}")
private val clientP12: String = ""
#Value("\${client_p12_password}")
private val clientP12Pass: String = ""
#Bean
fun feignClient(): Client {
val sslSocketFactory= getSSLSocketFactory()
log.info("CUSTOM FEIGN CLIENT CALLED")
return Client.Default(sslSocketFactory, DefaultHostnameVerifier())
}
private fun getSSLSocketFactory(): SSLSocketFactory {
val decoder = java.util.Base64.getDecoder()
val p12 = decoder.decode(clientP12)
val p12File = File("clientCer.p12")
p12File.writeBytes(p12)
try {
val sslContext = SSLContexts
.custom()
.loadKeyMaterial(p12File, clientP12Pass.toCharArray(), clientP12Pass.toCharArray())
.build()
return sslContext.socketFactory
} catch (exception: Exception) {
throw RuntimeException(exception)
}
}
}
The FeignClient interface that is using the configuration has to load this specifically
#FeignClient(name = "client", configuration = [CustomFeignConfiguration::class], url = "\${url}")
interface Client {
....
....
}
The SSLContexts library can only use p12 certificates and we have to convert the certificates and keys in PEM format to the P12 format.
Create a p12 certificate from your PEM certificate and key using the following SSL command:
openssl pkcs12 -export -inkey domain.key -in domain.crt -out domain.p12
Please record the password that you enter after you run this command.
Convert this p12 certificate to a base64 string using the following command
base64 domain.p12 > domain.p12.base64
Convert this multiline string to a single line string using the following command:
tr -d "\n\r" < domain.p12.base64 > domain.p12.base64.singleline
Use the single line string from this command and the password that you recorded earlier in your application.properties.

How to diagnose/fix Signature Digest Verification Failure on web service response?

I am using wss4jSecurityInterceptor for validation and securement actions on my webservice. The SOAP messages back and forth are secured with Signature, Encryption and Timestamp. The problem I am having is, the recipient is unable to verify my response signature and gets the following error:
oracle.wsm.security.SecurityException: WSM-00061 : Signature digest verification failure. The system property xml.debug.verify should be enabled for the details about the digest calculations during verification phase (note xml.debug.verify slows down the signature verification for very large messages).
Here is the code that I am using to perform validation and securement actions:
#EnableWs
#Configuration
public class WebServiceConfig extends WsConfigurerAdapter {
private static Log log = LogFactory.getLog(WebServiceConfig.class);
#Bean
public KeyStoreCallbackHandler securityCallbackHandler() {
KeyStoreCallbackHandler callbackHandler = new KeyStoreCallbackHandler();
callbackHandler.setPrivateKeyPassword("changeit");
return callbackHandler;
}
#Bean
public Wss4jSecurityInterceptor securityInterceptor() throws Exception {
Wss4jSecurityInterceptor securityInterceptor = new Wss4jSecurityInterceptor();
// validate incoming request
securityInterceptor.setValidationActions("Timestamp Signature Encrypt");
securityInterceptor.setValidationSignatureCrypto(getCryptoFactoryBean().getObject());
securityInterceptor.setValidationDecryptionCrypto(getCryptoFactoryBean().getObject());
securityInterceptor.setValidationCallbackHandler(securityCallbackHandler());
// encrypt the response
securityInterceptor.setSecurementEncryptionUser("client_pub"); // alias of client public key
securityInterceptor.setSecurementEncryptionParts("{Content}{}Body");
securityInterceptor.setSecurementEncryptionKeyIdentifier("DirectReference");
securityInterceptor.setSecurementEncryptionCrypto(getCryptoFactoryBean().getObject());
// sign the response
securityInterceptor.setSecurementSignatureAlgorithm(WSS4JConstants.RSA_SHA1);
securityInterceptor.setSecurementSignatureDigestAlgorithm(WSS4JConstants.SHA1);
securityInterceptor.setSecurementSignatureKeyIdentifier("DirectReference");
securityInterceptor.setSecurementActions("Encrypt Signature Timestamp");
securityInterceptor.setSecurementUsername("secret"); // alias of the private key
securityInterceptor.setSecurementPassword("changeit");
securityInterceptor.setSecurementSignatureCrypto(getCryptoFactoryBean().getObject());
return securityInterceptor;
}
#Bean
public CryptoFactoryBean getCryptoFactoryBean() throws IOException {
CryptoFactoryBean cryptoFactoryBean = new CryptoFactoryBean();
cryptoFactoryBean.setKeyStorePassword("changeit");
cryptoFactoryBean.setKeyStoreLocation(new ClassPathResource("cert/myKeystore.jks"));
return cryptoFactoryBean;
}
#Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
try {
interceptors.add(securityInterceptor());
} catch (Exception e) {
throw new RuntimeException("could not initialize security interceptor");
}
}
#Bean
public ServletRegistrationBean<MessageDispatcherServlet> registerMessageDispatcherServlet(
ApplicationContext context) {
MessageDispatcherServlet messageDispatcherServlet = new MessageDispatcherServlet();
messageDispatcherServlet.setApplicationContext(context);
messageDispatcherServlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean<MessageDispatcherServlet>(messageDispatcherServlet, "/ws/*");
}
The receiving side suspects that the error is caused by the fact that the certificate sent as has been corrupted by carriage return character sequence. Here is what it looks like:
<wsse:BinarySecurityToken
EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"
ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"
wsu:Id="X509-124b3a03-73e3-4838-a254-f20883ff4184">MIIG1zCCBb+gAwIBAgIRAIVi4APh2tBtAAAAAFDvSXwwDQYJKoZIhvcNAQELBQAwgboxCzAJBgNV
BAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3Qu
 bmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0gZm9yIGF1
 dGhvcml6ZWQgdXNlIG9ubHkxLjAsBgNVBAMTJUVudHJ1c3QgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
dHkgLSBMMUswHhcNMTkwMzIyMTY1ODA5WhcNMjAwMzIyMTcyODA4WjCBgjELMAkGA1UEBhMCVVMx
 EzARBgNVBAgTCkNhbGlmb3JuaWExETAPBgNVBAcTCEJlcmtlbGV5MS4wLAYDVQQKEyVMYXdyZW5j
 ZSBCZXJrZWxleSBOYXRpb25hbCBMYWJvcmF0b3J5MRswGQYDVQQDExJtc2cudWNwLWl0LmxibC5n
b3YwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9fSfO1cKz1D34dQaQF1ScY0eEGALm
 s3hkRRWMYPWTH8gZRpfP4iFLF9Sx09vagGFHUrru1xf8BnVyB2CsZnvHtE+bDrK4kp4hQpO8aXqf
 VEpn+SpY38aQDfZUqjRK6HJM5qxF/2xjNKSw41MPkXALK04yga07IwSUE+GpPt8i2noQPKKJufzf
guA8Plmsnf9uNcpxFezNXC1k+HQBKeO0V+CZ4K5FKyckq64eT8VWbawF5CFoNigtbxTuHHClECV0
 VrzNNf5jj/cpymwOu51ljnPhGEY/f73DlEBZg8jpFcDSnAyYyJH2GFgz2wa0TiOpdxItTWHMXalb
 YtDH9VvRAgMBAAGjggMMMIIDCDAdBgNVHREEFjAUghJtc2cudWNwLWl0LmxibC5nb3YwggF/Bgor
BgEEAdZ5AgQCBIIBbwSCAWsBaQB2AFWB1MIWkDYBSuoLm1c8U/DA5Dh4cCUIFy+jqh0HE9MMAAAB
 aaZyHvYAAAQDAEcwRQIhAJtC0LBFOfupRDy6BOvRrH6pAlvAJFFUWxMbbSRV59nOAiB/c2D5VOwS
 aBeC4czjDjUAAfeunN/npVLyX7i6v69aLgB3AId1v+dZfPiMQ5lfvfNu/1aNR1Y2/0q1YMG06v9e
oIMPAAABaaZyHwQAAAQDAEgwRgIhAJj/g/g+UjWaoHCl7ldHG08zuUrL8XbAkR8Op4G+//nvAiEA
 sLoq29TrwnOtWa+O1+PRHCCllKD22yeKxdMUNrR2Pu0AdgCyHgXMi6LNiiBOh2b5K7mKJSBna9r6
 cOeySVMt74uQXgAAAWmmch7YAAAEAwBHMEUCIBVGxbR2c/ByOtfDRMlpIFPRYA5+dGkB4zJ7tjQ6
moYYAiEAmFbeJdg9rCZt6qUqhRH2mxJlotRjp+mZmiHyRFmulykwDgYDVR0PAQH/BAQDAgWgMB0G
 A1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3Js
 LmVudHJ1c3QubmV0L2xldmVsMWsuY3JsMEsGA1UdIAREMEIwNgYKYIZIAYb6bAoBBTAoMCYGCCsG
AQUFBwIBFhpodHRwOi8vd3d3LmVudHJ1c3QubmV0L3JwYTAIBgZngQwBAgIwaAYIKwYBBQUHAQEE
 XDBaMCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5lbnRydXN0Lm5ldDAzBggrBgEFBQcwAoYnaHR0
 cDovL2FpYS5lbnRydXN0Lm5ldC9sMWstY2hhaW4yNTYuY2VyMB8GA1UdIwQYMBaAFIKicHTdvFM/
z3vU981/p2DGCky/MB0GA1UdDgQWBBRzakStBRG3O6NBdLt1XUkvwwKn4jAJBgNVHRMEAjAAMA0G
 CSqGSIb3DQEBCwUAA4IBAQAqt00w3oV4XgcdhcanJdgY/SUCtmN9I4jdras8vRson+wK6WJy6Em8
 EF5wyRDLOwUD75Rm9Ak23lKjYOcDTWQGG3YXrYh7xe3J8C+plEY1NAwNPXflCzxkDcdJ4pX0LDfj
aC5FniAvKJ9ET7ZQvpCjBRd7wgOrkuhf3YjCFC3hM4E6D7cGb6DEhCh9nOy+e9woQ/C1LnDil3NX
 Vgqk3PMZYkUeHjxqItnDcOAeJaeqsAUiTxZbC8sQQ9ae/CPahghgSWEwL7tMrAH4nGT3yeBQl0I9
 O7H9xWj0ooQ8Wt6nO7pq64X5uc7W6iA3/1Of5uCntmMrsqPlvSscP6QVuPw6</wsse:BinarySecurityToken>
My keystore is in PKCS format and I have also converted to jks. No dice. The certs in the keystore verify just fine with openssl command, so I know they are good.
I do my builds on a mac and deploy on CentOS. I have also copied the sources to CentOS and run the build there. Again, made no difference.
Has anyone run into this issue and solved it? Would appreciate any help!
Thanks much!
xmlsec library upgraded to 2.1.3.
wss4j-ws-security-dom upgraded to 2.2.3
Details: The problem was narrow and very obscure. I haven't put everything I did back to the way it was to see if the problem returns, but could do that easily. We use spring-ws-security and WSS4Jfrom apache to perform the validation and securement actions. Both libraries include xmlsec.jar which provides this functionality. In fact wss4j includes it twice since it also embeds opensaml , which in turn contains xmlsec. Spring-ws-security also contains its own xmlsec. They are all different versions. The problem with carriage returns is reported for xmlsec versions 2.1.0 and above.
https://issues.apache.org/jira/browse/SANTUARIO-482
https://issues.apache.org/jira/browse/SANTUARIO-494
I updated wss4j to ver 2.2.3 (where this is ostensibly fixed) and also added the fixed version of xmlsec.jar directly for good measure.
Hope it helps someone else out. :-)

Problemns connecting Spring to Active Directory

I'm trying to connect to my company's active diretory to create an appplication capable of adding, updating and removing users from it, I configured may LdapContextSource with all the information my collegue who created de AD environment but when I try to do a simple search it doesn't work and give me this error:
org.springframework.ldap.AuthenticationNotSupportedException: [LDAP: error code 8 - BindSimple: Transport encryption required.]; nested exception is javax.naming.AuthenticationNotSupportedException: [LDAP: error code 8 - BindSimple: Transport encryption required.]
Here is my simple code:
public User getUserByLogin(String login) {
LdapContextSource contextSource = new LdapContextSource();
try {
contextSource.setUrl("secret");
contextSource.setBase("secret");
contextSource.setUserDn("secret");
contextSource.setPassword("secret");
contextSource.afterPropertiesSet();
LdapTemplate ldapTemplate = new LdapTemplate(contextSource);
User user = ldapTemplate.findOne(query().where("uid").is(login), User.class);
return user;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}
}
May be relevant to let you guys to know that we're using GSS-API
Thanks in advance, hope someone can help me
The error indicates that transport encryption is required -- this generally means you need to change the LDAP server URI from ldap://server.domain.gTLD to ldaps://server.domain.gTLD but since few LDAP servers use certs issued from a public certificate authority (CA), you'll also need to set up the certificate trust. I use a private CA to sign all certificates, so can import the CA public key into the Java truststore.

Resources