Spring-MVC : Encryption/Decryption with user password - encryption

I am working on a Spring-MVC application which uses Hibernate as the
ORM and PostgreSQL as the database, in which I am looking for
on-the-fly encryption decryption solution, but only for 2 columns in
the database, the rest all can stay non-encrypted. I have a Person
entity, which has a password and I am encrypting the password with
BCrypt and saving them in database. I understand once the password is BCrypt encrypted I cannot decrypt it. I am planning to put an intermediate page temporarily where I would request the password once again and save it in some format which I can use for on the fly encryption-decryption.
If possible, I would like to use this password to encrypt/decrypt
those 2 columns once the user logs in and does action on those 2
columns.
As I am using Spring-Security too, I am injecting the encoder bean so
Spring-Security can login the user. Here is how I am saving the
password and my security-application-context. As I am just starting
with this problem, not that much progress to paste here :
Person model :
#Entity
#Table(name="person")
public class Person implements UserDetails{
#Id
#Column(name="id")
#GeneratedValue(strategy = GenerationType.SEQUENCE,generator = "person_seq_gen")
#SequenceGenerator(name = "person_seq_gen",sequenceName = "person_seq")
private int id;
#Valid
#Email
#Pattern(regexp = emailRegexp)
#Column(name = "username")
private String username;
#Valid
#NotEmpty(message = "Password may not be empty")
#Column(name = "password")
private String password;
// getters and setters ommitted }
PersonServiceImpl :
#Override
#Transactional
public boolean addPerson(Person p) {
Person existingUser = personDAO.findPersonByUsername(p.getUsername());
if(existingUser == null) {
this.personDAO.addPerson(p);
p.setAccountstatus(false);
p.setOnetimeemail(false);
p.setUsername(p.getUsername().toLowerCase());
// as you can see I am encrypting the password and saving in DB, I don't know how to access the plain password at this point to use in some algorithm for on-the-fly encryption/decryption
p.setPassword(BCrypt.hashpw(p.getPassword(), BCrypt.gensalt(11)));
p.setUsername(p.getUsername().toLowerCase());
this.personDAO.addPerson(p);
sendAccountActivationEmail(p.getUsername(), p.getFirstName());
return true;
} else {
return false;
}
}
Security-application-context.xml
<beans:bean id="encoder"
class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder">
<beans:constructor-arg name="strength" value="11" />
</beans:bean>
<beans:bean id="daoAuthenticationProvider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<beans:property name="userDetailsService" ref="LoginServiceImpl"/>
<beans:property name="passwordEncoder" ref="encoder"/>
</beans:bean>
Any pointers, help would be nice. If there is anything unclear, kindly let me know. Thanks a lot.

Related

id_token_hint parameter failed signature validation when Using B2C to generate the metadata endpoints

I am attempting to set up a Magic link like system using Azure B2C. Using the following samples:
Primary:
https://github.com/azure-ad-b2c/samples/tree/master/policies/sign-in-with-magic-link
For sing B2C to generate the metadata endpoints:
https://github.com/azure-ad-b2c/samples/tree/master/policies/invite#using-b2c-to-generate-the-metadata-endpoints
As a note I believe I had it working at one point but after a clean up I have been getting the error:
The provided id_token_hint parameter failed signature validation. Please provide another token and try again.
The steps I took to set up is as follows:
Create a cert via powershell and get thumbprint to use in local code
Use certmng via MMC to export cert
All Task / Export / Next / Yes, Export the private key
Personal Information Exchange - PKCS (Include all cert in cert path)(Enable cert privacy)
Security (Password) Randomly Generated Pass 25 character password.
Name: id_token_hint_cert.pfx
Browse Azure / B2C / Identity Experience Framework / Policy keys
Add / Option: Upload / Name: IdTokenHintCert / File Upload id_token_hint_cert.pfx / Password: Password from setup 3
This is where I have tried 2 different set ups. The first was to setup a set of custom policies so that I could update the following claims provider to have issuer_secret set to B2C_1A_IdTokenHintCert
<ClaimsProvider>
<DisplayName>Token Issuer</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="JwtIssuer">
<DisplayName>JWT Issuer</DisplayName>
<Protocol Name="None" />
<OutputTokenFormat>JWT</OutputTokenFormat>
<Metadata>
<Item Key="client_id">{service:te}</Item>
<Item Key="issuer_refresh_token_user_identity_claim_type">objectId</Item>
<Item Key="SendTokenResponseBodyWithJsonNumbers">true</Item>
</Metadata>
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="B2C_1A_IdTokenHintCert" />
<Key Id="issuer_refresh_token_key" StorageReferenceId="B2C_1A_TokenEncryptionKeyContainer" />
</CryptographicKeys>
<InputClaims />
<OutputClaims />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
This is set of policies grabbed from https://github.com/Azure-Samples/active-directory-b2c-custom-policy-starterpack/tree/master/LocalAccounts and updated to my tenant but left mostly alone.
I also tried changing out the issuer_secret in my main custom policies with the same error being output.
Heading into my code:
This is the important part of my startup:
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(
OpenIdConnectDefaults.AuthenticationScheme
).AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAdB2C"),"OpenIdConnect", "Cookies",true);
services.AddControllersWithViews();
services.AddRazorPages()
.AddMicrosoftIdentityUI();
services.AddTransient<IClaimsTransformation, AppClaimsTransformations>();
}
And here is my Home Controller where I submit a form, create the token and link and then just redirect to that link using it as a run now endpoint. (I know this wont end of working but need to get the signature validate before I can move on.)
public class HomeController : Controller
{
private static Lazy<X509SigningCredentials> SigningCredentials;
private readonly AppSettingsModel _appSettings;
private readonly IWebHostEnvironment HostingEnvironment;
private readonly ILogger<HomeController> _logger;
// Sample: Inject an instance of an AppSettingsModel class into the constructor of the consuming class,
// and let dependency injection handle the rest
public HomeController(ILogger<HomeController> logger, IOptions<AppSettingsModel> appSettings, IWebHostEnvironment hostingEnvironment)
{
_appSettings = appSettings.Value;
this.HostingEnvironment = hostingEnvironment;
this._logger = logger;
// Sample: Load the certificate with a private key (must be pfx file)
SigningCredentials = new Lazy<X509SigningCredentials>(() =>
{
X509Store certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
certStore.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certCollection = certStore.Certificates.Find(
X509FindType.FindByThumbprint,
"***************************************",
false);
// Get the first cert with the thumb-print
if (certCollection.Count > 0)
{
return new X509SigningCredentials(certCollection[0]);
}
throw new Exception("Certificate not found");
});
}
[HttpGet]
public ActionResult Index(string Name, string email, string phone)
{
if (string.IsNullOrEmpty(email))
{
ViewData["Message"] = "";
return View();
}
string token = BuildIdToken(Name, email, phone);
string link = BuildUrl(token);
return Redirect(link);
}
private string BuildIdToken(string Name, string email, string phone)
{
string issuer = $"{this.Request.Scheme}://{this.Request.Host}{this.Request.PathBase.Value}/";
// All parameters send to Azure AD B2C needs to be sent as claims
IList<System.Security.Claims.Claim> claims = new List<System.Security.Claims.Claim>();
claims.Add(new System.Security.Claims.Claim("name", Name, System.Security.Claims.ClaimValueTypes.String, issuer));
claims.Add(new System.Security.Claims.Claim("email", email, System.Security.Claims.ClaimValueTypes.String, issuer));
if (!string.IsNullOrEmpty(phone))
{
claims.Add(new System.Security.Claims.Claim("phone", phone, System.Security.Claims.ClaimValueTypes.String, issuer));
}
// Create the token
JwtSecurityToken token = new JwtSecurityToken(
issuer,
"******************************************",
claims,
DateTime.Now,
DateTime.Now.AddDays(7),
HomeController.SigningCredentials.Value);
// Get the representation of the signed token
JwtSecurityTokenHandler jwtHandler = new JwtSecurityTokenHandler();
return jwtHandler.WriteToken(token);
}
private string BuildUrl(string token)
{
string nonce = Guid.NewGuid().ToString("n");
return string.Format("https://{0}.b2clogin.com/{0}.onmicrosoft.com/{1}/oauth2/v2.0/authorize?client_id={2}&nonce={4}&redirect_uri={3}&scope=openid&response_type=id_token",
"myTenant",
"B2C_1A_SIGNIN_WITH_EMAIL",
"************************************",
Uri.EscapeDataString("https://jwt.ms"),
nonce)
+ "&id_token_hint=" + token;
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
}
Location Location Location.
I was adjusting the base profile which I learned I should not be doing. When I applied my change to the extension file instead everything starting working properly.

Spring Security SAML Extension and #PreAuthorize

My requirements are to use SAML based SSO. Retrieve the user groups from SAML assertions and secure the rest api endpoints. I am using Spring security SAML extension and Spring MVC. The steps I have taken are.
Configure the application for SP using Spring SAML extension. [Done]
Retrieve assertions and assign roles [Done]
Create rest endpoint. [Done]
Secure rest endpoint and services based on roles. [Not working]
I have implemented SAMLUserDetailsService which returns a UserDetails object with authorities. 'loadUserBySAML' below.
#Override
public Object loadUserBySAML(SAMLCredential credential) throws UsernameNotFoundException {
final String userId = credential.getNameID().getValue();
final String emailAddress = credential.getAttributeAsString("EmailAddress");
final String firstName = credential.getAttributeAsString("FirstName");
final String lastName = credential.getAttributeAsString("LastName");
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ROLE_STUDENT"));
return new User(userId, emailAddress, firstName, lastName, authorities);
}
I have added <!-- Enable security annotations on methods -->
<security:global-method-security pre-post-annotations="enabled" />to the securityContext.xml.
On the RestController and on the services I am using #PreAuthorize but this annotation seems to have no effect at all.
#PreAuthorize("hasRole('ROLE_PROGRAMLEAD')")
#RequestMapping(method = RequestMethod.GET)
public String hello() {
return "Hello.";}
Could someone please help me understand why the PreAuthorize is not firing? Am I missing some configuration?
I was facing the same issue. I wanted to perform API authorisation with SAML authentication. For it to work, you need to use the hasAuthority param with the annotation instead of the hasRole param.
The following worked for me:
#PreAuthorize(value="hasAuthority('Admin')")
The <security:global-method-security pre-post-annotations="enabled" /> needs to go in the servlet xml of the rest controller and not the security context xml.
Reference:
http://docs.spring.io/spring-security/site/faq/faq.html#faq-method-security-in-web-context

how to authenticate user in spring security with password encryption

i m using spring security in my application.in my database .the password is in encrypted form.so in login when i m sending the password,that password should be converted to the encrypted form then i should be able to compare the password which i m sending and the password which is present in the database.if it matches,successful login should occur.
this is my spring-security.xml
<authentication-manager">
<authentication-provider >
<password-encoder ref="encoder"/>
<jdbc-user-service data-source-ref="dataSource"
users-by-username-query="select email,password from user where email=?"
/>
</authentication-provider>
</authentication-manager>
<beans:bean id="encoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
and here is my user table which contains email,password,contactno,address.
any help how can i check the encrypted password value for the password which user entering and how to check whether it matechs or not?
You can write your own PasswordEncoder to transfer you user to spring secure.
#Component("PasswordEncoder")
public class PasswordEncoderimpl implements PasswordEncoder{
#Override
public String encodePassword(String rawPass, Object salt) {
//it is the algorithm the transfer password to encrypted password
}
#Override
public boolean isPasswordValid(String encPass, String rawPass, Object salt) {
//encPass is the password in your database
//rawPass is the password user entering
//then you can write it like
return encPass.euqals(encodePassword(rawPass));
}
}
Then your spring-security.xml will be:
<authentication-manager>
<authentication-provider>
<password-encoder ref="PasswordEncoder">
</password-encoder>
</authentication-provider>
</authentication-manager>

Getting a page according to the value passed in spring controller

I am trying to develop a network monitoring application in java using spring framework. It lists all the resources in the network. By clicking at one of the resource ids the page must redirect to a new page which displays all details of that resource. This way the next page changes with new contents according to the resource id we clicked.
Now I just connected the next page by mapping the page in controller, but the page is empty, i.e. the value is not passed to the controller correctly.
My controller:
#RequestMapping(value = "/deviceInformation", method = RequestMethod.GET)
public String deviceinfo(ModelMap model, HttpServletRequest request) {
List<NetworkInterface> list = inventoryService.getNetworkInterfaces("");
List<NetworkService> list2 = inventoryService.getNetworkServices("ipResource.discoveredName");
List<Equipment> list3 = inventoryService.getEquipments("resourceId");
List<Processor> list4 = inventoryService.getProcessors("hostId");
List<SoftwareRunning> list5 = inventoryService.getSoftwaresRunning("hostId");
List<StorageDisk> list6 = inventoryService.getStorageDisks("hostId");
List<StorageMemory> list7 = inventoryService.getStorageMemories("hostId");
model.addAttribute("INlist", list);
model.addAttribute("Serlist", list2);
model.addAttribute("Eqlist", list3);
model.addAttribute("Prolist", list4);
model.addAttribute("SRlist", list5);
model.addAttribute("Storlist", list6);
model.addAttribute("StoMry", list7);
return "inventory/deviceInformation";
}
#RequestMapping(value = "/interfaceList/{ipResource.discoveredName}", method = RequestMethod.POST)
public ModelAndView getInterface(#PathVariable String resourceId, HttpServletRequest request,
HttpServletResponse response) {
System.out.println("Got request param: " + resourceId);
List<NetworkInterface> list = inventoryService.getNetworkInterfaces("resID");
List<NetworkService> list2 = inventoryService.getNetworkServices("ipResource.discoveredName");
List<Equipment> list3 = inventoryService.getEquipments("ipResource.discoveredName");
List<Processor> list4 = inventoryService.getProcessors("hostId");
List<SoftwareRunning> list5 = inventoryService.getSoftwaresRunning("hostId");
List<StorageDisk> list6 = inventoryService.getStorageDisks("hostId");
List<StorageMemory> list7 = inventoryService.getStorageMemories("hostId");
ModelAndView modelAndView = new ModelAndView("/inventory/deviceInformation");
modelAndView.addObject("INlist", list);
modelAndView.addObject("Serlist", list2);
modelAndView.addObject("Eqlist", list3);
modelAndView.addObject("Prolist", list4);
modelAndView.addObject("SRlist", list5);
modelAndView.addObject("Storlist", list6);
modelAndView.addObject("StoMry", list7);
return modelAndView;
}
here is a sample, which has a spring controller and two jsp files. This sample is just for helping purpose and it does not mean the best practice of using spring MVC.
Controller :
#RequestMapping(value="/getdevices")
public ModelAndView generateTable(){
ModelAndView mv = new ModelAndView("table");
List<Device> devices = new ArrayList<Device>();
devices.add(new Device(1,"Iphone5",500.00));
devices.add(new Device(1,"Iphone4s",450.00));
mv.addObject("devices", devices);
return mv;
}
#RequestMapping(value="/getdeviceinfo")
public ModelAndView getDeviceInfo(#RequestParam Integer id, #RequestParam String name,#RequestParam Double price){
ModelAndView mv = new ModelAndView("device");
Device device = new Device(id,name,price);
mv.addObject("device", device);
return mv;
}
table.jsp (generating list of devices with links to each individual device info page)
<ul>
<c:forEach var="device" items="${devices}">
<li>${device.name}</li>
</c:forEach>
</ul>
device.jsp (info of individual device)
<body>
${device.name}
</body>
device.java (simple pojo)
public Integer id;
private String name;
private Double price;
public Device(Integer id, String name, Double price) {
super();
this.id = id;
this.name = name;
this.price = price;
}
note: I've noticed that in your latest code, you have set your View as "/inventory/deviceInformation". I'm not sure what your web app structure looks like. The value of View relies on how your structured jsp files under web root as well as prefix of spring view resolver.
For instance, in my testing spring web app, I have table.jsp and device.jsp right under /webapp/WEB-INF/views and spring config is like
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
so as you can see, in the controller above, there is no leading slash in the View, e.g. new ModelAndView("table.jsp)

ReportViewer - "Unable to serialize the session state. in 'stateserver' and 'sqlserver' mode

I am using VS2010 Report Viewer control in web application. The applications sessionstate mode is set to StateServer as follows
<sessionState timeout="30" mode="StateServer" stateConnectionString="tcpip=127.0.0.1:42424" sqlConnectionString="data source=127.0.0.1;Trusted_Connection=yes" cookieless="false" />
The reportviewer control is working fine on my devlopment machine but when the applicaiton is deployed onto server and when the reportviewer control page is loaded the following error is thrown.. All the other pages are working fine.
"Unable to serialize the session state. In 'StateServer' and 'SQLServer' mode, ASP.NET will serialize the session state objects, and as a result non-serializable objects or MarshalByRef objects are not permitted. The same restriction applies if similar serialization is done by the custom session state store in 'Custom' mode."
Can anyone please help, any idea will be of great help..
Thanks in advance.
rptvw.ProcessingMode = ProcessingMode.Remote;
rptvw.ServerReport.ReportServerUrl = new Uri("http://localhost:90/reportserver");
rptvw.ServerReport.ReportPath = string.Format("/Reports/{0}", reportName);
var param = new ReportParameter[4];
param[0] = new ReportParameter("Parameter1", DropDownListCodes.SelectedValue));
param[1] = new ReportParameter("Parameter2", DropDownListQuarters.SelectedValue));
param[2] = new ReportParameter("Parameter3", DropDownListComparators.SelectedValue));
param[3] = new ReportParameter("Parameter4", comptype);
rptvw.ServerReport.SetParameters(param);
rptvw.ServerReport.Refresh();
I managed to get it to work.
I followed this link for my solution msdn link
"When implementing the IReportServerCredentials interface, it is important know that the ReportViewer control stores the instance of the object in ASP.NET session. If the server's ASP.NET session is being stored out of process, such as in Reporting Services, the class must be marked Serializable so that it may be serialized for storage." taken from above link.
Created a new file in App_Code\ReportServerConnection.cs
[Serializable]
public sealed class ReportServerConnection : IReportServerConnection2
{
public bool GetFormsCredentials(out Cookie authCookie, out string userName, out string password, out string authority)
{
authCookie = null;
userName = null;
password = null;
authority = null;
// Not using form credentials
return false;
}
public WindowsIdentity ImpersonationUser
{
// Use the default Windows user. Credentials will be
// provided by the NetworkCredentials property.
get { return null; }
}
public ICredentials NetworkCredentials
{
get
{
// Read the user information from the web.config file. By reading the information on demand instead of
// storing it, the credentials will not be stored in session, reducing the vulnerable surface area to the
// web.config file, which can be secured with an ACL.
// User name
string userName = ConfigurationManager.AppSettings["ReportViewerUser"];
if (string.IsNullOrEmpty(userName))
throw new InvalidOperationException("Please specify the user name in the project's Web.config file.");
// Password
string password = ConfigurationManager.AppSettings["ReportViewerPassword"];
if (string.IsNullOrEmpty(password))
throw new InvalidOperationException("Please specify the password in the project's Web.config file");
// Domain
string domain = ConfigurationManager.AppSettings["ReportViewerDomain"];
if (string.IsNullOrEmpty(domain))
throw new InvalidOperationException("Please specify the domain in the project's Web.config file");
return new NetworkCredential(userName, password, domain);
}
}
public Uri ReportServerUrl
{
get
{
string url = ConfigurationManager.AppSettings["ReportServerUrl"];
if (string.IsNullOrEmpty(url))
throw new InvalidOperationException("Please specify the report server URL in the project's Web.config file");
return new Uri(url);
}
}
public int Timeout
{
// set timeout to 60 seconds
get { return 60000; }
}
public IEnumerable<Cookie> Cookies
{
// No custom cookies
get { return null; }
}
public IEnumerable<string> Headers
{
// No custom headers
get { return null; }
}
}
On the Report.aspx.cs page
protected void Page_Init(object sender, EventArgs e)
{
rptvw.ServerReport.ReportServerCredentials = new ReportServerConnection();
}
Changed this line in the code on the main post
rptvw.ServerReport.ReportServerUrl = rsc.ReportServerUrl;
And in the Web.config
<appSettings>
<add key="ReportViewerServerConnection" value=" App_Code.ReportServerConnection, App_Code"/>
<add key="ReportViewerUser" value="username"/>
<!-- Used as the user name by the ReportServerConnection class. -->
<add key="ReportViewerPassword" value="password"/>
<!-- Used as the password by the ReportServerConnection class. -->
<add key="ReportViewerDomain" value="domainname"/>
<!-- Used as the domain by the ReportServerConnection class. -->
<add key="ReportServerUrl" value="http://localhost:90/reportserver"/>
<!-- Used as the report server URL by the ReportServerConnection class. -->

Resources