Can I decrypt password using the private key generated in Django backend in Flutter? - django

I am making a password manager app where I am using asymmetric encryption to store passwords. I have created a Django backend for the app where I have stored the user's public key in the User model. During registration, I am storing the public key in the database and sending the user their private key to the Flutter app.
I am using the following function to encrypt the password and store it in the password store model:
def encrypt(data, key):
key = serialization.load_pem_public_key(
key.encode(),
backend=default_backend()
)
data = key.encrypt(
data.encode(),
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
return data
Now, I want to know if I can decrypt passwords or any other string data in Flutter using the private key that is generated in the Django backend. If yes, how can I do it?
Also, I don't want to pass the decrypted password from the backend to the Flutter app. I want the client-side to handle it. For this, I am storing the private key in the user's device using shared preferences in Flutter:
SharedPreferences.getInstance().then((prefs) {
prefs.setString("privateKey", widget.privateKey);
});
Can anyone help me with this?
I tried using the decryptByPrivateKey function below to decrypt a password on the client side in my Flutter app using a private key generated on my Django backend:
dynamic decryptByPrivateKey(String content) async {
final prefs = await SharedPreferences.getInstance();
final private = prefs.getString('privateKey')!;
RSAKeyParser parser = RSAKeyParser();
RSAPrivateKey privateKey = parser.parse(private) as RSAPrivateKey;
AsymmetricBlockCipher cipher = PKCS1Encoding(RSAEngine());
cipher..init(false, PrivateKeyParameter<RSAPrivateKey>(privateKey));
return utf8.decode(cipher.process(Encrypted.fromBase64(content).bytes));
}
However, I got the following error related to the content parameter, and I'm not sure how to resolve it:
FormatException: Invalid character (at character 2) b'\rV\x86ldn\x171*^c\xabsAc;\xead\xea\x05C\x996o\x0e#\xe2\xed\x1c\xa21\x80d... ^
Also, I am generating and sending the private key to the client as shown in the code snippet below:
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend()
)
public_key = private_key.public_key()
public_key = public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
)
private_key = private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption()
)
private_key = private_key.decode('utf-8')
public_key = public_key.decode('utf-8')
I am hoping to be able to decrypt the password on the client side using the private key without passing the decrypted password from the backend. Can anyone help me with this issue?"

Related

Establish SSO/set cookies with access or id token/token exchange

I'm allowing users logged in an external application to jump into our application with their access token through Keycloak's identity brokering and external to internal token exchange.
Now I'd like to establish an SSO session in an embedded JxBrowser in our application similar to a regular browser login flow, where three cookies are set in the browser: AUTH_SESSION, KEYCLOAK_SESSION(_LEGACY) and KEYCLOAK_IDENTITY(_LEGACY).
KEYCLOAK_IDENTITY contains a token of type Serialized-ID which looks somewhat similar to an ID token.
Is it possible to create the KEYCLOAK_IDENTITY cookie using the exchanged (internal) access and/or ID token and, provided that the other two cookies are correctly created as well, would this establish a valid SSO session?
Basically all I am missing is how I could obtain or create the Serialized-ID type token.
One way to achieve this:
Implement a custom endpoint following this example
Note that the provider works fine for me without registering it in standalone.xml, I'm just adding the JAR to the Keycloak Docker image.
Add a method that validates a given access token, looks up the user, gets the user session and sets the cookies in the response (most error handling omitted for brevity):
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path("sso")
public Response sso(#Context final HttpRequest request) {
final HttpHeaders headers = request.getHttpHeaders();
final String authorization = headers.getHeaderString(HttpHeaders.AUTHORIZATION);
final String[] value = authorization.split(" ");
final String accessToken = value[1];
final AccessToken token = Tokens.getAccessToken(accessToken, keycloakSession);
if (token == null) {
throw new ErrorResponseException(Errors.INVALID_TOKEN, "Invalid access token", Status.UNAUTHORIZED);
}
final RealmModel realm = keycloakSession.getContext().getRealm();
final UriInfo uriInfo = keycloakSession.getContext().getUri();
final ClientConnection clientConnection = keycloakSession.getContext().getConnection();
final UserModel user = keycloakSession.users().getUserById(token.getSubject(), realm);
final UserSessionModel userSession = keycloakSession.sessions().getUserSession(realm, token.getSessionState());
AuthenticationManager.createLoginCookie(keycloakSession, realm, user, userSession, uriInfo, clientConnection);
return Response.noContent().build();
}
Disclaimer: I am not completely certain this implementation does not imply any security issues, but since Tokens.getAccessToken(accessToken, keycloakSession) does full validation of the access token, setting the cookies should only be possible with a valid access token.
For CORS, add:
#OPTIONS
#Produces(MediaType.APPLICATION_JSON)
#Path("sso")
public Response preflight(#Context final HttpRequest request) {
return Cors.add(request, Response.ok("", MediaType.APPLICATION_JSON))
.auth()
.preflight()
.allowedMethods("GET", "OPTIONS")
.build();
}
and in sso():
return Cors.add(request, Response.ok("", MediaType.APPLICATION_JSON))
.auth()
.allowedMethods("GET")
.allowedOrigins(token)
.build();
What I am uncertain about is why Firefox preflights the GET request, making it necessary to handle that.

