Patches by Sergei Golovin

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@38188 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
rfm 2014-11-20 09:43:21 +00:00
parent bc50e26e2a
commit 8127f31ca3
6 changed files with 639 additions and 47 deletions

View file

@ -1,3 +1,18 @@
2014-11-20 Sergei Golovin <Golovin.SV@gmail.com>
* Source/GSSocketStream.m:
* Tests/base/NSURL/Helpers/capture.m:
* Tests/base/NSURL/testKey.pem:
* Tests/base/NSURL/test02.m:
* Tests/base/NSURL/testCert.pem:
Implement TLS on server socket and make use of it in HTTPS URL helper
code. Also, permit NSStream zero length write (it seems OSX allows
it).
2014-11-20 Richard Frith-Macdonald <rfm@gnu.org>
* Source/NSRunLoop.m: Avoinf some unnecessary retain/release cycles.
2014-11-05 Richard Frith-Macdonald <rfm@gnu.org>
* Source/GSTLS.m: If a user default value is removed while running,

View file

@ -354,6 +354,17 @@ GSPrivateSockaddrSetup(NSString *machine, uint16_t port,
@public
GSTLSSession *session;
}
/**
* Populates the dictionary 'dict', copying in all the properties
* of the supplied streams. If a property is set for both then
* the output stream's one has precedence.
*/
+ (void) populateProperties: (NSMutableDictionary**)dict
withTLSPriority: (NSString*)pri
fromInputStream: (NSStream*)i
orOutputStream: (NSStream*)o;
@end
/* Callback to allow the TLS code to pull data from the remote system.
@ -443,6 +454,37 @@ static NSArray *keys = nil;
}
}
+ (void) populateProperties: (NSMutableDictionary**)dict
withTLSPriority: (NSString*)pri
fromInputStream: (NSStream*)i
orOutputStream: (NSStream*)o
{
NSString *str;
NSMutableDictionary *opts = *dict;
NSUInteger count;
if (NULL != dict)
{
if (nil != pri)
{
[opts setObject: pri forKey: GSTLSPriority];
}
count = [keys count];
while (count-- > 0)
{
NSString *key = [keys objectAtIndex: count];
str = [o propertyForKey: key];
if (nil == str) str = [i propertyForKey: key];
if (nil != str) [opts setObject: str forKey: key];
}
}
else
{
NSWarnLog(@"%@ requires not nil 'dict'", NSStringFromSelector(_cmd));
}
}
+ (void) tryInput: (GSSocketInputStream*)i output: (GSSocketOutputStream*)o
{
NSString *tls;
@ -537,10 +579,10 @@ static NSArray *keys = nil;
{
NSString *str;
NSMutableDictionary *opts;
NSUInteger count;
BOOL server;
server = [[o propertyForKey: @"IsServer"] boolValue];
// Check whether the input stream has been accepted by a listening socket
server = [[i propertyForKey: @"IsServer"] boolValue];
str = [o propertyForKey: NSStreamSocketSecurityLevelKey];
if (nil == str) str = [i propertyForKey: NSStreamSocketSecurityLevelKey];
@ -580,16 +622,10 @@ static NSArray *keys = nil;
* properties. GSTLSPriority overrides NSStreamSocketSecurityLevelKey.
*/
opts = [NSMutableDictionary new];
if (nil != str) [opts setObject: str forKey: GSTLSPriority];
count = [keys count];
while (count-- > 0)
{
NSString *key = [keys objectAtIndex: count];
str = [o propertyForKey: key];
if (nil == str) str = [i propertyForKey: key];
if (nil != str) [opts setObject: str forKey: key];
}
[[self class] populateProperties: &opts
withTLSPriority: str
fromInputStream: i
orOutputStream: o];
session = [[GSTLSSession alloc] initWithOptions: opts
direction: (server ? NO : YES)
@ -2244,16 +2280,32 @@ setNonBlocking(SOCKET fd)
- (NSInteger) write: (const uint8_t *)buffer maxLength: (NSUInteger)len
{
if (len == 0)
{
/*
* The method allows the 'len' equal to 0. In this case the 'buffer'
* is ignored. This can be useful if there is a necessity to postpone
* actual writing (for no data are ready for example) without leaving
* the stream in the state of unhandled NSStreamEventHasSpaceAvailable
* (to keep receiving of that event from a runloop).
* The delegate's -[stream:handleEvent:] would keep calling of
* -[write: NULL maxLength: 0] until the delegate's state allows it
* to write actual bytes.
* The downside of that is that it produces a busy wait ... with the
* run loop immediately notifying the stream that it has space to
* write, so care should be taken to ensure that the delegate has a
* near constant supply of data to write, or has some mechanism to
* detect that no more data is arriving, and shut down.
*/
_events &= ~NSStreamEventHasSpaceAvailable;
return 0;
}
if (buffer == 0)
{
[NSException raise: NSInvalidArgumentException
format: @"null pointer for buffer"];
}
if (len == 0)
{
[NSException raise: NSInvalidArgumentException
format: @"zero byte length write requested"];
}
if (_handler == nil)
return [self _write: buffer maxLength: len];
@ -2550,6 +2602,11 @@ setNonBlocking(SOCKET fd)
- (void) acceptWithInputStream: (NSInputStream **)inputStream
outputStream: (NSOutputStream **)outputStream
{
NSArray *keys;
NSUInteger count;
NSMutableDictionary *opts;
NSString *str;
GSSocketStream *ins = AUTORELEASE([[self _inputStreamClass] new]);
GSSocketStream *outs = AUTORELEASE([[self _outputStreamClass] new]);
/* Align on a 2 byte boundary for a 16bit port number in the sockaddr
@ -2589,6 +2646,31 @@ setNonBlocking(SOCKET fd)
* connection (client).
*/
[ins setProperty: @"YES" forKey: @"IsServer"];
str = [self propertyForKey: NSStreamSocketSecurityLevelKey];
if(nil != str)
{
opts = [NSMutableDictionary new];
[opts setObject: str forKey: NSStreamSocketSecurityLevelKey];
// copy the properties in the 'opts'
[GSTLSHandler populateProperties: &opts
withTLSPriority: str
fromInputStream: self
orOutputStream: nil];
// and set the input/output streams's properties from the 'opts'
keys = [opts allKeys];
count = [keys count];
while(count-- > 0)
{
NSString *key = [keys objectAtIndex: count];
str = [opts objectForKey: key];
[ins setProperty: str forKey: key];
[outs setProperty: str forKey: key];
}
[GSTLSHandler tryInput: (GSSocketInputStream *)ins output: (GSSocketOutputStream *)outs];
DESTROY(opts);
}
}
if (inputStream)
{

View file

@ -9,7 +9,12 @@
unsigned written;
BOOL readable;
BOOL writable;
BOOL isSecure; /* whether to use a secure TLS/SSL connection */
BOOL doRespond; /* the request is read */
BOOL done; /* the response is written */
NSString *file; /* the file to write the captured request */
}
- (id)initWithSecure:(BOOL)flag;
- (int) runTest;
@end
@ -20,14 +25,27 @@
RELEASE(capture);
RELEASE(op);
RELEASE(ip);
DESTROY(file);
[super dealloc];
}
- (id)initWithSecure:(BOOL)flag
{
if((self = [super init]) != nil)
{
isSecure = flag;
capture = [NSMutableData new];
doRespond = NO;
done = NO;
file = nil;
}
return self;
}
- (id) init
{
capture = [NSMutableData new];
return self;
return [self initWithSecure: NO];
}
- (int) runTest
@ -36,12 +54,14 @@
NSRunLoop *rl = [NSRunLoop currentRunLoop];
NSHost *host = [NSHost hostWithName: @"localhost"];
NSStream *serverStream;
NSString *file;
int port = [[defs stringForKey: @"Port"] intValue];
isSecure = [[defs stringForKey: @"Secure"] boolValue];
if (port == 0) port = 54321;
file = [defs stringForKey: @"FileName"];
RETAIN(file);
if (file == nil) file = @"Capture.dat";
serverStream = [GSServerStream serverStreamToAddr: [host address] port: port];
@ -50,16 +70,20 @@
NSLog(@"Failed to create server stream");
return 1;
}
if(isSecure)
{
[serverStream setProperty: NSStreamSocketSecurityLevelTLSv1 forKey: NSStreamSocketSecurityLevelKey];
[serverStream setProperty: @"testCert.pem" forKey: GSTLSCertificateFile];
[serverStream setProperty: @"testKey.pem" forKey: GSTLSCertificateKeyFile];
}
[serverStream setDelegate: self];
[serverStream scheduleInRunLoop: rl forMode: NSDefaultRunLoopMode];
[serverStream open];
[rl runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 30]];
if ([capture writeToFile: file atomically: YES] == NO)
while(!done)
{
NSLog(@"Unable to write captured data to '%@'", file);
return 1;
[rl runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.1]];
}
return 0;
@ -68,8 +92,7 @@
- (void) stream: (NSStream *)theStream handleEvent: (NSStreamEvent)streamEvent
{
NSRunLoop *rl = [NSRunLoop currentRunLoop];
NSString *resp = @"HTTP/1.0 204 Empty success response\r\n\r\n";
NSString *resp = @"HTTP/1.0 204 Empty success response\r\n\r\n";
// NSLog(@"Event %p %d", theStream, streamEvent);
switch (streamEvent)
@ -97,6 +120,13 @@
}
if (theStream == ip)
{
NSRange r1;
NSRange r2;
NSString *headers;
NSString *tmp1;
NSString *tmp2;
NSUInteger contentLength;
readable = YES;
while (readable == YES)
{
@ -113,39 +143,84 @@
[capture appendBytes: buffer length: readSize];
}
}
// the following chunk ensures that the captured data are written only
// when all request's bytes are read... it waits for full headers and
// reads the Content-Length's value then waits for the number of bytes
// equal to that value is read
tmp1 = [[NSString alloc] initWithData: capture
encoding: NSUTF8StringEncoding];
// whether the headers are read
if((r1 = [tmp1 rangeOfString: @"\r\n\r\n"]).location != NSNotFound)
{
headers = [tmp1 substringToIndex: r1.location + 2];
if((r2 = [[headers lowercaseString] rangeOfString: @"content-length:"]).location != NSNotFound)
{
tmp2 = [headers substringFromIndex: r2.location + r2.length]; // content-length:<tmp2><end of headers>
if((r2 = [tmp2 rangeOfString: @"\r\n"]).location != NSNotFound)
{
// full line with content-length is present
tmp2 = [tmp2 substringToIndex: r2.location]; // number of content's bytes
contentLength = [tmp2 intValue];
if(r1.location + 4 + contentLength == [capture length]) // Did we get headers + body?
{
// full request is read so write it
if ([capture writeToFile: file atomically: YES] == NO)
{
NSLog(@"Unable to write captured data to '%@'", file);
}
doRespond = YES; // allows to write the response
theStream = op;
}
}
}
}
DESTROY(tmp1);
}
break;
if(!doRespond) break;
}
case NSStreamEventHasSpaceAvailable:
{
NSData *data;
NSAssert(theStream == op, @"Wrong stream for writing");
writable = YES;
data = [resp dataUsingEncoding: NSASCIIStringEncoding];
while (writable == YES && written < [data length])
if(doRespond)
{
int result = [op write: [data bytes] + written
maxLength: [data length] - written];
// if we have read all request's bytes
NSData *data;
if (result <= 0)
NSAssert(theStream == op, @"Wrong stream for writing");
writable = YES;
data = [resp dataUsingEncoding: NSASCIIStringEncoding];
while (writable == YES && written < [data length])
{
writable = NO;
int result = [op write: [data bytes] + written
maxLength: [data length] - written];
if (result <= 0)
{
writable = NO;
}
else
{
written += result;
}
}
else
if (written == [data length])
{
written += result;
[ip close];
[ip removeFromRunLoop: rl forMode: NSDefaultRunLoopMode];
[op close];
[op removeFromRunLoop: rl forMode: NSDefaultRunLoopMode];
done = YES;
}
}
if (written == [data length])
{
[op close];
[op removeFromRunLoop: rl forMode: NSDefaultRunLoopMode];
}
break;
}
case NSStreamEventEndEncountered:
{
{
[theStream close];
[theStream removeFromRunLoop: rl forMode: NSDefaultRunLoopMode];
NSLog(@"Server close %p", theStream);

320
Tests/base/NSURL/test02.m Normal file
View file

@ -0,0 +1,320 @@
#import <Foundation/Foundation.h>
#import "Testing.h"
#import "ObjectTesting.h"
int main()
{
#if GNUSTEP
NSAutoreleasePool *arp = [NSAutoreleasePool new];
unsigned i;
NSURL *url;
NSMutableString *m;
NSData *data;
NSString *str;
NSTask *t;
NSString *helpers;
NSString *capture;
NSMutableURLRequest *request;
NSHTTPURLResponse *response = nil;
NSError *error = nil;
NSFileManager *fm;
NSRange r;
NSString *file = @"Capture.dat";
fm = [NSFileManager defaultManager];
helpers = [fm currentDirectoryPath];
helpers = [helpers stringByAppendingPathComponent: @"Helpers"];
helpers = [helpers stringByAppendingPathComponent: @"obj"];
capture = [helpers stringByAppendingPathComponent: @"capture"];
m = [NSMutableString stringWithCapacity: 2048];
for (i = 0; i < 128; i++)
{
[m appendFormat: @"Hello %d\r\n", i];
}
t = [NSTask launchedTaskWithLaunchPath: capture
arguments: [NSArray arrayWithObjects:
nil]];
if (t != nil)
{
// Pause to allow server subtask to set up.
[NSThread sleepUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.5]];
// remove the captured data from a possible previous run
[fm removeItemAtPath: file error: NULL];
// making a POST request
url = [NSURL URLWithString: @"http://localhost:54321/"];
request = [NSMutableURLRequest requestWithURL: url];
data = [m dataUsingEncoding: NSUTF8StringEncoding];
[request setHTTPBody: data];
[request setHTTPMethod: @"POST"];
// sending the request
[NSURLConnection sendSynchronousRequest: request
returningResponse: &response
error: &error];
// analyzing the response
PASS(response != nil && [response statusCode] == 204,
"NSURLConnection synchronous load returns a response");
data = [NSData dataWithContentsOfFile: @"Capture.dat"];
str = [[NSString alloc] initWithData: data
encoding: NSUTF8StringEncoding];
r = [str rangeOfString: m];
PASS(r.location != NSNotFound,
"NSURLConnection capture test OK");
// Wait for server termination
[t terminate];
[t waitUntilExit];
DESTROY(str);
response = nil;
error = nil;
}
// the same but with secure connection (HTTPS)
t = [NSTask launchedTaskWithLaunchPath: capture
arguments: [NSArray arrayWithObjects:
@"-Secure", @"YES",
nil]];
if (t != nil)
{
// Pause to allow server subtask to set up.
[NSThread sleepUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.5]];
// remove the captured data from a possible previous run
[fm removeItemAtPath: file error: NULL];
// making a POST request
url = [NSURL URLWithString: @"https://localhost:54321/"];
request = [NSMutableURLRequest requestWithURL: url];
data = [m dataUsingEncoding: NSUTF8StringEncoding];
[request setHTTPBody: data];
[request setHTTPMethod: @"POST"];
// sending the request
[NSURLConnection sendSynchronousRequest: request
returningResponse: &response
error: &error];
// sending the request
PASS(response != nil && [response statusCode] == 204,
"NSURLConnection synchronous load returns a response");
data = [NSData dataWithContentsOfFile: @"Capture.dat"];
str = [[NSString alloc] initWithData: data
encoding: NSUTF8StringEncoding];
r = [str rangeOfString: m];
PASS(r.location != NSNotFound,
"NSURLConnection capture test OK");
// Wait for server termination
[t terminate];
[t waitUntilExit];
DESTROY(str);
}
[arp release]; arp = nil;
#endif
return 0;
}
#import <Foundation/Foundation.h>
#import "Testing.h"
#import "ObjectTesting.h"
int main()
{
#if GNUSTEP
NSAutoreleasePool *arp = [NSAutoreleasePool new];
unsigned i;
NSURL *url;
NSMutableString *m;
NSData *data;
NSString *str;
NSTask *t;
NSString *helpers;
NSString *capture;
NSMutableURLRequest *request;
NSHTTPURLResponse *response = nil;
NSError *error = nil;
NSFileManager *fm;
NSRange r;
NSString *file = @"Capture.dat";
fm = [NSFileManager defaultManager];
helpers = [fm currentDirectoryPath];
helpers = [helpers stringByAppendingPathComponent: @"Helpers"];
helpers = [helpers stringByAppendingPathComponent: @"obj"];
capture = [helpers stringByAppendingPathComponent: @"capture"];
m = [NSMutableString stringWithCapacity: 2048];
for (i = 0; i < 128; i++)
{
[m appendFormat: @"Hello %d\r\n", i];
}
t = [NSTask launchedTaskWithLaunchPath: capture
arguments: [NSArray arrayWithObjects:
nil]];
if (t != nil)
{
// Pause to allow server subtask to set up.
[NSThread sleepUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.5]];
// remove the captured data from a possible previous run
[fm removeItemAtPath: file error: NULL];
// making a POST request
url = [NSURL URLWithString: @"http://localhost:54321/"];
request = [NSMutableURLRequest requestWithURL: url];
data = [m dataUsingEncoding: NSUTF8StringEncoding];
[request setHTTPBody: data];
[request setHTTPMethod: @"POST"];
// sending the request
[NSURLConnection sendSynchronousRequest: request
returningResponse: &response
error: &error];
// analyzing the response
PASS(response != nil && [response statusCode] == 204,
"NSURLConnection synchronous load returns a response");
data = [NSData dataWithContentsOfFile: @"Capture.dat"];
str = [[NSString alloc] initWithData: data
encoding: NSUTF8StringEncoding];
r = [str rangeOfString: m];
PASS(r.location != NSNotFound,
"NSURLConnection capture test OK");
// Wait for server termination
[t terminate];
[t waitUntilExit];
DESTROY(str);
response = nil;
error = nil;
}
// the same but with secure connection (HTTPS)
t = [NSTask launchedTaskWithLaunchPath: capture
arguments: [NSArray arrayWithObjects:
@"-Secure", @"YES",
nil]];
if (t != nil)
{
// Pause to allow server subtask to set up.
[NSThread sleepUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.5]];
// remove the captured data from a possible previous run
[fm removeItemAtPath: file error: NULL];
// making a POST request
url = [NSURL URLWithString: @"https://localhost:54321/"];
request = [NSMutableURLRequest requestWithURL: url];
data = [m dataUsingEncoding: NSUTF8StringEncoding];
[request setHTTPBody: data];
[request setHTTPMethod: @"POST"];
// sending the request
[NSURLConnection sendSynchronousRequest: request
returningResponse: &response
error: &error];
// sending the request
PASS(response != nil && [response statusCode] == 204,
"NSURLConnection synchronous load returns a response");
data = [NSData dataWithContentsOfFile: @"Capture.dat"];
str = [[NSString alloc] initWithData: data
encoding: NSUTF8StringEncoding];
r = [str rangeOfString: m];
PASS(r.location != NSNotFound,
"NSURLConnection capture test OK");
// Wait for server termination
[t terminate];
[t waitUntilExit];
DESTROY(str);
}
[arp release]; arp = nil;
#endif
return 0;
}
#import <Foundation/Foundation.h>
#import "Testing.h"
#import "ObjectTesting.h"
int main()
{
#if GNUSTEP
NSAutoreleasePool *arp = [NSAutoreleasePool new];
unsigned i;
NSURL *url;
NSMutableString *body;
NSData *data;
NSString *str;
NSTask *t;
NSString *helpers;
NSString *capture;
NSMutableURLRequest *request;
NSHTTPURLResponse *response = nil;
NSError *error = nil;
NSFileManager *fm;
NSRange r;
NSString *file = @"Capture.dat";
fm = [NSFileManager defaultManager];
helpers = [fm currentDirectoryPath];
helpers = [helpers stringByAppendingPathComponent: @"Helpers"];
helpers = [helpers stringByAppendingPathComponent: @"obj"];
capture = [helpers stringByAppendingPathComponent: @"capture"];
body = [NSMutableString stringWithCapacity: 2048];
for (i = 0; i < 128; i++)
{
[body appendFormat: @"Hello %d\r\n", i];
}
t = [NSTask launchedTaskWithLaunchPath: capture
arguments: [NSArray arrayWithObjects:
nil]];
if (t != nil)
{
// Pause to allow server subtask to set up.
[NSThread sleepUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.5]];
// remove the captured data from a possible previous run
[fm removeItemAtPath: file error: NULL];
// making a POST request
url = [NSURL URLWithString: @"http://localhost:54321/"];
request = [NSMutableURLRequest requestWithURL: url];
data = [body dataUsingEncoding: NSUTF8StringEncoding];
[request setHTTPBody: data];
[request setHTTPMethod: @"POST"];
// sending the request
[NSURLConnection sendSynchronousRequest: request
returningResponse: &response
error: &error];
// analyzing the response
PASS(response != nil && [response statusCode] == 204,
"NSURLConnection synchronous load returns a response");
data = [NSData dataWithContentsOfFile: @"Capture.dat"];
str = [[NSString alloc] initWithData: data
encoding: NSUTF8StringEncoding];
r = [str rangeOfString: body];
PASS(r.location != NSNotFound,
"NSURLConnection capture test OK");
// Wait for server termination
[t terminate];
[t waitUntilExit];
DESTROY(str);
response = nil;
error = nil;
}
[arp release]; arp = nil;
#endif
return 0;
}

