How do I form the SSH private key signature in JavaScript? - ssh

I am using an in-browser library, SSHy, for SSHing to devices. I'm currently working on adding support for publickey authentication, but I keep getting an error from the server about an invalid signature. I'm able to send the first SSH_MSG_USERAUTH_REQUEST without the signature and get back a SSH_MSG_USERAUTH_PK_OK. But when I send the next message with the signature, I always get a SSH_MSG_USERAUTH_FAILURE.
I'm doing the signing with another library (sshpk-browser) and forming the signature below using SSHy based on the SSH schema.
Can anyone see any potential issues with how I am forming the signature?
const decodedPublicKey = config.privateKey.toPublic().toString('ssh', { hashAlgo: 'sha512' }).split(' ')[1];
const publicKey = atob(decodedPublicKey);
var m = new SSHyClient.Message();
m.add_bytes(String.fromCharCode(SSHyClient.MSG_USERAUTH_REQUEST));
m.add_string(this.termUsername);
m.add_string('ssh-connection');
m.add_string('publickey');
m.add_boolean(true); // has signature
m.add_string('rsa-sha2-512'); // public key algorithm name
m.add_string(publicKey); // public key
// Create signature
var sigMsg = new SSHyClient.Message();
sigMsg.add_string(SSHyClient.kex.sessionId);
sigMsg.add_bytes(String.fromCharCode(SSHyClient.MSG_USERAUTH_REQUEST));
sigMsg.add_string(this.termUsername);
sigMsg.add_string('ssh-connection');
sigMsg.add_string('publickey');
sigMsg.add_boolean(true); // has signature
sigMsg.add_string('rsa-sha2-512');
sigMsg.add_string(publicKey);
const sigMsgString = sigMsg.toString();
// Sign signature
const sign = config.privateKey.createSign('sha512');
sign.update(sigMsgString);
const signature = sign.sign();
m.add_string(atob(signatureToString)); // signature
this.parceler.send(m);

Related

confused on validation of token on auth0 JWT java library

I read public/private key is so you can
create JWT token with private / public key
hand out your public key only to 3rd parties
3rd parties can now validate users JWT tokens via the public key
However, their example with private / public key requires the private key to validate which seems odd ->
String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCJ9.AbIJTDMFc7yUa5MhvcP03nJPyCPzZtQcGEp-zWfOkEE";
RSAPublicKey publicKey = //Get the key instance
RSAPrivateKey privateKey = //Get the key instance
try {
Algorithm algorithm = Algorithm.RSA256(publicKey, privateKey);
JWTVerifier verifier = JWT.require(algorithm)
.withIssuer("auth0")
.build(); //Reusable verifier instance
DecodedJWT jwt = verifier.verify(token);
} catch (JWTVerificationException exception){
//Invalid signature/claims
}
Is there no way to validate with just the public key?
On this line:
Algorithm algorithm = Algorithm.RSA256(publicKey, privateKey);
pass the privateKey as null. Private keys are for signing.
Algorithm algorithm = Algorithm.RSA256(publicKey, null);

Generate and validate session in node.js

What is a correct way to generate session and than validate it on every request?
Registration and initial authentication (user identity check for generating session) is handled by external service, so it's out of the question.
To simplify a question, what is the native secure way to generate and encrypt session with secret.
Requirements (alternatives is welcomed):
Session should be of two parts, one stored in cookies, second in database.
User check handled by server using database session part, cookies part and validate function.
Session generating and validating functions stored on server side and not accessible to user.
If database session part or functions is compromised, hacker couldn't make request pretending to be user. For this he will need to steal user cookies or session generate function and database session part.
Multiple device support with the same database session part.
JWT is not usable as logout is needed on server side (database session part will be deleted and all devices wouldn't be able to login with old cookies session part). User had some trust level that can change and it's will require JWT invalidation, so sessions is better choice.
I was thinking of using Crypto AES for this, but after asking "is it ok?" - answer was no, i'm not an expert in crypto, so i didn't fully understood a reason.
Here is my initial idea of implementation:
/**
* #param {string} data dummy
* #param {string} userKey from database or create new
* #return {object} {iv, key, encryptedData}
*/
function encrypt(data, userKey) {
let key = userKey ? Buffer.from(userKey, 'hex') : crypto.randomBytes(32)
let iv = crypto.randomBytes(16)
let cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(key), iv)
let encrypted = cipher.update(data)
encrypted = Buffer.concat([encrypted, cipher.final()])
return { iv: iv.toString('hex'), key: key.toString('hex'), encryptedData: encrypted.toString('hex') }
}
/**
* #param {string} iv
* #param {string} key
* #param {string} encryptedData
* #return {string} decrupted dummy data
*/
function decrypt(iv, key, encryptedData) {
try {
iv = Buffer.from(iv, 'hex')
key = Buffer.from(key, 'hex')
encryptedData = Buffer.from(encryptedData, 'hex')
let decipher = crypto.createDecipheriv('aes-256-cbc', key, iv)
let decrypted = decipher.update(encryptedData)
decrypted = Buffer.concat([decrypted, decipher.final()])
return decrypted.toString()
} catch (err) {
return false
}
}
A way to make it, is that user generate RSA and use crypto with public and private keys, but the public key must always be sent by the user('device')
'use strict';
const express = require('express');
const fs = require('fs');
const nodersa = require('node-rsa');
const bodyParser = require('body-parser');
let app = express();
app.use(bodyParser.urlencoded({ extended : false }));
app.use(bodyParser.json());
// req.body.value is public key, this never write in database or file
app.post('/', (req, res)=>{
let value = req.body.value;
const privateKey = fs.readFileSync('./store/privateKey.pem', 'utf8');
const original = new nodersa(privateKey).decrypt(value, 'utf8');
res.end(original);
});
app.listen(8000, ()=>{
console.log('on 8000');
});
if you use a public certificate authority with node, use aditional file called "certification file", node example here, this file is issue by C.A. you could work as C.A. and generarte this file, but It is recommended for closed systems, if you not need a gubernamental allow