Aws Cognito: Secret hash with Java SDK (not Android) and ADMIN_NO_SRP_AUTH flow

i try to register a user in my amazon cognito user pool with username and password from my java backend but i always get the error:
Unable to verify secret hash for client
in the documentation i don't found any information how to pass the clientSecret in the register request and i don't like to create an (backend) app without a clientSecret.
My code looks like this
identityProvider = AWSCognitoIdentityProviderClientBuilder.standard().withCredentials(new AWSStaticCredentialsProvider(awsCreds)).withRegion(Regions.EU_CENTRAL_1).build();
Map<String, String> authParameters = new HashMap<>();
authParameters.put("USERNAME", "username");
authParameters.put("PASSWORD", "password");
authParameters.put("SECRET_HASH", "secret copy and paste from the aws console"); // i read in a forum post, that this should work
AdminInitiateAuthRequest authRequest = new AdminInitiateAuthRequest();
authRequest.withAuthFlow(AuthFlowType.ADMIN_NO_SRP_AUTH);
authRequest.setAuthParameters(authParameters);
authRequest.setClientId("clientId");
authRequest.setUserPoolId("userPoolId");
AdminInitiateAuthResult authResponse = identityProvider.adminInitiateAuth(authRequest);
Thanks
Marcel
To register users you should use the SignUp API. The secret hash can be calculated as follows in Java:
public String calculateSecretHash(String userPoolclientId, String userPoolclientSecret, String userName) {
if (userPoolclientSecret == null) {
return null;
}
SecretKeySpec signingKey = new SecretKeySpec(
userPoolclientSecret.getBytes(StandardCharsets.UTF_8),
HMAC_SHA256_ALGORITHM);
try {
Mac mac = Mac.getInstance(HMAC_SHA256_ALGORITHM);
mac.init(signingKey);
mac.update(userName.getBytes(StandardCharsets.UTF_8));
byte[] rawHmac = mac.doFinal(userPoolclientId.getBytes(StandardCharsets.UTF_8));
return Encoding.encodeBase64(rawHmac);
} catch (Exception e) {
throw new RuntimeException("Error while calculating ");
}
}
Can you please elaborate your use case of creating users from your backend instead of directly calling Amazon Cognito from your clients?
Edit: We have updated our documentation to include a section about how to compute the secret hash.
The following code works perfectly:
AdminInitiateAuthRequest adminInitiateAuthRequest = new AdminInitiateAuthRequest().withAuthFlow(AuthFlowType.ADMIN_NO_SRP_AUTH).withClientId("<ID of your client application>").withUserPoolId("<your user pool ID>")
.addAuthParametersEntry("USERNAME", "<your user>").addAuthParametersEntry("PASSWORD", "<your password for the user>");
AdminInitiateAuthResult adminInitiateAuth = identityProvider.adminInitiateAuth(adminInitiateAuthRequest);
System.out.println(adminInitiateAuth.getAuthenticationResult().getIdToken());

Web Application with Charm Crypto

