feat: cleanup README and add test-ca files for testing

This commit is contained in:
Mahdi Dibaiee 2021-03-19 17:13:53 +00:00
parent 36587867c2
commit 1dcb2ebef1
12 changed files with 200 additions and 84 deletions

View File

@ -1,15 +1,53 @@
tls-forward-proxy tlsproxy
================= =================
A most basic forward proxy using [Tokio](https://tokio.rs/) [TcpStreams](https://docs.rs/tokio/1.3.0/tokio/net/struct.TcpStream.html). A most basic TLS man-in-the-middle forward proxy using [rustls](https://github.com/ctz/rustls) .
Sample usage: Sample usage:
``` ```
$ cargo build && cargo run $ cargo build && cargo run -- \
--chaincert test-ca/end.fullchain \
--key test-ca/end.key \
--cacert test-ca/ca.cert \
--replace 's/foo/bar' \
--verbose
Listening on 127.0.0.1:8080 Listening on 127.0.0.1:8080
``` ```
Send requests to your destination through this proxy: Send requests to your destination through this proxy:
``` ```
curl --cacert /path/to/cert.pem https://test.dev:5000 -x http://proxy.dev:8080 --proxytunnel curl https://testserver.com:5000 \
--cacert test-ca/ca.cert \
-x http://127.0.0.1:8080 --proxytunnel \
--verbose
```
Please note this means you need to have a server running at testserver.com:5000, to do so, you can use the sample python server provided:
```
cd sample-server
pyenv local # 3.6.4 version
pip install flask
python main.py
```
You will then have a server running on 127.0.0.1:5000. You can then point testserver.com to this server by editing your `/etc/hosts`:
```
127.0.0.1 testserver.com
```
Then you can try sending a request with a replacement:
```
curl 'https://testserver.com:5000?foo=foo' \
--cacert test-ca/ca.cert \
-x http://127.0.0.1:8080 --proxytunnel \
--verbose
```
The python server will log:
```
GET https://testserver.com:5000/?bar=bar
``` ```

View File

@ -0,0 +1 @@
3.6.4

1
sample-server/fullchain.pem Symbolic link
View File

@ -0,0 +1 @@
../test-ca/end.fullchain

1
sample-server/key.pem Symbolic link
View File

@ -0,0 +1 @@
../test-ca/end.key

14
sample-server/main.py Normal file
View File

@ -0,0 +1,14 @@
from flask import Flask, request
app = Flask(__name__)
app.debug = True
@app.route('/')
def hello_world():
print(f"{request.method} {request.url}")
return 'Hello, World!'
app.run(
debug=True,
ssl_context=('fullchain.pem', 'key.pem'),
port=5000
)

View File

@ -1,4 +1,3 @@
use env_logger;
use rustls_pemfile; use rustls_pemfile;
use std::io::BufReader; use std::io::BufReader;
use std::{fs, str}; use std::{fs, str};
@ -18,6 +17,7 @@ pub struct Args {
pub fn args() -> Args { pub fn args() -> Args {
let version = env!("CARGO_PKG_NAME").to_string() + ", version: " + env!("CARGO_PKG_VERSION"); let version = env!("CARGO_PKG_NAME").to_string() + ", version: " + env!("CARGO_PKG_VERSION");
// enable this to get trace logs from rustls
//env_logger::Builder::new().parse_filters("trace").init(); //env_logger::Builder::new().parse_filters("trace").init();
let matches = App::new("tlsproxy") let matches = App::new("tlsproxy")

View File

@ -1,8 +1,6 @@
use std::sync::{Arc};
use std::net::SocketAddr; use std::net::SocketAddr;
use tokio::net::{TcpListener}; use tokio::net::{TcpListener};
use rustls::{ServerConfig, ClientConfig, NoClientAuth}; use rustls::{ServerConfig, ClientConfig, NoClientAuth};
use webpki_roots;
mod command; mod command;
mod proxy; mod proxy;
@ -30,10 +28,6 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
.add_pem_file(&mut command::read_file(&args.cacert)).unwrap(); .add_pem_file(&mut command::read_file(&args.cacert)).unwrap();
println!("{} certificates added, {} unused", added, unused); println!("{} certificates added, {} unused", added, unused);
let (added, unused) = client_config
.root_store
.add_pem_file(&mut command::read_file(&args.chaincert)).unwrap();
println!("{} certificates added, {} unused", added, unused);
let tcp_listener = TcpListener::bind(addr).await?; let tcp_listener = TcpListener::bind(addr).await?;
loop { loop {

View File

@ -6,7 +6,7 @@ use webpki;
use std::str; use std::str;
use std::io; use std::io;
use std::io::{Write, Read}; use std::io::{Write, Read};
use rustls::{ServerConfig, ClientConfig, Session}; use rustls::{ServerConfig, ClientConfig};
mod ops; mod ops;
@ -71,7 +71,6 @@ pub async fn proxy(mut incoming: TcpStream, client_config: ClientConfig, server_
} }
}); });
outgoing_tls.write_all(&incoming_buf)?; outgoing_tls.write_all(&incoming_buf)?;
println!("done writing");
}, },
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {}, Err(e) if e.kind() == io::ErrorKind::WouldBlock => {},