View file

@ -0,0 +1,46 @@
-----BEGIN CERTIFICATE-----
MIID6TCCAtGgAwIBAgIJAP02s2/x3i8ZMA0GCSqGSIb3DQEBCwUAMIGKMQswCQYD
VQQGEwJYWDEOMAwGA1UECAwFV29ybGQxEDAOBgNVBAcMB0dOVXN0ZXAxITAfBgNV
BAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0
MSIwIAYJKoZIhvcNAQkBFhNnbnVzdGVwLWRldkBnbnUub3JnMB4XDTE0MDgwNzEx
MTIxN1oXDTI0MDgwNDExMTIxN1owgYoxCzAJBgNVBAYTAlhYMQ4wDAYDVQQIDAVX
b3JsZDEQMA4GA1UEBwwHR05Vc3RlcDEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
cyBQdHkgTHRkMRIwEAYDVQQDDAlsb2NhbGhvc3QxIjAgBgkqhkiG9w0BCQEWE2du
dXN0ZXAtZGV2QGdudS5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
AQDxFVEHh137hyl0juYvbXuAOUIXRSVwk92mAGJIIn0g0Dm6KIAJW1EGR5LeHY3L
vrkxEAvGxb7Ypqtg1F4OcwoZE/1Y0xKjHBnqtMJcw7DgN9F1dIkQ9HxHkYPiHzTF
d7floomsjyt0BcqAqE1Qf0ahsnveq9E6KTIYRTZ91RGHQrAW4KBxFM30ieHYQYdn
2vDgH/8wyLjwQ+89P5SrBJdt+eKHPjHvjs1WZe0i660hvLa19l/DQ5ZxlV5LJ6tZ
yjvr+OjT9tE86r9n34vk+ZZBCQwvzZ+dHwLpufz3VgPap54bCCLY21BZj15nqITN
k+8XJz+aavFe3zdMvT9cGiZNAgMBAAGjUDBOMB0GA1UdDgQWBBRB6fstnCsIvg3r
9f3EmiZhLS867TAfBgNVHSMEGDAWgBRB6fstnCsIvg3r9f3EmiZhLS867TAMBgNV
HRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQC0fy/lWgBZRTXfAUOfZp5sWJcQ
mQKQiPfXYxiUALq6iI1SyQq90kJ1DyLEGJJ9HEaP3s3jyFQgLXoi1J/8qyOESUDy
ogC8nIod6vfA9g8eWcFeOEd6YnNWykPjGCqA/mzrzN3abFkERap8ivx4RWVYX9bP
ZNhJVxcoqVCjtFmIh6ATNG/0+xfxax4U4GitcHNYD0Ij+qdXqNJHym67uVponLYE
SnxxF3lZ6FzB51SyKtJF5j4/fEeyDXR8Uy3zJdrb6mhSUJNX5RYcrdiR9RflrfGy
qYOXFtWmFv/+w/OaqugXQfx9By8uI49U9BG8Q7xSsZSXmoEuMGYt+UwE7UP+
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIID6TCCAtGgAwIBAgIJAP02s2/x3i8ZMA0GCSqGSIb3DQEBCwUAMIGKMQswCQYD
VQQGEwJYWDEOMAwGA1UECAwFV29ybGQxEDAOBgNVBAcMB0dOVXN0ZXAxITAfBgNV
BAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0
MSIwIAYJKoZIhvcNAQkBFhNnbnVzdGVwLWRldkBnbnUub3JnMB4XDTE0MDgwNzEx
MTIxN1oXDTI0MDgwNDExMTIxN1owgYoxCzAJBgNVBAYTAlhYMQ4wDAYDVQQIDAVX
b3JsZDEQMA4GA1UEBwwHR05Vc3RlcDEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
cyBQdHkgTHRkMRIwEAYDVQQDDAlsb2NhbGhvc3QxIjAgBgkqhkiG9w0BCQEWE2du
dXN0ZXAtZGV2QGdudS5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
AQDxFVEHh137hyl0juYvbXuAOUIXRSVwk92mAGJIIn0g0Dm6KIAJW1EGR5LeHY3L
vrkxEAvGxb7Ypqtg1F4OcwoZE/1Y0xKjHBnqtMJcw7DgN9F1dIkQ9HxHkYPiHzTF
d7floomsjyt0BcqAqE1Qf0ahsnveq9E6KTIYRTZ91RGHQrAW4KBxFM30ieHYQYdn
2vDgH/8wyLjwQ+89P5SrBJdt+eKHPjHvjs1WZe0i660hvLa19l/DQ5ZxlV5LJ6tZ
yjvr+OjT9tE86r9n34vk+ZZBCQwvzZ+dHwLpufz3VgPap54bCCLY21BZj15nqITN
k+8XJz+aavFe3zdMvT9cGiZNAgMBAAGjUDBOMB0GA1UdDgQWBBRB6fstnCsIvg3r
9f3EmiZhLS867TAfBgNVHSMEGDAWgBRB6fstnCsIvg3r9f3EmiZhLS867TAMBgNV
HRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQC0fy/lWgBZRTXfAUOfZp5sWJcQ
mQKQiPfXYxiUALq6iI1SyQq90kJ1DyLEGJJ9HEaP3s3jyFQgLXoi1J/8qyOESUDy
ogC8nIod6vfA9g8eWcFeOEd6YnNWykPjGCqA/mzrzN3abFkERap8ivx4RWVYX9bP
ZNhJVxcoqVCjtFmIh6ATNG/0+xfxax4U4GitcHNYD0Ij+qdXqNJHym67uVponLYE
SnxxF3lZ6FzB51SyKtJF5j4/fEeyDXR8Uy3zJdrb6mhSUJNX5RYcrdiR9RflrfGy
qYOXFtWmFv/+w/OaqugXQfx9By8uI49U9BG8Q7xSsZSXmoEuMGYt+UwE7UP+
-----END CERTIFICATE-----