Keyset does not exist /r /n

I was tasked to create a integration service between our SharePoint app and one service provider. One requirement of the service provider I'm going to integrate with is to provide them a public key which they will use to verify my request which was signed using our own private key.
Initially I created a console app which reads the certificate store and gets the private key which to use to sign my request and all. The console app works fine so I decided to move it now within our SharePoint application. Unfortunately it fails in this specific part of the code:
key.FromXmlString(privateCert.PrivateKey.ToXmlString(true));
The whole code snippet which gets the certificate and does the signing can be found below:
X509Certificate2 privateCert = null;
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.MaxAllowed);
var certs = store.Certificates.Find(X509FindType.FindByThumbprint, "thumbprinthere", true);
if (certs.Count > 0)
{
privateCert = certs[0];
}
RSACryptoServiceProvider key = new RSACryptoServiceProvider();
key.FromXmlString(privateCert.PrivateKey.ToXmlString(true));
byte[] sig = key.SignData(Encoding.ASCII.GetBytes(data), CryptoConfig.MapNameToOID("SHA256"));
string signature = Convert.ToBase64String(sig);
[UPDATE]
I tried following the steps in this link. I first uninstalled my existing private key in the server. I then imported it back to the Certificate store and confirmed that there was a Thumbprint property. After that, I ran findprivatekey.exe and was able to navigate to the MachineKeys folder. From there I added different users ranging from Network Services, IIS_IUSRS and even local accounts I used to login to the server as well as SPFarm admin but I still keep getting the error.
I also made sure that the key I added was exportable so there should be a way for it the application to extract the private key attached to the certificate.
[UPDATE 2]
I updated the code so that it just returns one certificate prior to assigning it to the variable I was using to extract the private key. Still the same issue even if I can see that the certs variable is returning exactly one record.
After much checking I realized I missed one important part in calling the method code block above. I forgot to wrap it an elevate privilege block. After doing that, the code functioned similarly as my console app.
SPSecurity.RunWithElevatedPrivileges(delegate())
{
...
X509Certificate2 privateCert = null;
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.MaxAllowed);
var certs = store.Certificates.Find(X509FindType.FindByThumbprint, "<thumbprinthere>", true);
if (certs.Count > 0)
{
privateCert = certs[0];
}
RSACryptoServiceProvider key = new RSACryptoServiceProvider();
key.FromXmlString(privateCert.PrivateKey.ToXmlString(true));
byte[] sig = key.SignData(Encoding.ASCII.GetBytes(data), CryptoConfig.MapNameToOID("SHA256"));
string signature = Convert.ToBase64String(sig);
...
});

S3 upload image file security issue