View File

@ -1,8 +1,6 @@
use tokio::net::{TcpStream}; use tokio::net::{TcpStream};
use std::error::Error; use std::error::Error;
use std::{io}; use std::{io};
use std::io::{Read, Write};
use rustls::{Session};
pub async fn read(stream: &TcpStream, buf: &mut Vec<u8>) -> Result<(), Box<dyn Error>> { pub async fn read(stream: &TcpStream, buf: &mut Vec<u8>) -> Result<(), Box<dyn Error>> {
loop { loop {
@ -46,72 +44,3 @@ pub async fn write(stream: &TcpStream, buf: &[u8]) -> Result<(), Box<dyn Error>>
Ok(()) Ok(())
} }
pub fn sync_read<T: Read>(stream: &mut T, buf: &mut Vec<u8>) -> Result<usize, Box<dyn Error>> {
loop {
match stream.read(buf) {
Ok(n) => {
buf.truncate(n);
return Ok(n);
}
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
continue;
}
Err(e) => {
println!("error reading from stream: {:#?}", e);
return Err(e.into());
}
}
}
}
pub fn sync_write<T: Write>(stream: &mut T, buf: &[u8]) -> Result<usize, Box<dyn Error>> {
loop {
match stream.write(buf) {
Ok(n) => {
return Ok(n);
}
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
continue;
}
Err(e) => {
println!("error writing to stream: {:#?}", e);
return Err(e.into());
}
}
}
}
pub fn tls_read<T: Session, R: Read>(stream: &mut T, buf: &mut R) -> Result<usize, Box<dyn Error>> {
loop {
match stream.read_tls(buf) {
Ok(n) => {
return Ok(n);
}
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
continue;
}
Err(e) => {
println!("error reading from stream: {:#?}", e);
return Err(e.into());
}
}
}
}
pub fn tls_write<T: Session, W: Write>(stream: &mut T, buf: &mut W) -> Result<usize, Box<dyn Error>> {
loop {
match stream.write_tls(buf) {
Ok(n) => {
return Ok(n);
}
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
continue;
}
Err(e) => {
println!("error writing to stream: {:#?}", e);
return Err(e.into());
}
}
}
}

30
test-ca/ca.cert Normal file
View File

