I am trying to decrypt communication from web sockets secured with SSL certificate. The certificate is installed on the machine and the private key is present. I have this decrypt function
public static string DecryptTry(X509Certificate2 x509, string stringTodecrypt)
{
try
{
if (x509 == null || string.IsNullOrEmpty(stringTodecrypt))
throw new Exception("A x509 certificate and string for decryption must be provided");
if (!x509.HasPrivateKey)
throw new Exception("x509 certificate does not contain a private key for decryption");
if(x509 == null)
{
Console.WriteLine("certificate is null");
return "";
}
Console.WriteLine("decoding text with private key " + x509.HasPrivateKey);//true
using (RSA csp = (RSA)x509.PrivateKey)
{
byte[] bytestoDecrypt = Encoding.UTF8.GetBytes(stringTodecrypt).Take(256).ToArray();
Console.WriteLine("key size: " + x509.PrivateKey.KeySize + " received data length: " + stringTodecrypt.Length + " bytes length: " + bytestoDecrypt.Length);
Console.WriteLine(Encoding.UTF8.GetString(bytestoDecrypt));
byte[] bytesDecrypted = csp.Decrypt(bytestoDecrypt, RSAEncryptionPadding.OaepSHA256); //ERROR HERE
return Encoding.UTF8.GetString(bytesDecrypted);
}
}
catch(Exception e)
{
Console.WriteLine("Error while decrypting text: " + e.Message + e.HResult + e.StackTrace);
return "";
}
}
But the csp.Decrypt is throwing an error
parameter is incorrect
I have tried all padding parameters - none of the seemed to make a difference.
Does anybody know where the problem might be? Am I missing something?
**
EDIT 25.12.2020
**
To add some more background info: The website where the WebSocket client is listening is secured HTTPS, the SSL certificate is signed by CA with my full access to all of its information. The initial problem is handshake for the WebSocket communication which comes encrypted. I was thinking I would be able to decrypt it with the private key and that is where the problem occurs. The length of the incoming request (or handshake) is between 490 and 520 bytes, so that is the reason for .Take(256). I was thinking to split the text into multiple, decode them separately and put together after. That, however, brought me here.
One final thought: This is a .NET console application. Could the problem be possibly fixed by converting it to a format that IIS accepts? The IIS on the machine has the certificate installed... could it possibly make a difference?
Thanks in advance!
I'm not sure what you want to achieve:
Decrypt something using RSA
Decrypt TLS layer of HTTP
Decrypt something in Websocket protocol
I think that you should give some more informations how it works. Can you show example input for function function, means:
DecryptTry(X509Certificate2 x509, string stringTodecrypt) { ... }
How 'strindTodecypt' looks like? What do you put there?
How do you connect to this server to get data to decrypt, via TLS?
Related
This is a follow-up of this question.
I am writing an external server which gets called by a SAP-Server. The SAP-Server signs the URL with a certificate before it is transmitted. In a previous step the SAP-Server sent the certificate it will be using to sign the URL to my server. So my server has the certificate the SAP-Server is using for signing.
From the SAP-documentation I know the following.
The unsigned URL looks like this
http://pswdf009:1080/ContentServer/ContentServer.dll?get&pVersion=0046&contRep=K1&docId=361A524A3ECB5459E0000800099245EC&accessMode=r&authId=pawdf054_BCE_26&expiration=19981104091537
The values of important QueryString-parameters are concatenated (in the same order they appear in the QueryString) to form the "message".
For the given QueryString-Parameters
ContRep = K1
DocId = 361A524A3ECB5459E0000800099245EC
AccessMode = r
AuthId = pawdf054_BCE_26
Expiration = 19981104091537
the generated "message" looks like this:
K1361A524A3ECB5459E0000800099245ECrpawdf054_BCE_2619981104091537
The "message" is used to calculate the hash from which the SecKey is calculated. SAP uses the Digital Signature Standard (DSS) to digitally sign the hash value according to PKCS#. The digital signature is appended to the querystring in a parameter with the name SecKey. The SecKey for the chosen procedure is about 500 bytes long. In the example from the SAP-documentation, the arbitary values 0x83, 0x70, 0x21, 0x42 are chosen for the secKey, for the sake of clarity.
The SecKey is base64 encoded and added to the URL.
0x83, 0x70, 0x21, 0x42 gets to "g3AhQg=="
and the transferred URL looks like this
http://pswdf009:1080/ContentServer/ContentServer.dll?get&pVersion=0046&contRep=K1&docId=361A524A3ECB5459E0000800099245EC&accessMode=r&authId=pawdf054_BCE_26&expiration=19981104091537&secKey=g3AhQg%3D%3D
When my server receives the URL I need to check the signature. I recreate the "message" by concatenating the QueryString-parameters the same way as it was described in point 2. (as it is described in the SAP-documentation)
SAP gives this Summary of Technical Information
Format of digital signature: PKCS#7 "signed data"
Public key procedure: DSS
Key length: 512 - 1024 bits
Public exponent: 2^16 + 1
Public key format: X.509 v3 certificate
MD (message digest) algorithm: MD5 or RIPEMD-160
The library for checking signatures can be obtained from SAP AG. Because the standard format PKCS#7 was used for the signature, other products can also be used for decoding.
I receive an "The hash value is not correct"-Exception on line cms.CheckSignature(certificates, true);
private void CheckSignature(string secKey, string message, X509Certificate2 cert)
{
byte[] signature = Convert.FromBase64String(secKey);
ContentInfo ci = new ContentInfo(System.Text.Encoding.ASCII.GetBytes(message));
SignedCms cms = new SignedCms(ci, true);
X509Certificate2Collection certificates = new X509Certificate2Collection(cert);
cms.Decode(signature);
try
{
cms.CheckSignature(certificates, true);
}
catch(Exception ex)
{
log.Error(ex.ToString());
}
}
Can anybody help, or knows what I am doing wrong?
Actually the above function CheckSignature works correct
BUT the second parameter 'message' has to be URL-encoded. Or to be more precise, when concatenating you must use the NOT-URL-DECODED queryString values. [with the same spelling (uppercase/lowercase) SAP uses]
ContRep = AA
DocId = 53730C7E18661EDCB1F816798DAA18B2
AccessMode = r
AuthId = CN=NPL (for concatenating 'CN%3DNPL' is used)
Expiration = 20220511173746
will become the message
AA53730C7E18661EDCB1F816798DAA18B2rCN%3DNPL20220511173746
I currently doing a proof of concept to encrypt data using a certificate. It works well but now, I want to try a scenario when the certificate is expired. I created an expired certificate and I was surprise to notice that everthing works property even with the expired certificate. I was expecting an error.
Do you know if it's because it's a self signed certificate ?
Here's the code I using to test my case
[TestMethod]
public void Encrypt_decrypt_with_expired_certificate()
{
//Arrange
var baseString = "This is an encryption test";
X509Certificate2 newX509Certificate2 = new X509Certificate2("d:\\testx509certExpired.pfx", "apassword");
Console.WriteLine(newX509Certificate2.NotAfter); //Show the expiration date which is in the past
var encryptor = new CertificateEncryptor(newX509Certificate2); //This class is a simple wrapper around RSACryptoServiceProvider
//Act
string encryptedResult = encryptor.Encrypt(baseString); //Exception expected because of the expired certificate but not thrown
//Assert
Console.WriteLine("Base string : {0}", baseString);
Console.WriteLine("Encrypted string : {0}", encryptedResult);
Assert.IsNotNull(encryptedResult);
//revert back
string decryptedString = encryptor.Decrypt(encryptedResult);
Console.WriteLine("Decrypted string : {0}", decryptedString);
Assert.AreEqual(baseString, decryptedString);
}
Thanks
As GregS said, RSACryptoServiceProvider class (not X509Certificate2) provides an ability to perform cryptographic operations. RSACryptoServiceProvider knows nothing about certificate, it knows only keys and their parameters. This is why you don't see any errors.
This means that certificate validation -- is your app responsibility. You should check certificate when encrypting data and skip all certificate checks to decrypt data.
When attempting to access the X509Certificate2.PublicKey.Key attribute of the certificate, a CryptographicException should be thrown if the certificate is not within its validity period.
Here is how I load the public & private keys from a certificate to perform cryptographic operations:
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
class Example
{
private RSACryptoServiceProvider publicKey,
privateKey;
private bool getRSAKeys(X509Certificate2 cert, StoreLocation location)
{
try
{
//This will throw a CryptographicException if the certificate is expired
publicKey = (RSACryptoServiceProvider)cert.PublicKey.Key;
privateKey = (RSACryptoServiceProvider)cert.PrivateKey;
return true;
}
catch (CryptographicException e)
{
Console.WriteLine("The certificate is expired or otherwise unusable\r\n" + e.ToString());
return false;
}
}
I'm using the moon aspn library for sending push notifications in asp.net and c#. I'm encountering the following error: Call to SSPI Failed; in the inner exception i have: Unknown error while processing the certificate; error code: -2147467259.
here is my code:
private void SendQueueToapple(IEnumerable<NotificationPayload> queue)
{
int i = 1000;
foreach (var item in queue)
{
if (!_conected)
{
Connect(_host, NotificationPort, _certificates);
var response = new byte[6];
--> --> --> _apnsStream.BeginRead(response, 0, 6, ReadResponse, new MyAsyncInfo(response, _apnsStream));<-- <-- <--
} try {
if (item.DeviceToken.Length == 64) //check lenght of device token, if its shorter or longer stop generating Payload.
{
item.PayloadId = i;
byte[] payload = GeneratePayload(item);
_apnsStream.Write(payload);
Logger.Info("Notification successfully sent to APNS server for Device Toekn : " + item.DeviceToken);
Thread.Sleep(1000); //Wait to get the response from apple.
}
else
Logger.Error("Invalid device token length, possible simulator entry: " + item.DeviceToken);
}
catch (Exception ex)
{
Logger.Error("An error occurred on sending payload for device token {0} - {1}", item.DeviceToken, ex.Message);
_conected = false;
}
i++;
}
}
This might be a certificate related problem. You have to create .cer and .p12 files. Pay close attention to these files. You first generate the .cer and I'm not going into detail here. There's enough material available. The problem comes when generating the .p12 file.
In your mac, go to the Keychain Access. Select "My Certificates" from the left pane. There you will see a list of certificates. Select the certificate you generated for push notifications. Right click and export. Make sure you DO NOT expand the certificate perform this step on the private key. That's where thing starts to go wrong.
I need to decrypt a conection that was created initially under an account that is no longer available.
In order to do that I made a simple app:
private void btnEncrypt_Click(object sender, EventArgs e)
{
DataProtection.DataProtector dp = new DataProtection.DataProtector(DataProtection.DataProtector.Store.USE_MACHINE_STORE);
try
{
byte[] dbToEncrypt = Encoding.ASCII.GetBytes(txtText.Text);
string resultEncrypted = Convert.ToBase64String(dp.Encrypt(dbToEncrypt, null));
txtEncrypt.Text = resultEncrypted;
}
catch (Exception ex)
{
MessageBox.Show("Error: " + ex.Message);
}
}
private void btnDecrypt_Click(object sender, EventArgs e)
{
DataProtection.DataProtector dp = new DataProtection.DataProtector(DataProtection.DataProtector.Store.USE_MACHINE_STORE);
try
{
byte[] dbToDecrypt = Convert.FromBase64String(txtEncrypt.Text);
string resultDecrypted = Encoding.ASCII.GetString(dp.Decrypt(dbToDecrypt, null));
txtDecrypt.Text = resultDecrypted;
}
catch (Exception ex)
{
MessageBox.Show("Error: " + ex.Message);
}
}
Now, I have noticed that when I do a test in my computer, and try to decrypt the encrypted result in a different computer I get:
Exception decrypting. Decryption failed. Key not valid for use in
specific state.
Then, I did some research and found out this:
Did you export the key from one server to the other so they are both
set up the same? If not, you are using mismatched keys, which will
cause an encryption/decryption error.
and I can find the keys here:
How to get the validationkey value and decryptionkey value?
decryption key can be found at "D:\Documents and Settings\All
Users\Application Data\Microsoft\Crypto\RSA\MachineKeys"
so my question is: If I export the keys in that location from my computer the the one I want to decrypt the data will that work? and by export mean just copy the key files or do another operation?
AFAIK this is not possible - and in any case is not desirable. DPAPI regularly creates new keys, so even if you could copy the keys between machines, they would become obsolete after a period of time.
If you want to decrypt data on more than one computer, use a different method, e.g. RSA.
I'm and out of practice programmer, so basically new to it again.
What I am doing is logging onto a a device over Telnet or TCP. Instead of controlling the device by type command I am using a custom forms application to send the type string commands by pre programmed push button. The device is an old Codec. The purpose of my software is to create a push button controller to be used from a PC.
The problem I am having is that some of my devices are password protected and some are not (different firmware). This cannot be changed. The Password protection is what has me stuck.
I am sending data to the device using ASCII
public void Write(string cmd)
{
if (!tcpSocket.Connected) return;
byte[] buf = System.Text.ASCIIEncoding.ASCII.GetBytes(cmd.Replace("\0xFF", "\0xFF\0xFF"));
tcpSocket.GetStream().Write(buf, 0, buf.Length);
I have been searching on MD5 and have become stuck.
I have tried sending the password by plain text typing the password into a text box and initiating the write command. I have also tried sending the output of this code I found on the internet
public string EncodePassword(string originalPassword)
{
//Declarations
Byte[] originalBytes;
Byte[] encodedBytes;
MD5 md5;
//Instantiate MD5CryptoServiceProvider, get bytes for original password and compute hash (encoded password)
md5 = new MD5CryptoServiceProvider();
originalBytes = ASCIIEncoding.Default.GetBytes(originalPassword);
encodedBytes = md5.ComputeHash(originalBytes);
//Convert encoded bytes back to a 'readable' string
return BitConverter.ToString(encodedBytes);
I even found another MD5 line that forced upper and lower case. I don't know if it wont work because it is still sending the encoded password in ASCII or what.
I do know that my password is right because I can load telnet in windows and log on fine there. Any help in getting this client to authenticate with the server would be most appreciated.
Forgive the length. Since I am unable to reply I had to edit. I think that I was confused on the MD5... After reading the replies I think my problem is the ASCII. I need plain text.
Ok, so this is where my beginner stripes shine brightly. This is my first attempt at programming that involves a network of any sort (if it wasn't already that obvious). From reading the replies I think my first problem is the ASCII. I assumed that being sent though that was plain text. Given that when I connect to a server with the same client that does not require password login... The ASCII works just fine.
So if I am to use plain text, then How would I go about sending in plain text and not a byte conversion? Assuming that my assumption that ASCII was the way to send plain text is wrong...Which I now think that it is.
I have added more code to help this along.
When using the Windows telnet client, the device prompts for password and when you type it into telnet no text is shown until after login. After login all typing is shown immediately.
The Class used for the socket is mostly a code I found on google with some small tweeks.
using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
namespace STC_Control
{
enum Verbs
{
WILL = 251,
WONT = 252,
DO = 253,
DONT = 254,
IAC = 255
}
enum Options
{
SGA = 3
}
class TelnetConnection
{
TcpClient tcpSocket;
int TimeOutMs = 100;
public TelnetConnection(string Hostname, int Port)
{
tcpSocket = new TcpClient(Hostname, Port);
}
public void WriteLine(string cmd)
{
Write(cmd + "\n");
}
public void Write(string cmd)
{
if (!tcpSocket.Connected) return;
byte[] buf = System.Text.ASCIIEncoding.ASCII.GetBytes(cmd.Replace("\0xFF", "\0xFF\0xFF"));
tcpSocket.GetStream().Write(buf, 0, buf.Length);
}
public string Read()
{
if (!tcpSocket.Connected) return null;
StringBuilder sb = new StringBuilder();
do
{
ParseTelnet(sb);
System.Threading.Thread.Sleep(TimeOutMs);
} while (tcpSocket.Available > 0);
return sb.ToString();
}
public bool IsConnected
{
get { return tcpSocket.Connected; }
}
void ParseTelnet(StringBuilder sb)
{
while (tcpSocket.Available > 0)
{
int input = tcpSocket.GetStream().ReadByte();
switch (input)
{
case -1:
break;
case (int)Verbs.IAC:
// interpret as command
int inputverb = tcpSocket.GetStream().ReadByte();
if (inputverb == -1) break;
switch (inputverb)
{
case (int)Verbs.IAC:
//literal IAC = 255 escaped, so append char 255 to string
sb.Append(inputverb);
break;
case (int)Verbs.DO:
case (int)Verbs.DONT:
case (int)Verbs.WILL:
case (int)Verbs.WONT:
// reply to all commands with "WONT", unless it is SGA (suppres go ahead)
int inputoption = tcpSocket.GetStream().ReadByte();
if (inputoption == -1) break;
tcpSocket.GetStream().WriteByte((byte)Verbs.IAC);
if (inputoption == (int)Options.SGA)
tcpSocket.GetStream().WriteByte(inputverb == (int)Verbs.DO ? (byte)Verbs.WILL : (byte)Verbs.DO);
else
tcpSocket.GetStream().WriteByte(inputverb == (int)Verbs.DO ? (byte)Verbs.WONT : (byte)Verbs.DONT);
tcpSocket.GetStream().WriteByte((byte)inputoption);
break;
default:
break;
}
break;
default:
sb.Append((char)input);
break;
}
}
}
}
}
Then the program
public Form1()
{
InitializeComponent();
}
//declorations
TelnetConnection tc;
Int16 vl = 13;
private void connect_Click(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(roomBox.Text))
{
MessageBox.Show("Please enter a selection before continuing");
}
else
{
{
try
{
//Connects to the server
tc = new TelnetConnection(roomBox.Text, 23);
//Enables controls
panelAll.Enabled = true;
}
catch
{
MessageBox.Show("Server Unreachable. ");
panelAll.Enabled = false;
cState.Text = "Disconnected";
}
}
}
// Button to send login password Temp created to test login
public void p_Click(object sender, EventArgs e)
{
try
{
//sends text to server
tc.WriteLine("PASSWORD");
//enables Buttons
panelAll.Enabled = true;
//displays return to textbox to verify login or disconnect
rx.Text = (tc.Read());
}
catch
{
panelAll.Enabled = false;
MessageBox.Show("Communication with device was lost.");
cState.Text = "Disconnected";
}
}
------------------------------------------------------------------------------------------
Your code seems a bit incomplete to give a full answer. If possible, supply a more complete flow of logic stripped out of your program. But from what I can gather, here's some immediate tips...
Don't confuse MD5 with encryption. They're very, very different things (MD5 is a one-way hash while encryption is a two-way encoding). This is likely to be the source of your headaches. Encryption/hashing handshake methods must be agreed upon by both participants. If you're trying to login with an MD5 hash of a password, to a device that only accepts plain-text passwords, then it will not recognize it as a valid password. There is literally nothing you could do about this scenario... plain-text it would have to be.
You assume passwords are ASCII? If not, the ASCII conversion is bad news. Probably not a root problem here, but something to think about.
EDIT: Actually... let me go more into depth because after further thought, I'm about 90% sure this is your problem (without more information of course).
The flow of logic when dealing with a device that only supports plain-text passwords is this (where C is client [you] and S is server [them])...
[C] Send password plain-text
[S] Got your password, thanks! I just checked it against what I know and it's good/bad
When using any kind of hashing.
[C] Hash password (MD5, SHA1, etc)
[C] Send password
[S] Receive hash of password. Check against hashed password stored (or worse, hash of plain-text password stored). Respond good/bad
When encrypting the connection (when client and server know what kind of encryption they both use)...
[C] Garble-garble-garble (password encrypted)
[S] Got it... and garble-garble-garble turns into whatever plain-text password. Checked it against local storage and .... garble-garble-garble (good/bad encrypted)
When encrypting the connection (and the client/server may not necessarily support the same methods but know how to negotiate which to use)...
[C] Which encryption methods do you support? Here's my list...
[S] Oh, well, we both support these methods. Which do you want to use?
[C] IDEA sounds good.
[S] Sounds good to me too. Start using it.
[C] Garble-garble-garble (password encrypted)
[S] Got it... and garble-garble-garble turns into whatever plain-text password. Checked it against local storage and .... garble-garble-garble (good/bad encrypted)
As you can see, very different methods. Those must be built into the communication device (server) for a successful negotiation.
I don't think this is a programming problem. I believe it's more of a problem of understanding how your device actually works. Does it accept password as plain text, or does it accept password in some hashed or encrypted form?
The fact that you can provide the password through telnet suggest that it is plain text, unless the telnet protocol has provision for some for of authentication.
It would be good if you could provide a screenshot of the telnet window. We might be able to get some hints from there.
I would recommend that you submit the plain text password followed by the \n new line character.
It sounds to me that your best solution here would be to download the OpenSSL package, install it on the device and your forms app, and then use the APIs to use a secure shell connection instead of telnet to send device commands.
That way the key management is outside your app and you can use prewritten APIs to do your encryption in a tested piece of code.