I'm reading the following tutorial:
https://devcenter.heroku.com/articles/s3-upload-node#uploading-directly-to-s3
The first step is when a user chooses an image
function s3_upload(){
var s3upload = new S3Upload({
file_dom_selector: '#files',
s3_sign_put_url: '/sign_s3',
onProgress: function(percent, message) {
// some code
},
onFinishS3Put: function(public_url) {
// some cde
},
onError: function(status) {
// somecode
}
});
}
Now the s3_sign_put_url refers to a server side function that returns
app.get('/sign_s3', function(req, res){
...
// calculates signature (signature variable)
// sets expiration time (expires variable)
var credentials = {
signed_request: url+"?AWSAccessKeyId="+AWS_ACCESS_KEY+"&Expires="+expires+"&Signature="+signature,
url: url
};
...
}
If I already calculated a signature as function of (AWS_SECRET_KEY) like this:
var signature = crypto.createHmac('sha1', AWS_SECRET_KEY).update(put_request).digest('base64');
signature = encodeURIComponent(signature.trim());
signature = signature.replace('%2B','+');
Question:
Why do I have to pass the AWS_SECRET_KEY value as part of the credentials object returned by s3_sign function? why isn't the signature enough to be returned? isn't this a security issue?
You aren't doing that.
The returned credentials contain the AWS_ACCESS_KEY, not the AWS_SECRET_KEY.
The access key is analogous to a username... it's needed by S3 so that it knows who created the signature. From this, S3 looks up the associated secret key internally, creates a signature for the request, and if it's the same signature as the one you generated and the supplied access key is associated with a user with permission to perform the operation, it succeeds.
The access key and secret key work as a pair, and one can't reasonably be derived from the other; the access key is not considered private, while the secret key is.

Windows 8 Metro RSA Encryption: AsymmetricKeyAlgorithmProvider ImportPublicKey Fails

I am attempting to pass some encrypted data between a Win 8 Metro app and a RESTful WCF service. Initially the Metro app requests a public key and the WCF service returns it as a raw Stream as to avoid any pesky formatting issues. The Base 64 encoded public key is decoded in the metro app into a byte array. Here is where the problem occurs. When I attempted to call AsymmetricKeyAlgorithmProvider.ImportPublicKey I get the error "ASN1 bad tag value met".
I am using RSA PKCS1 for the encryption. Here is the relevant code:
WCF Service
string keyName = "This is passed in via a parameter";
var key = !CngKey.Exists(keyName) ? CngKey.Create(CngAlgorithm2.Rsa, keyName) : CngKey.Open(keyName);
// Create the RSA container to get keys and then dispose
using (var rsaCng = new RSACng(key) { EncryptionPaddingMode = AsymmetricPaddingMode.Pkcs1, KeySize = 2048 })
{
byte[] publicBlob = rsaCng.Key.Export(CngKeyBlobFormat.GenericPublicBlob);
publicKey = Convert.ToBase64String(publicBlob);
}
Metro App
public static string Encrypt(IBuffer dataBuffer, string publicKeyString)
{
var asymmAlg = AsymmetricKeyAlgorithmProvider.OpenAlgorithm(AsymmetricAlgorithmNames.RsaPkcs1);
// The next line fails with ASN1 bad tag value met
var publicKey = asymmAlg.ImportPublicKey(CryptographicBuffer.DecodeFromBase64String(publicKeyString), CryptographicPublicKeyBlobType.Pkcs1RsaPublicKey);
var encryptedData = CryptographicEngine.Encrypt(publicKey, dataBuffer, null);
return CryptographicBuffer.EncodeToBase64String(encryptedData);
}
EDIT 1: More information below
Exporting the public key from a 2048bit key pair from the WCF service yields a 283 bit length key blob, while exporting the same type of public key from the Metro app is only 270 bits. When I import the Metro generated public key it succeeds. Any idea why the WCF service has 13 extra bits on its public key? I think those extra 13 bits are causing the failure.
Here is the Metro code that yields the shorter public key blob:
var provider = AsymmetricKeyAlgorithmProvider.OpenAlgorithm(AsymmetricAlgorithmNames.RsaPkcs1);
CryptographicKey standardKeyPair = provider.CreateKeyPair(2048);
byte[] standardKey = standardKeyPair.ExportPublicKey(CryptographicPublicKeyBlobType.Pkcs1RsaPublicKey).ToArray();
Quite late, but maybe it will help you or saves someone's time...
Change the type of blob type during import. It's really wierd, but I had success with it, after experimenting.
Your code in WCF may stay as it is.
Change just the Metro code:
public static string Encrypt(IBuffer dataBuffer, string publicKeyString)
{
var asymmAlg = AsymmetricKeyAlgorithmProvider.OpenAlgorithm(AsymmetricAlgorithmNames.RsaPkcs1);
// The next line fails with ASN1 bad tag value met
var publicKey = asymmAlg.ImportPublicKey(CryptographicBuffer.DecodeFromBase64String(publicKeyString), CryptographicPublicKeyBlobType.BCryptPublicKey);
var encryptedData = CryptographicEngine.Encrypt(publicKey, dataBuffer, null);
return CryptographicBuffer.EncodeToBase64String(encryptedData);
}
So the only change here is the BCryptPublicKey during the importing. Then it works. But do not ask me why :-).