@ -0,0 +1,30 @@
-----BEGIN CERTIFICATE-----
MIIFFTCCAv2gAwIBAgIUGXgsGl4sGhiPwUPOme5w5jJscVQwDQYJKoZIhvcNAQEL
BQAwGjEYMBYGA1UEAwwPcG9ueXRvd24gUlNBIENBMB4XDTE5MDYwOTE3MTUxMVoX
DTI5MDYwNjE3MTUxMVowGjEYMBYGA1UEAwwPcG9ueXRvd24gUlNBIENBMIICIjAN
BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEArpW4WHfSmTCMvnDJaCRL5BR2knDc
C+T4/wv7i//zsBL3q0YVdBpllWCZx8TSfuXT/1o/Zxdt+H1ZjnH8yuwuHOToE3W0
+Y/912hAPiD+d79A+MtqVX1gayjHDy9HybznTN2Onh1ZhWyks0FLL5SB0vVCLwXP
W7tcyY0w6wn3RTw4nBu7co5a280cHgHv5V3XeNgteQrnkXE9TfuqqUZwVt0v1jr1
bk1KaDDD+36wfeO70Q6CeLwKPhN0mPDHdj/lecj4efl0l1B0ehHxLkLuztqtkCF6
+yMoBrEyzJjO6TGMSTMsnw4F9bA15jkIIEQs5FhwVHFOTmu5BG+j37umoy27k5As
5cV0/djr3r8WhBZ6w2+XjvbAdJPgqc+Xobnx1i5GI4noRC4G4Cl0Vx+iRru1/nsE
xPazz9UVkiUsDlN9n49f1pik9z74Box6CD6IZVb8h4vV7um+0R4/eErVf/Cyf1Xe
axVaOgA/CD0ucLdSfDz61O/2PG3P7YjWY1R9zr35e8pakgyFPXnWMbsfniuGkM0X
6lG743Q7yVt38/HuAqi1AGq+r/AKisrezt97UNm0CryuEk02y4SVLbhuG/V6mBCh
4Va3KD1ZOgfJcTZbuxebIz6W+5jL80hE0YwBBs8vSv8Fo5N7dYNSNvyMxkoPh8Gw
CAFyqt5zBLGCEvkCAwEAAaNTMFEwHQYDVR0OBBYEFDjj6hEpDZdjAIdvd9Moe3un
RvJWMB8GA1UdIwQYMBaAFDjj6hEpDZdjAIdvd9Moe3unRvJWMA8GA1UdEwEB/wQF
MAMBAf8wDQYJKoZIhvcNAQELBQADggIBACu+Ct6J+Dh3EXFOgjW3gN3CaD0UK0kW
03u7znNAJBAYhi+MXSTPBdrxYGOPkHF1Jl15qb2fc58+0nakf8bvah8kgxi2ujX8
vrPBjsAv/ixPvUrcol9ZGrseP5DdFS8pw1FoDR/JdgNCdSM4xC3GSk2SLYs+QSJx
YG3nQLOAZEvnbiZ4zBZ0wVct4w9jrxtqdq2eS8cLoRVx715MzKPBCGEccYu/py5a
gkyclr16s0mb+wN49of34AQ+xXguHZGNZzCy6PTbx0IC+sRVe82+RZkj21JG+AFM
9s+vLgRdtWIEZW1AIYbUUbhuvsne+sidZW5XQuFp1V4LlQbO49oEhrPMBn+oHKg5
MWIe68bjkqDSnDzG+TEBTWiAhyyGyZcebfCs72DGbYrfKt1uTyi+groumPnvQfJB
y3kqy2pUFeEkNJkx4BfYL+N7I07s9WTy8UMoqn/OLuyqoFaYMd9XMaOOx3xTy8aw
pUJ69c3VI66W/Ii1ypk2EPUImWpG/n89Y/8Mk1NbesaZLk9feTDfbM4VNPkQU+7T
3DNQxPSswSh4nXGURwC46SOu2s1lRA98ZXkP5XhUvTuvfg/e4suq26OqjORHQ5zI
57NP+uHRrfHGlrQ196j1Maw7W7vkocbEv8/06v6s54CG8ezzD2nt1QrLJqSpUqHo
qolvgn/PK+gg
-----END CERTIFICATE-----

81
test-ca/end.fullchain Normal file
View File

