# jetblack-negotiate-stream
A Python client for .Net [NegotiateStream](https://learn.microsoft.com/en-us/dotnet/api/system.net.security.negotiatestream).
It supports single sign on (SSO) and encryption.
This was tested using Python 3.8 on Windows 11.
## Installation
Install from pypi.
```bash
pip install jetblack-negotiate-stream
```
## Example
The following programs provide a simple echo server in C# and client in Python.
### Client
This is an example of a Python client using the synchronous `NegotiateStream` class. Note the call to `authenticate_as_client` before reading and writing.
```python
import socket
from jetblack_negotiate_stream import NegotiateStream
def main():
hostname = socket.gethostname()
port = 8181
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.connect((hostname, port))
stream = NegotiateStream(hostname, sock)
# Do the client side negotiate handshake.
stream.authenticate_as_client()
for data in (b'first line', b'second line', b'third line'):
# All reads and writes are encrypted.
stream.write(data)
response = stream.read()
print("Received: ", response)
if __name__ == '__main__':
main()
```
### Async Client
This program uses `NegotiateStreamAsync` which is simply a synchronous version of the `NegotiateStream` class demonstrated above.
```python
import asyncio
import socket
from jetblack_negotiate_stream import NegotiateStreamAsync
async def main():
hostname = socket.gethostname()
port = 8181
reader, writer = await asyncio.open_connection(hostname, port)
stream = NegotiateStreamAsync(hostname, reader, writer)
await stream.authenticate_as_client()
for data in (b'first line', b'second line', b'third line'):
stream.write(data)
await stream.drain()
response = await stream.read()
print("Received: ", response)
stream.close()
await stream.wait_closed()
if __name__ == '__main__':
asyncio.run(main())
```
### Alternative Async Client
The following client follows the patterns demonstrated in the asyncio library using `open_negotiate_stream`. This follows
the conventions of the asyncio `open_connection` function. The negotiation happens before the function returns, resulting in cleaner code.
```python
import asyncio
import socket
from jetblack_negotiate_stream import open_negotiate_stream
async def main():
hostname = socket.gethostname()
port = 8181
# Following the same pattern as asyncio.open_connection.
reader, writer = await open_negotiate_stream(hostname, port)
for data in (b'first line', b'second line', b'third line'):
writer.write(data)
await writer.drain()
response = await reader.read()
print("Received: ", response)
writer.close()
await writer.wait_closed()
if __name__ == '__main__':
asyncio.run(main())
```
### Server
Here is a trivial C# echo server for the clients.
```csharp
using System;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Text;
namespace NegotiateStreamServer
{
internal class Program
{
static void Main(string[] args)
{
var listener = new TcpListener(IPAddress.Any, 8181);
listener.Start();
while (true)
{
Console.WriteLine("Listening ...");
var client = listener.AcceptTcpClient();
try
{
Console.WriteLine("... Client connected.");
Console.WriteLine("Authenticating...");
var stream = new NegotiateStream(client.GetStream(), false);
stream.AuthenticateAsServer();
Console.WriteLine(
"... {0} authenticated using {1}",
stream.RemoteIdentity.Name,
stream.RemoteIdentity.AuthenticationType);
var buf = new byte[4096];
for (var i = 0; i < 3; ++i)
{
var bytesRead = stream.Read(buf, 0, buf.Length);
var message = Encoding.UTF8.GetString(buf, 0, bytesRead);
Console.WriteLine(message);
stream.Write(buf, 0, bytesRead);
}
stream.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
}
}
}
```
## Acknowledgements
The library uses the [pyspnego](https://github.com/jborean93/pyspnego) library,
and takes many ideas from [net.tcp-proxy](https://github.com/ernw/net.tcp-proxy).
Raw data
{
"_id": null,
"home_page": "https://github.com/rob-blackbourn/jetblack-negotiate-stream",
"name": "jetblack-negotiate-stream",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.8,<4.0",
"maintainer_email": "",
"keywords": "",
"author": "Rob Blackbourn",
"author_email": "rob.blackbourn@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/a1/8a/874d79fd0b61aa0573b3006380ef0bd07a7ee1bec96826c9238ccfb2727d/jetblack_negotiate_stream-0.1.0.tar.gz",
"platform": null,
"description": "# jetblack-negotiate-stream\n\nA Python client for .Net [NegotiateStream](https://learn.microsoft.com/en-us/dotnet/api/system.net.security.negotiatestream).\nIt supports single sign on (SSO) and encryption.\n\nThis was tested using Python 3.8 on Windows 11.\n\n## Installation\n\nInstall from pypi.\n\n```bash\npip install jetblack-negotiate-stream\n```\n\n## Example\n\nThe following programs provide a simple echo server in C# and client in Python.\n\n### Client\n\nThis is an example of a Python client using the synchronous `NegotiateStream` class. Note the call to `authenticate_as_client` before reading and writing.\n\n```python\nimport socket\n\nfrom jetblack_negotiate_stream import NegotiateStream\n\ndef main():\n hostname = socket.gethostname()\n port = 8181\n\n with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:\n sock.connect((hostname, port))\n\n stream = NegotiateStream(hostname, sock)\n\n # Do the client side negotiate handshake.\n stream.authenticate_as_client()\n\n for data in (b'first line', b'second line', b'third line'):\n # All reads and writes are encrypted.\n stream.write(data)\n response = stream.read()\n print(\"Received: \", response)\n\nif __name__ == '__main__':\n main()\n```\n\n### Async Client\n\nThis program uses `NegotiateStreamAsync` which is simply a synchronous version of the `NegotiateStream` class demonstrated above.\n\n```python\nimport asyncio\nimport socket\n\nfrom jetblack_negotiate_stream import NegotiateStreamAsync\n\nasync def main():\n hostname = socket.gethostname()\n port = 8181\n\n reader, writer = await asyncio.open_connection(hostname, port)\n\n stream = NegotiateStreamAsync(hostname, reader, writer)\n\n await stream.authenticate_as_client()\n for data in (b'first line', b'second line', b'third line'):\n stream.write(data)\n await stream.drain()\n response = await stream.read()\n print(\"Received: \", response)\n\n stream.close()\n await stream.wait_closed()\n\nif __name__ == '__main__':\n asyncio.run(main())\n```\n\n### Alternative Async Client\n\nThe following client follows the patterns demonstrated in the asyncio library using `open_negotiate_stream`. This follows\nthe conventions of the asyncio `open_connection` function. The negotiation happens before the function returns, resulting in cleaner code. \n\n```python\nimport asyncio\nimport socket\n\nfrom jetblack_negotiate_stream import open_negotiate_stream\n\nasync def main():\n hostname = socket.gethostname()\n port = 8181\n\n # Following the same pattern as asyncio.open_connection.\n reader, writer = await open_negotiate_stream(hostname, port)\n\n for data in (b'first line', b'second line', b'third line'):\n writer.write(data)\n await writer.drain()\n response = await reader.read()\n print(\"Received: \", response)\n\n writer.close()\n await writer.wait_closed()\n\nif __name__ == '__main__':\n asyncio.run(main())\n```\n\n### Server\n\nHere is a trivial C# echo server for the clients.\n\n```csharp\nusing System;\nusing System.Net;\nusing System.Net.Security;\nusing System.Net.Sockets;\nusing System.Text;\n\nnamespace NegotiateStreamServer\n{\n internal class Program\n {\n static void Main(string[] args)\n {\n var listener = new TcpListener(IPAddress.Any, 8181);\n listener.Start();\n\n while (true)\n {\n Console.WriteLine(\"Listening ...\");\n var client = listener.AcceptTcpClient();\n\n try\n {\n Console.WriteLine(\"... Client connected.\");\n\n Console.WriteLine(\"Authenticating...\");\n var stream = new NegotiateStream(client.GetStream(), false);\n stream.AuthenticateAsServer();\n\n Console.WriteLine(\n \"... {0} authenticated using {1}\",\n stream.RemoteIdentity.Name,\n stream.RemoteIdentity.AuthenticationType);\n\n var buf = new byte[4096];\n for (var i = 0; i < 3; ++i)\n {\n var bytesRead = stream.Read(buf, 0, buf.Length);\n var message = Encoding.UTF8.GetString(buf, 0, bytesRead);\n Console.WriteLine(message);\n stream.Write(buf, 0, bytesRead);\n }\n stream.Close();\n }\n catch (Exception ex)\n {\n Console.WriteLine(ex.ToString());\n }\n }\n }\n }\n}\n```\n\n## Acknowledgements\n\nThe library uses the [pyspnego](https://github.com/jborean93/pyspnego) library,\nand takes many ideas from [net.tcp-proxy](https://github.com/ernw/net.tcp-proxy).\n",
"bugtrack_url": null,
"license": "Apache-2.0",
"summary": "A python client for .Net NegotiateStream",
"version": "0.1.0",
"split_keywords": [],
"urls": [
{
"comment_text": "",
"digests": {
"md5": "41c7812d4cb7378bcb811983a8926123",
"sha256": "32fe7f4db7eefd02db4770c6c161d85ed396221490f41a8b682280f1743bac03"
},
"downloads": -1,
"filename": "jetblack_negotiate_stream-0.1.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "41c7812d4cb7378bcb811983a8926123",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8,<4.0",
"size": 7410,
"upload_time": "2022-12-21T14:59:11",
"upload_time_iso_8601": "2022-12-21T14:59:11.117469Z",
"url": "https://files.pythonhosted.org/packages/a9/5a/74e0a75df7019952a8734160e09699565883b0b61a4a2df6152292251aa4/jetblack_negotiate_stream-0.1.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"md5": "417210b136dd2ba7086fbf50b27d9e17",
"sha256": "be5e8fa9dc678e3564e4f41c02a7abc2870a915132fa5f9f2ff9d78b1a09abce"
},
"downloads": -1,
"filename": "jetblack_negotiate_stream-0.1.0.tar.gz",
"has_sig": false,
"md5_digest": "417210b136dd2ba7086fbf50b27d9e17",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8,<4.0",
"size": 5650,
"upload_time": "2022-12-21T14:59:13",
"upload_time_iso_8601": "2022-12-21T14:59:13.214751Z",
"url": "https://files.pythonhosted.org/packages/a1/8a/874d79fd0b61aa0573b3006380ef0bd07a7ee1bec96826c9238ccfb2727d/jetblack_negotiate_stream-0.1.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2022-12-21 14:59:13",
"github": true,
"gitlab": false,
"bitbucket": false,
"github_user": "rob-blackbourn",
"github_project": "jetblack-negotiate-stream",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "jetblack-negotiate-stream"
}