I'm used to implementing pusher client with javascript and having messages decrypted automatically, what we need is to just configure an auth point that authorizes connection and provides a master encryption key. With DotNet I'm experiencing issues, what seems to me is that this approach doesn't work with dotnet pusher client(I'm using Xamarin).
Trying to solve this issue, I have searched and found this code at github.com/pusher/pusher-websocket-dotnet/blob/master/PusherClient/ChannelDataDecrypter.cs but I'm still not achieving to decrypt, It fails and I keep getting Decryption failed for channel.
I'm sure the key I'm passing to DecryptData() is the same of the one at auth endpoint, and to be more specific, I will provide it, plus the relevant part of the code that suppostly should decrypt:
This is the json (json_event.data):
{"data":{"nonce":"Cr3K2CDnVXH1s65jtQpjorV8PnSvy+At","ciphertext":"R+Bg+aJ1VciAGPdhqVH7vB/NjBzsVLcBjLU1Q8LSA5Czd34DLyJaaVpsQCexYt/bYtUo78e0VY2U5lfv7vFna2NGOq7T+6yE0uj9xlvQSHzpfavErQ1XMaZ6LXoFqZakv786BLZ9BsW2jGA4U4+iBzDA1/kPfeJ15w1qbLink8AihtgNAo0v65YUJpmomWlAHwucFCP+psYmPzhk9Sci9RvLxjKIhq13B/G9urSU+EF990Ox05oqGt3+aoIM7XYnqSw69uqskXJE0WOLUbZYrZNbFgoVuZ8BzoUm82OpBCZVSxtlplFFpKA9oz3mh/jOQg7i8jVftLkgF9Ci2pNmjGsKoxuexTp8suwXLygD7Qkffj/cwdEYPglJ+EebCm/FItnL+Lt/ZsXJaAaeSbsGzsDU+gXNbgSwbRgXt38AI/evllhz8g51mhz6W056ON3IKPzpEgiEc5WhcsGb2tNSzpk4rxN41ZCQ3c/TnXXCMySfL0rh54zTAKXfSMWaOXRXAcUgNyD2NnPYgbtLQ6+QCtYRZITxdETqQqyFpDF0TbJueK5eG3CJUCHOoT0WyOlMs1Ry8XEDLA6Mk4M+Xed917b14imgM4lfOiU+o/tp0sg/x271EpMciKbZ8TXNhN+yCJgR2vLJZlnJ5CT7kxC7TaNaap0XBp9h4Hdu4FTevlIu0XLxd9o3EMht1o0COcpvcBO6nZmQZHgDJ/0jJSkSK6BnCEU2snIHqMJUSjtKl5AMvUISjLN+l/CS7T6FIObtSmlDLRVkFBY4IaYDrDfnsxM+b9Zq4vSVT5+nhnbLg+NrFgwjzQQbBi5GObIPxjxhm4HZ9A8nwBmNM8DWQe1o/OZP3XFqGpYYzpx8+05OW1zBB2gk+1lhHlZdA+dFJa9dko83j5Q2BEZc00HL"}}
C# code:
string key = "60e4a9540aa77099695ca4aa4e5a746f"; //got it from auth endpoint server logs
string msgDecrypted = channelDataDecrypter.DecryptData(System.Text.Encoding.ASCII.GetBytes(key), json_event.data);
This is how I'm generating the key at server side(PHP):
require_once 'pusher/autoload.php';
require_once('pem_files/chat_privatekey_salt.php');
$master_key=md5(base64_encode(openssl_digest($salt.$_POST['channel_name'],'SHA256', true)));
//using md5 because pusher was complaining about the key length and saying that the key must have 32 chars, but I think this is not the point since decryption works in javascript client using this very same key.
$options = array('cluster' => 'us2','useTLS' => true,'encrypted' => true,'encryption_master_key'=>$master_key);
$pusher = new Pusher\Pusher('xxxxxxxx','yyyyyyyy','0000000',$options);
echo $pusher->socket_auth($_POST['channel_name'],$_POST['socket_id']);
As I said before, this fails with Decryption failed for channel. What can I do to fix this?
Related
I need to sign and encrypt a SOAP request with a certificate to access a WS method, but i'm getting the same response all the time:
"A security error was encountered when verifying the message".
I guess there's something wrong with my code rather than any other issue.
Here it is:
cert = new System.Security.Cryptography.X509Certificates.X509Certificate2(sCertificatePath, sCertificatePassword);
userToken = new Microsoft.Web.Services3.Security.Tokens.UsernameToken(sUser, sPass, Microsoft.Web.Services3.Security.Tokens.PasswordOption.SendHashed);
secureToken = new Microsoft.Web.Services3.Security.Tokens.X509SecurityToken(cert);
encDataToken = new Microsoft.Web.Services3.Security.EncryptedData(secureToken);
mSignUsernameToken = new Microsoft.Web.Services3.Security.MessageSignature(userToken);
mSignSecurityToken = new Microsoft.Web.Services3.Security.MessageSignature(secureToken);
wsVehicleInfo = new wsBusiness.VehicleInfoWSImplService();
vehData = new wsBusiness.getVehicleInfoRequest();
vehData.vehicleRegistration = "XXXXYYY";
vehData.language = "es";
requestContext = wsVehicleInfo.RequestSoapContext;
requestContext.Security.Elements.Add(encDataToken);
requestContext.Security.Tokens.Add(secureToken);
requestContext.Security.Elements.Add(mSignSecurityToken);
requestContext.Security.Timestamp.TtlInSeconds = 300;
requestContext.Security.Tokens.Add(userToken);
Is it correct? Actually I got some questions:
I'm signing and encrypting with the same certificate issued by a CA. Don't I need to encrypt with the server's one? How can I get it?
Does order of XML elements generated matter in the request? Which should be the code order?
The algorithm used in the documentation to encrypt the soap body is "aes128-gcm" but I wasn't able to find it and instead using "aes128-cbc". May it cause any trouble?
Is it recommended the usage of WSE in this case? I read this:
"Instead of asymmetrically encrypting the message, WSE use an asymmetric algorithm with a public copy of the recipient's X.509 certificate to encrypt the symmetric key that was actually used to encrypt the message data.". No way to just encrypt the SOAP body from the request with the server's certificate with WSE instead of encrypting the symmetric key?
I am trying to figure out a way of authentication between two distributed services.
I don't want to have a shared secret distributed on every service host, because it would mean that once one host has been compromised, all hosts are compromised.
So my scenario is:
Host A knows the public key of Host B
Host A encodes and encryptes the jwt using Host B´s public key
Host B receives and decrypts the jwt using its private key, that it only knows itself.
The jose-jwt package:
https://github.com/dvsekhvalnov/jose-jwt
seems like a good option to me. Beside the signing of the jwt, it also supports encryption using private/public keys.
On the page there are the following examples for encoding and decoding a jwt:
Encode:
var publicKey=new X509Certificate2("my-key.p12", "password").PublicKey.Key as RSACryptoServiceProvider;
string token = Jose.JWT.Encode(payload, publicKey, JweAlgorithm.RSA_OAEP, JweEncryption.A256GCM);
Decode:
var privateKey=new X509Certificate2("my-key.p12", "password", X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet).PrivateKey as RSACryptoServiceProvider;
string json = Jose.JWT.Decode(token,privateKey);
Now, here is what i don´t understand:
How can I create a .p12 certificate file that only contains the public key information (for the host/service A that encodes the jwt) ?
.. and how can I create a .p12 certificate file that contains both, the public and the private key information (for the host/service B that decodes the jwt) ?
From all the research that I have done, i get the impression that you can either only make a .p12 file that contains both, or one that contains only the public key. But it seems there is no way to create two .p12 files, one with both information and one with only the public key. What am I missing?
Thanks for your answers.
Normally a PKCS12/PFX is not used for public-only, but you can do it if you like.
Assuming that cert.HasPrivateKey is true: cert.Export(X509ContentType.Pkcs12, somePassword) will produce a byte[] that you can write to "publicAndPrivate.p12" (or whatever).
Normally for a public-only certificate you'll write it down just as the X.509 data, either DER-binary or PEM-DER encoded. .NET doesn't make PEM-DER easy, so we'll stick with DER-binary. You can get that data by either cert.RawData, or cert.Export(X509ContentType.Cert) (both will produce identical results, since this export form has no random data in it). (publicOnly.cer)
If you really want a PKCS12 blob which has just the public certificate:
using (X509Certificate2 publicOnly = new X509Certificate2(publicPrivate.RawData))
{
return publicOnly.Export(X509ContentType.Pkcs12, somePassword);
}
The resulting byte[] could then be publicOnly.p12.
I am able to successfully identify client certificates in a .NET thick client app, and the user is able to successfully select one.
X509Store store = new X509Store("MY", StoreLocation.CurrentUser);
store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
var fcollection = store.Certificates.Find(X509FindType.FindByApplicationPolicy, "1.3.6.1.5.5.7.3.2", true);
// other stuff where user selects one of them
Now how do I ask the user to answer the challenge (e.g. PIN in this case)?
I see there's a SignedXML.ComputeSignature() class, but it takes a byte stream, and I'm not sure where that comes from (perhaps in certificate.RawData[]?).
I'm not really as interested in getting the actual pin as I am that the card/pin match.
EDIT:
I tried using the private key from the smart card (and even encrypted from it), but I don't get asked for my PIN.
RSACryptoServiceProvider rsacsp = (RSACryptoServiceProvider)certificate.PrivateKey;
UnicodeEncoding ByteConverter = new UnicodeEncoding();
byte[] dataToEncrypt = ByteConverter.GetBytes("Data to Encrypt");
var encryptedData = RSAEncrypt(dataToEncrypt, rsacsp.ExportParameters(false), false);
Looks like the PIN request happens when I call RSACryptoServiceProvidersa.Decrypt.
Here's example code that worked perfectly for me in a Console app:
http://blog.aggregatedintelligence.com/2010/02/encryptingdecrypting-using.html
Much simpler in ASP.NET (aside from all the IIS config hassles/mysteries ...).
If this is a smartcard, the pin prompt will happen when you try to use the private key of the certificate.
You need to use the certificate somehow, and validate the result. For example, you might use the certificate to sign something. Once that signature operation happens, the pin prompt will appear.
If you don't really need to "use" the certificate, just want to validate that it's there and the user knows the pin, then you need some sort of proof of work. The certificate could be used to sign a challenge, and a remote server could validate the signature uses a key that belongs to a trusted root. Keep in mind this is difficult to get right, such as making sure you aren't open to a replay attack, etc.
I have a WCF client that is crashing with the error "The EncryptedKey clause was not wrapped with the required encryption token 'System.IdentityModel.Tokens.X509SecurityToken'." for every response.
I've looked around and this blog post seems to indicate that the problem is with my certificate set up, but I'm not sure what I am doing wrong...
My client uses a custom binding with a MutualCertificateBindingElement for security, I am configuring the certificates in code as follows:
client.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.ChainTrust;
client.ClientCredentials.ServiceCertificate.SetDefaultCertificate
(
StoreLocation.CurrentUser,
StoreName.AddressBook,
X509FindType.FindBySerialNumber,
"[serial number 1]"
);
client.ClientCredentials.ClientCertificate.SetCertificate
(
StoreLocation.CurrentUser,
StoreName.My,
X509FindType.FindBySerialNumber,
"[serial number 2]"
);
The serial numbers match the values in the <X509SerialNumber> elements in both the request and the response messages.
One discrepancy I have noticed is the <X509IssuerName> elements in the request and the response are formatted differently:
Request: CN=[CN], O=[O], L=[L], C=[C]
Response: C=[C],L=[L],O=[O],CN=[CN]
Is it possible this is causing the issue?
UPDATE
Turns out it was the certificate name formatting causing the issue. I managed to resolve it by replacing the cert names in the response with what WCF expects by using a custom encoder. Now I have this ugly hack, but it works so I'll live with it!
public override Message ReadMessage(ArraySegment<byte> buffer, BufferManager bufferManager, string contentType)
{
var msgContents = new byte[buffer.Count];
Array.Copy(buffer.Array, buffer.Offset, msgContents, 0, msgContents.Length);
bufferManager.ReturnBuffer(buffer.Array);
var message = Encoding.UTF8.GetString(msgContents);
// Fix certificate issuer name formatting to match what WCF expects.
message = message.Replace
(
"C=[C],L=[L],O=[O],CN=[CN]",
"CN=[CN], O=[O], L=[L], C=[C]"
);
var stream = new MemoryStream(Encoding.UTF8.GetBytes(message));
return ReadMessage(stream, int.MaxValue);
}
The issuer name order that you mentioned is most probably the issue. Since these names are not signed I suggest you write a custom encoder in your client that replaces the names in the response to be formatted as in the request.
Besides obvious cert mismatch and barring miss-configuration... I have seen an issue trying to access private key. Check that client has appropriate permissions to the cert private key. If you right click on the cert in certmanager you should see AllTasks/Manage Private Keys. Add your client's process identity to the list.
Also make sure that the certificate you are using is correct. I used self-signed certificate which was missing Subject Key Identifier.
WCF : The EncryptedKey clause was not wrapped with the required encryption token 'System.IdentityModel.Tokens.X509SecurityToken'
I am doing an SSL3 handshake using an SslStream, but, in spite of my best efforts, the SslStream never sends a client certificate on my behalf. Here is the code:
SSLConnection = new System.Net.Security.SslStream(SSLInOutStream, false, new System.Net.Security.RemoteCertificateValidationCallback(AlwaysValidRemoteCertificate), new System.Net.Security.LocalCertificateSelectionCallback(ChooseLocalCertificate));
X509CertificateCollection CC = new X509CertificateCollection();
CC.Add(Org.BouncyCastle.Security.DotNetUtilities.ToX509Certificate(MyLocalCertificate));
SSLConnection.AuthenticateAsClient("test", CC, System.Security.Authentication.SslProtocols.Ssl3, false);
and then I have AlwaysValidRemoteCertificate just returning true, and ChooseLocalCertificate returning the zeroth element of the array.
The code probably looks a little weird because the project is a little weird, but I think that is beside the point here. The SSL handshake completes. The issue is that instead of sending a certificate message on my behalf (in the handshake process), with the ASN.1 encoded certificate (MyLocalCertificate), the SslStream sends an SSL alert number 41 (no certificate) and then carries on. I know this from packet sniffing. After the handshake is completed, the SslStream marks IsAuthenticated as true, IsMutuallyAuthenticated as false, and its LocalCertificate member is null.
I feel like I'm probably missing something pretty obvious here, so any ideas would be appreciated. I am a novice with SSL, and this project is off the beaten path, so I am kind of at a loss.
P.S. 1: My ChooseLocalCertificate routine is called twice during the handshake, and returns a valid (as far as I can tell), non-null certificate both times.
P.S. 2: SSLInOutStream is my own class, not a NetworkStream. Like I said, though, the handshake proceeds mostly normally, so I doubt this is the culprit... but who knows?
For some reason the site won't let me leave a comment on the solution posted above, but it solved my problem. Here's my comment:
Hey, that was a great guess, and it fixed my issue. Thank you very much. The C# X509Certificate class doesn't seem to support setting a private key, but the daughter class X509Certificate2 does. I had to monkey around a bit with BouncyCastle stuff, but the implementation (if anyone needs it in the future) is this:
X509CertificateCollection CC = new X509CertificateCollection();
X509Certificate2 C2 = new X509Certificate2(MyLocalCertificate.GetEncoded());
C2.PrivateKey = Org.BouncyCastle.Security.DotNetUtilities.ToRSA((Org.BouncyCastle.Crypto.Parameters.RsaPrivateCrtKeyParameters)MyPrivateKey.Private);
CC.Add(C2);
MyLocalCertificate is an instance of bouncycastle's X509Certificate class, and MyPrivateKey is an instance of bouncycastle's AsymmetricCipherKeyPair.
I'm not familiar with the Bouncycastle .NET API for SSL, but at first look, I'm guessing that you aren't supplying the private key to the API.
Even though the private key itself is never sent to the server, it is required to digitally sign some data in order to prove to the server that you hold it. There should be some API to provide the private key for this signature operation.