@ -0,0 +1,81 @@
-----BEGIN CERTIFICATE-----
MIIEADCCAmigAwIBAgICAcgwDQYJKoZIhvcNAQELBQAwLDEqMCgGA1UEAwwhcG9u
eXRvd24gUlNBIGxldmVsIDIgaW50ZXJtZWRpYXRlMB4XDTE5MDYwOTE3MTUxMloX
DTI0MTEyOTE3MTUxMlowGTEXMBUGA1UEAwwOdGVzdHNlcnZlci5jb20wggEiMA0G
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDdwbEc6ZKih4mQA916JwctBZgnRmzO
KERrzlHjfzy8ZfsUJLENQBN8s3RVRwPThReHdp8bpiVRrNmMRxyXoa5oqxWDjXOu
5W4hoISIMbOAq4Kj8G+eS0UKwypKHwJ1aUzEjWQGKxNpIYvcGqwYpN1Yi1+qTgLg
2qw1ENtBhrWHhmQruGqDtQTQLe2tbcOuGhIL0cyWIRtEWHWL/wb1Akzhm31WQF+m
URtYvYonA/Ta7ErONXCxsEXndTR4iT/XognnOhTJ+uIinNwn52y9Te7MYix6SDBE
VeKZx9v3iOYU81zXf+WaxNqZvTfbPjkLsXiymOgVfGQcO4hiQeLoJIHXAgMBAAGj
gb4wgbswDAYDVR0TAQH/BAIwADALBgNVHQ8EBAMCBsAwHQYDVR0OBBYEFEweXJqS
BzVcf/00QeOC29OwBQszMEIGA1UdIwQ7MDmAFEKPy8hHZVazpvIsxFcGo4YrkEkw
oR6kHDAaMRgwFgYDVQQDDA9wb255dG93biBSU0EgQ0GCAXswOwYDVR0RBDQwMoIO
dGVzdHNlcnZlci5jb22CFXNlY29uZC50ZXN0c2VydmVyLmNvbYIJbG9jYWxob3N0
MA0GCSqGSIb3DQEBCwUAA4IBgQCViHp2pLcIMzl/wN+sULznLYZvrlynU4AHnL8/
ba6iSAM6EMlrcu11+UBQglHIN2BEn+Jjas+HT1sQOIOixMgjrMBgirLez8n5DN66
o5aK5bu23GjQvzq5JEh0skQDHtSFX0YRwqXIhi1spGtObsnoupxJNBQbdAcDv50/
m6/8WXcPbXBnR+wRywFmjb6+OSVNgCRtBFTbR5XRVHMPEwvSk4hVj4jimlnPHZYL
3VatCPtZr6iaLZl9E64BbS+J4vPQ0Z/2JMUjtXCuj19k8LO2TTTBz54QVoMF5jrZ
xotneq+wmPH3lmozEOmyj4+4CmoyNz+RDhrlok84x3g4YEKUQyK1V4ROi9DtL1CV
VoLfHSwS9SiDdD/Qn2n7RICn6DP2lHozICyHX0Op4W+vETHho7Flsw21bMisAGrl
wwQ7UYU4XfPOC9hQoCvU60uVe7z+uZvlBY8RwmcW4iFIbfCcPT6Hrom5F1X4Z/dm
zDW8ZhLDsjUY/D4lUeWjbO1RCHI=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEnzCCAoegAwIBAgIBezANBgkqhkiG9w0BAQsFADAaMRgwFgYDVQQDDA9wb255
dG93biBSU0EgQ0EwHhcNMTkwNjA5MTcxNTEyWhcNMjkwNjA2MTcxNTEyWjAsMSow
KAYDVQQDDCFwb255dG93biBSU0EgbGV2ZWwgMiBpbnRlcm1lZGlhdGUwggGiMA0G
CSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQCj/tOFeSW3WB+TtuLCR1L/84lZytFw
zbpzOTGB1kPEKNbrMsv3lHXm5bHa8Bl3k113k7Hi7OAt/nkMm05s8LcUoovhaG5C
G7tjzL+ld1nO74gNS3IQHCzxRdRwIgaDZHyICfBQBfB9/m+9z3yRtOKWJl6i/MT9
HRN6yADW/8gHFlMzRkCKBjIKXehKsu8cbtB+5MukwtXI4rKf9aYXZQOEUn1kEwQJ
ZIKBXR0eyloQiZervUE7meRCTBvzXT9VoSEX49/mempp4hnfdHlRNzre4/tphBf1
fRUdpVXZ3DvmzoHdXRVzxx3X5LvDpf7Eb3ViGkXDFwkSfHEhkRnAl4lIzTH/1F25
stmT8a0PA/lCNMrzJBzkLcuem1G1uMHoQZo1f3OpslJ8gHbE9ZlIbIKmpmJS9oop
Vh1BH+aOy5doCrF8uOLTQ3d5CqA/EZMGahDHy7IkeNYmG/RXUKNltv+r95gwuRP+
9UIJ9FTa4REQbIpGWP5XibI6x4LqLTJj+VsCAwEAAaNeMFwwHQYDVR0OBBYEFEKP
y8hHZVazpvIsxFcGo4YrkEkwMCAGA1UdJQEB/wQWMBQGCCsGAQUFBwMBBggrBgEF
BQcDAjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIB/jANBgkqhkiG9w0BAQsFAAOC
AgEAMzTRDLBExVFlw98AuX+pM+/R2Gjw5KFHvSYLKLbMRfuuZK1yNYYaYtNrtF+V
a53OFgaZj56o7tXc2PB8kw4MELD0ViR8Do2bvZieFcEe4DwhdjGCjuLehVLT29qI
7T3N/JkJ5daemKZcRB6Ne0F4+6QlVVNck28HUKbQThl88RdwLUImmSAfgKSt6uJ5
wlH7wiYQR2vPXwSuEYzwot+L/91eBwuQr4Lovx9+TCKTbwQOKYjX4KfcOOQ1rx0M
IMrvwWqnabc6m1F0O6//ibL0kuFkJYEgOH2uJA12FBHO+/q2tcytejkOWKWMJj6Y
2etwIHcpzXaEP7fZ75cFGqcE3s7XGsweBIPLjMP1bKxEcFKzygURm/auUuXBCFBl
E16PB6JEAeCKe/8VFeyucvjPuQDWB49aq+r2SbpbI4IeZdz/QgEIOb0MpwStrvhH
9f/DtGMbjvuAEkRoOorK4m5k4GY3LsWTR2bey27AXk8N7pKarpu2N7ChBPm+EV0Y
H+tAI/OfdZuNUCES00F5UAFdU8zBUZo19ao2ZqfEADimE7Epk2s0bUe4GSqEXJp6
68oVSMhZmMf/RCSNlr97f34sNiUA1YJ0JbCRZmw8KWNm9H1PARLbrgeRBZ/k31Li
WLDr3fiEVk7SGxj3zo94cS6AT55DyXLiSD/bFmL1QXgZweA=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFFTCCAv2gAwIBAgIUGXgsGl4sGhiPwUPOme5w5jJscVQwDQYJKoZIhvcNAQEL
BQAwGjEYMBYGA1UEAwwPcG9ueXRvd24gUlNBIENBMB4XDTE5MDYwOTE3MTUxMVoX
DTI5MDYwNjE3MTUxMVowGjEYMBYGA1UEAwwPcG9ueXRvd24gUlNBIENBMIICIjAN
BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEArpW4WHfSmTCMvnDJaCRL5BR2knDc
C+T4/wv7i//zsBL3q0YVdBpllWCZx8TSfuXT/1o/Zxdt+H1ZjnH8yuwuHOToE3W0
+Y/912hAPiD+d79A+MtqVX1gayjHDy9HybznTN2Onh1ZhWyks0FLL5SB0vVCLwXP
W7tcyY0w6wn3RTw4nBu7co5a280cHgHv5V3XeNgteQrnkXE9TfuqqUZwVt0v1jr1
bk1KaDDD+36wfeO70Q6CeLwKPhN0mPDHdj/lecj4efl0l1B0ehHxLkLuztqtkCF6
+yMoBrEyzJjO6TGMSTMsnw4F9bA15jkIIEQs5FhwVHFOTmu5BG+j37umoy27k5As
5cV0/djr3r8WhBZ6w2+XjvbAdJPgqc+Xobnx1i5GI4noRC4G4Cl0Vx+iRru1/nsE
xPazz9UVkiUsDlN9n49f1pik9z74Box6CD6IZVb8h4vV7um+0R4/eErVf/Cyf1Xe
axVaOgA/CD0ucLdSfDz61O/2PG3P7YjWY1R9zr35e8pakgyFPXnWMbsfniuGkM0X
6lG743Q7yVt38/HuAqi1AGq+r/AKisrezt97UNm0CryuEk02y4SVLbhuG/V6mBCh
4Va3KD1ZOgfJcTZbuxebIz6W+5jL80hE0YwBBs8vSv8Fo5N7dYNSNvyMxkoPh8Gw
CAFyqt5zBLGCEvkCAwEAAaNTMFEwHQYDVR0OBBYEFDjj6hEpDZdjAIdvd9Moe3un
RvJWMB8GA1UdIwQYMBaAFDjj6hEpDZdjAIdvd9Moe3unRvJWMA8GA1UdEwEB/wQF
MAMBAf8wDQYJKoZIhvcNAQELBQADggIBACu+Ct6J+Dh3EXFOgjW3gN3CaD0UK0kW
03u7znNAJBAYhi+MXSTPBdrxYGOPkHF1Jl15qb2fc58+0nakf8bvah8kgxi2ujX8
vrPBjsAv/ixPvUrcol9ZGrseP5DdFS8pw1FoDR/JdgNCdSM4xC3GSk2SLYs+QSJx
YG3nQLOAZEvnbiZ4zBZ0wVct4w9jrxtqdq2eS8cLoRVx715MzKPBCGEccYu/py5a
gkyclr16s0mb+wN49of34AQ+xXguHZGNZzCy6PTbx0IC+sRVe82+RZkj21JG+AFM
9s+vLgRdtWIEZW1AIYbUUbhuvsne+sidZW5XQuFp1V4LlQbO49oEhrPMBn+oHKg5
MWIe68bjkqDSnDzG+TEBTWiAhyyGyZcebfCs72DGbYrfKt1uTyi+groumPnvQfJB
y3kqy2pUFeEkNJkx4BfYL+N7I07s9WTy8UMoqn/OLuyqoFaYMd9XMaOOx3xTy8aw
pUJ69c3VI66W/Ii1ypk2EPUImWpG/n89Y/8Mk1NbesaZLk9feTDfbM4VNPkQU+7T
3DNQxPSswSh4nXGURwC46SOu2s1lRA98ZXkP5XhUvTuvfg/e4suq26OqjORHQ5zI
57NP+uHRrfHGlrQ196j1Maw7W7vkocbEv8/06v6s54CG8ezzD2nt1QrLJqSpUqHo
qolvgn/PK+gg
-----END CERTIFICATE-----