View file

@ -0,0 +1,54 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA8RVRB4dd+4cpdI7mL217gDlCF0UlcJPdpgBiSCJ9INA5uiiA
CVtRBkeS3h2Ny765MRALxsW+2KarYNReDnMKGRP9WNMSoxwZ6rTCXMOw4DfRdXSJ
EPR8R5GD4h80xXe35aKJrI8rdAXKgKhNUH9GobJ73qvROikyGEU2fdURh0KwFuCg
cRTN9Inh2EGHZ9rw4B//MMi48EPvPT+UqwSXbfnihz4x747NVmXtIuutIby2tfZf
w0OWcZVeSyerWco76/jo0/bRPOq/Z9+L5PmWQQkML82fnR8C6bn891YD2qeeGwgi
2NtQWY9eZ6iEzZPvFyc/mmrxXt83TL0/XBomTQIDAQABAoIBAAZmlnwor+oZsJQT
pzDjK0BAROzxPQk8I8pggDuCDuhsHtw+bwfQkNol1FRpXHZoXepbjrR8U5DU+//a
I5UmoMIBsdxF3lzORjHhErf7yhpp4PnJWkpE83fC+Ulroq8LeqpyIk2ej3zJGpNH
5KWae3mXj4pd7XQp29ahH80/dvOsWGZyYOXqh7jO6UdXfhPHKIhLN6wd9nzmRy44
PLFcQYo/nTM19ackz9joovyGPy87BGsj3+rALRXd+GMPVtRFFPMtm6PnSTsMsoDg
HEsm76CygnmL7/ywIXSVY/rfomm73SV9FgFalkUSpNWdLqiXiLICVTKC2P1ZUCdg
eL/A59ECgYEA/nfw/9Vh86LVQ6Vqfp9W/pAut9ik8SecqodOptBSVV2YBYcfBejL
dqksW3LaARabGMJh1z5tHfIw+buF7tl8OIy+TNYRNdrUlsNpI7lWVCVp5F4Znr3c
a7GsGE82/XA3eiEZhUfaQeCctOMXoPnZO32mdDal3rYYQ7yKMvAO8gsCgYEA8ojA
pK80FURe+kM0Zd/ftTAzMkGJNWnKkSSqd+sI7iCwzd9KAX3d+EM4/vqguMXzw+MP
FMkXOv4PUP0iV1XyBt1hn+OSdfax4mD9ALOyJoCHvLPsRjlIv7iEsD2oi84mJ07v
3fFuku+xDrPwMBJgeIWjC2ml9J3YYrB5ppPRmAcCgYEA+64/Y5l1ttW/XpeVm8UW
8tJCEr2obYfDMPqAtQZn2FyohhcdfOfBjQxHfe87ZUYpgjSHNq9clvi6rdVl41Wh
wgCaGz7CaOSVzMNbEuU1WCZk9GSJrHKWNsHUt3pppgK+LAHezu7BFNUFyPauoR1c
WLWu01RVe8/Yce5hNX4vGf8CgYEA5w7pmPthfzFX2szTyopyMcftvl85PK3A0m5A
CWbdZx+10Sx88Nbc9Xv1fNWA8QeFqIVVBNRfUVBhfyLp6JJ0tZ2LOCwyiDeyWJ1V
66lGe+/PYTN4UZ6ZdC1yHAVh4W9QYfqOAr/UPCAman96wBGB3tBR+Ll55YXLdJn0
C4KgF1kCgYAHjE0j82DF+6sKw+ggUcMgLM0z6HcYE975T/c4KNqb11Mu2+38CQvY
pvlzOgQxzbjq59reek3gRtj3u0UCaXzTnIvgLl+sC0/l4f4qEVwcEcnMiJyUAIsR
VR7Mw874clbF8TK8tLlcgCipmDmWxFlrgxOPs1ikcObvTOR/oxacUQ==
-----END RSA PRIVATE KEY-----
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA8RVRB4dd+4cpdI7mL217gDlCF0UlcJPdpgBiSCJ9INA5uiiA
CVtRBkeS3h2Ny765MRALxsW+2KarYNReDnMKGRP9WNMSoxwZ6rTCXMOw4DfRdXSJ
EPR8R5GD4h80xXe35aKJrI8rdAXKgKhNUH9GobJ73qvROikyGEU2fdURh0KwFuCg
cRTN9Inh2EGHZ9rw4B//MMi48EPvPT+UqwSXbfnihz4x747NVmXtIuutIby2tfZf
w0OWcZVeSyerWco76/jo0/bRPOq/Z9+L5PmWQQkML82fnR8C6bn891YD2qeeGwgi
2NtQWY9eZ6iEzZPvFyc/mmrxXt83TL0/XBomTQIDAQABAoIBAAZmlnwor+oZsJQT
pzDjK0BAROzxPQk8I8pggDuCDuhsHtw+bwfQkNol1FRpXHZoXepbjrR8U5DU+//a
I5UmoMIBsdxF3lzORjHhErf7yhpp4PnJWkpE83fC+Ulroq8LeqpyIk2ej3zJGpNH
5KWae3mXj4pd7XQp29ahH80/dvOsWGZyYOXqh7jO6UdXfhPHKIhLN6wd9nzmRy44
PLFcQYo/nTM19ackz9joovyGPy87BGsj3+rALRXd+GMPVtRFFPMtm6PnSTsMsoDg
HEsm76CygnmL7/ywIXSVY/rfomm73SV9FgFalkUSpNWdLqiXiLICVTKC2P1ZUCdg
eL/A59ECgYEA/nfw/9Vh86LVQ6Vqfp9W/pAut9ik8SecqodOptBSVV2YBYcfBejL
dqksW3LaARabGMJh1z5tHfIw+buF7tl8OIy+TNYRNdrUlsNpI7lWVCVp5F4Znr3c
a7GsGE82/XA3eiEZhUfaQeCctOMXoPnZO32mdDal3rYYQ7yKMvAO8gsCgYEA8ojA
pK80FURe+kM0Zd/ftTAzMkGJNWnKkSSqd+sI7iCwzd9KAX3d+EM4/vqguMXzw+MP
FMkXOv4PUP0iV1XyBt1hn+OSdfax4mD9ALOyJoCHvLPsRjlIv7iEsD2oi84mJ07v
3fFuku+xDrPwMBJgeIWjC2ml9J3YYrB5ppPRmAcCgYEA+64/Y5l1ttW/XpeVm8UW
8tJCEr2obYfDMPqAtQZn2FyohhcdfOfBjQxHfe87ZUYpgjSHNq9clvi6rdVl41Wh
wgCaGz7CaOSVzMNbEuU1WCZk9GSJrHKWNsHUt3pppgK+LAHezu7BFNUFyPauoR1c
WLWu01RVe8/Yce5hNX4vGf8CgYEA5w7pmPthfzFX2szTyopyMcftvl85PK3A0m5A
CWbdZx+10Sx88Nbc9Xv1fNWA8QeFqIVVBNRfUVBhfyLp6JJ0tZ2LOCwyiDeyWJ1V
66lGe+/PYTN4UZ6ZdC1yHAVh4W9QYfqOAr/UPCAman96wBGB3tBR+Ll55YXLdJn0
C4KgF1kCgYAHjE0j82DF+6sKw+ggUcMgLM0z6HcYE975T/c4KNqb11Mu2+38CQvY
pvlzOgQxzbjq59reek3gRtj3u0UCaXzTnIvgLl+sC0/l4f4qEVwcEcnMiJyUAIsR
VR7Mw874clbF8TK8tLlcgCipmDmWxFlrgxOPs1ikcObvTOR/oxacUQ==
-----END RSA PRIVATE KEY-----