I suppose to make a web application where the users can login in this platform with username and password (I want to make a MySQL database to stare username and password).
After when the user is logged, he selects a file from his computer and send this file on server.
I want encrypt this file to a group of users (I want use HybridABE cryptography with Charm Crypto).
Now I have these architectural/programming question.
Suppose that we have this program:
from charm.toolbox.pairinggroup import PairingGroup,GT
from charm.adapters.abenc_adapt_hybrid import HybridABEnc as HybridABEnc
from charm.schemes.abenc.abenc_waters09 import CPabe09
group = PairingGroup('SS512')
cpabe = CPabe09(group)
hyb_abe = HybridABEnc(cpabe, group)
policy = '((ONE or THREE) and (TWO or FOUR))'
msg = "hello world this is an important message."
(master_secret_key, master_public_key) = hyb_abe.setup()
attr_list = ['THREE', 'ONE', 'TWO']
secret_key = hyb_abe.keygen(master_public_key, master_secret_key, attr_list)
cipher_text = hyb_abe.encrypt(master_public_key, msg, policy)
decrypted_msg = hyb_abe.decrypt(master_public_key, secret_key, cipher_text)
Where can I save the Master Private Key and the Master Public Key ? On a directory server like file ? On database ?
Where can I save the secret key of user ?
An Attribute-based Encryption system is usually created once and has only one master secret key and public key pair.
The master secret key is stored on the server that generates the user secret keys. Since there is usually only one master secret key, you can even generate it and put it into the source code of your server code. Of course, you can include it in the server database.
User secret keys have to be given to users. Remember to give your users some kind of (public) identifier along with the user secret key so that you can manage the list of attributes that a certain user has at the server-side. Otherwise, you will have a headache when you try to update attributes, because you will need to contact users with their new user secret key.
The master public key (usually called "public parameters" or simply "public key") is public. It's a good idea to include it in the package that you give to your users. You can also create an API endpoint so that interested "users" can ask your server for the public key.

My webservice using apache shiro always return unauthorized

I have this shiro.ini:
[main]
ds = org.apache.shiro.jndi.JndiObjectFactory
ds.requiredType = javax.sql.DataSource
ds.resourceName = java:/comp/env/jdbc/myDS
# JDBC realm config
jdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm
jdbcRealm.permissionsLookupEnabled = true
jdbcRealm.authenticationQuery = "SELECT password FROM user WHERE username = ?"
jdbcRealm.dataSource = $ds
credentialsMatcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher
# base64 encoding, not hex in this example:
credentialsMatcher.storedCredentialsHexEncoded = false
credentialsMatcher.hashIterations = 1024
jdbcRealm.credentialsMatcher = $credentialsMatcher
[urls]
/logout = logout
/** = authcBasic
Im debbuging JndiRealm in doGetAuthenticationInfo. I get an exception when shiro try to execute my authenticationQuery in getPasswordForUser method. Those are the lines that execute the select:
ps = conn.prepareStatement(authenticationQuery);
ps.setString(1, username);
My atuthenticationQuery is "SELECT password FROM user WHERE username = ?" so trying to access position 1 is invalid since it starts from position 0. Is that a bug on JndiRealm from apache shiro or i wrote my sql wrong?
Looks like you have a simple mock implementation of a realm.
For logging in to work, you needs 2 steps:
authentication (is the username/password correct)
authorization (what is the user allowed to do)
Looks like you have only created the first step, but you are just giving back the password in the return statement.
Shiro will hash the password that was entered by the user. You should have the same hash stored somewhere in your database. In the doGetAuthenticationInfo you should do a lookup based on the username that was entered and retrieve the hash (either from the db, or disk or whatever you prefer), that is what you should put in the SimpleAuthenticationInfo object and return, shiro will do the user password hashing and comparison for you.
For the second step, override the method doGetAuthorizationInfo. You could let it return an instance of SimpleAuthorixationInfo containg a set of permissions, the simplest being "*", when it has access to everything.
Creating such a method can be as simple as:
#Override
public AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//here you can create custom permissions based on the principal
//that has been authenticated using your own logic
info.addStringPermission("*");
return info;
}

Where to get the credentials to use for Authentication.asmx?

For one of our FBA enabled SharePoint site, we need to access various web services. I know that we need to invoke Authentication.asmx before we make any other SP web service call.
How do I get the currently logged in user's username & password to pass to the Authentication.asmx service?
Thanks.
Update: I tried Marek's solution with a known username and password and got a 401 for Authentication.asmx. So probably some settings are off. The admin is looking into it.
MembershipUser user = Membership.GetUser();
string username = user.UserName;
string password = user.GetPassword();
Authentication auth = new Authentication();
auth.CookieContainer = new CookieContainer();
LoginResult result = auth.Login(username, password);
if (result.ErrorCode == LoginErrorCode.NoError)
{
CookieCollection cookies = auth.CookieContainer.GetCookies(new Uri(auth.Url));
Cookie authCookie = cookies[result.CookieName];
Lists lists = new Lists();
lists.CookieContainer = new CookieContainer();
lists.CookieContainer.Add(authCookie);
lists.GetListCollection();
}
However, depending on the settings of the membership provider (is password stored in plain text, encrypted or hashed? is it required to pass the security answer to get the password?) retrieving the password may be more difficult or even impossible and you will need to ask the user for it.