28
test-ca/end.key Normal file
View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDdwbEc6ZKih4mQ
A916JwctBZgnRmzOKERrzlHjfzy8ZfsUJLENQBN8s3RVRwPThReHdp8bpiVRrNmM
RxyXoa5oqxWDjXOu5W4hoISIMbOAq4Kj8G+eS0UKwypKHwJ1aUzEjWQGKxNpIYvc
GqwYpN1Yi1+qTgLg2qw1ENtBhrWHhmQruGqDtQTQLe2tbcOuGhIL0cyWIRtEWHWL
/wb1Akzhm31WQF+mURtYvYonA/Ta7ErONXCxsEXndTR4iT/XognnOhTJ+uIinNwn
52y9Te7MYix6SDBEVeKZx9v3iOYU81zXf+WaxNqZvTfbPjkLsXiymOgVfGQcO4hi
QeLoJIHXAgMBAAECggEATFl3xWCV3+eScUcjZf8x9UpLzJnutDwg8o0inJUeMC9c
dt77Jni9PN38W7ALnTPhmf45YaeeibRdYnLJYVuFVPwyeAynm7vaYzGE7+9MwixK
2m7Zv0JjDwWK9eIfUpVinPmhSo5iLHwkTy/PuNxqaSXzVgtt6kTfrZWUJ8ddkL8M
bMQTvSLByspyZq/9n6Xq4cy1kummrYgluGKrh6+b+3/ff4wTfF9txlecM+te0uoI
nu5jTRGGSouKKiOWLLkQNPCqrSmy/VfQLkacs3l8Y89Fo4TlBU6MEb02u+fCQ/58
q1E8Y7J1/Yjv2VTwav9q1EX9/ncA8b2C0K1Ylgh9QQKBgQD2bZwI03z4Zpo1TnxU
d4r0qWVExY7fP9BfJPEn3KE4zlPXbiiNazMprdFoIUEtKNcl77ZYVcvNLCUDOWzj
maYtVJm7wuUPcJQU2becuw6N7yZJd9mfXPOiBWmv8Df5AJJymdUcXqMySi9eFr1m
SwFhrsFRTs8Fo0bGrw8UTMM72QKBgQDmXsHt80+F7YuVUrVMuhTTr/DqwHgqyCQ1
zQXuOeGDaFPSYzgk6XEPPJU+Kil+bFIY7DaMokVHWvJJ9e9iF8fjflSnp2pp1BWa
t3D+I3zfX+SCioD8KXcFiMfoH9bqIfBzaQfeMNgqMbR0fpsf/l0n/cwJRQ4KGU7s
puXqY0aNLwKBgEa2kU3fEj9dgebGDNtYKmGmsk6XujXJ5AtJWIItx327h0eMbsqV
9mqBXFPbJw7EZ2iVbufORtsrTbutINf24T6kxjCg7oYNshCBoTSyYKzN8VinsaUP
UUIu93LrJcSoK14DUqn/ZikqLIl9UQAnic/0C7k/OhzOC6M73MHgfS2RAoGBAM0O
y9DjI4YzTGw+kuMZQDCuC+TqLgzm2lSJix3ip7oww2wipXc11E2bv7z2Crld8jX9
DRFh4AkEC2eKYusN//+gE/qoKzDId/KgFxQgwqaS1PTeFLJgtnFWr5sPvF3sl/wj
Ib3F/KSSWe7YQ3zXDlTqtRQLQ9P5cydz6HQaqlJBAoGBAL3xNfmStaUFV4moms64
fZ755LqQwN5rwjZLxmRTsOgVI/KPEg44xvbcG885eNW+JhYSPUyvkrP6Qb+I8PEN
qdMPUgTetOrnA4T9yf7+U/xHghDSb3BEQKyGlrbRO2GB/iGa3xHD963WozDzeAfQ
uxrLrUaQjPsf2AEhrHk8slgM
-----END PRIVATE KEY-----