[openssl-users] openssl-users Digest, Vol 11, Issue 5

David Lobron dlobron at akamai.com
Mon Oct 12 14:34:50 UTC 2015


> Your attached sample certificate and private key (1024 bit RSA) works fine.
> I am reading it with PEM_read_PrivateKey( fp, &key, NULL, NULL), and also
> PEM_read_bio_PrivateKey(pkeybio, NULL, 0, NULL) works.
> 
> If you could post the code or code fragment that creates the problem?
> d2i_RSAPrivateKey() is not reading PEM, just making sure...

Thanks very much, Frank.  My code reads the PEM file, base64-decodes it, and passes the resulting DER value to d2i_RSAPrivateKey.  I verified that I can extract the private key with d2i_PrivateKey from the DER formatted data, and I can call SSL_CTX_use_PrivateKey with it on my SSL context without a problem.  It's only when I call d2i_RSAPrivateKey I encounter a problem.  I have included the code below, with annotations (it's in Objective-C).  

I've got custom classes for X509Certificate and X509PrivateKey.  I use them like this:

X509Certificate *cert = [X509Certificate certificateWithPemEncodedFile:certFile];
X509PrivateKey *privKey = [X509PrivateKey privateKeyWithPemEncodedFile:keyFile];
[cert validateWithPrivateKey:privKey];
[privKey validate];

That last call to [privKey validate] is where things fail currently.  The validateWithPrivateKey method works fine, and it looks like this:

- (void)validateWithPrivateKey:(X509PrivateKey *)key
{
    SSL_CTX *sslContext;
    
    [self validate];
    sslContext = SSL_CTX_new(TLSv1_server_method());
    NS_DURING {
        NSData *d = [key der];
        const unsigned char *p = (const unsigned char *)[d bytes];
        EVP_PKEY *pkey = d2i_PrivateKey(EVP_PKEY_RSA, NULL, &p, [d length]);
        if (!sslContext)
            [NSException raise:X509CertificateExcInternalError format:@"SSL_CTX_new failed: %@", sslErrorString()];
        if (SSL_CTX_use_certificate(sslContext, _x) != 1)
            [NSException raise:X509CertificateExcInvalidCertificate format:@"SSL_CTX_use_certificate failed: %@", sslErrorString()];
        if (SSL_CTX_use_PrivateKey(sslContext, pkey) != 1)
            [NSException raise:X509CertificateExcInvalidPrivateKey format:@"SSL_CTX_use_PrivateKey_ASN1 failed: %@", sslErrorString()];
        SSL_CTX_free(sslContext);
    } NS_HANDLER {
        if (sslContext)
            SSL_CTX_free(sslContext);
        [localException raise];
    } NS_ENDHANDLER
}

(That initial call to "self validate" simply validates the cert object's SSL_CTX). 

I initialize my private key object as follows:

- (id)initWithPemEncodedFile:(NSString *)path
{
    NSData *d = nil;
    NS_DURING {
        NSString *s;
        NSArray *inputLines;
        
        // read the file
        s = [NSString stringWithContentsOfFile:path];
        if (s == nil || [s length] == 0)
            [NSException raise:X509CertificateExcParameterError format:@"File %@ is empty or cannot be read", path];
        inputLines = [s componentsSeparatedByString:@"\n"];
        d = [X509Certificate decodePemFragmentFromLines:inputLines withBoundaryPhrases:[NSArray arrayWithObjects:@"PRIVATE KEY", @"RSA PRIVATE KEY", nil]];
    } NS_HANDLER {
        [self release];
        [localException raise];
    } NS_ENDHANDLER
    return [self initWithDer:d];

}

The decodePemFragmentFromLines method looks like this:

// Extract part of a PEM-encoded message, base64-decode it, and return an NSData object
+ (NSData *)decodePemFragmentFromLines:(NSArray *)inputLines withBoundaryPhrases:(NSArray *)boundaryPhrases
{
    NSEnumerator *e = [inputLines objectEnumerator];
    NSMutableString *b64 = [NSMutableString string];
    NSString *s;
    NSString *boundaryPhrase = nil;
    NSString *startBoundary = nil;
    NSString *endBoundary = nil;
    
    while ((s = [e nextObject]) != nil) {
        NSEnumerator *e = [boundaryPhrases objectEnumerator];
        while ((boundaryPhrase = [e nextObject]) != nil) {
            startBoundary = [NSString stringWithFormat:@"-----BEGIN %@-----", boundaryPhrase];
            if ([s isEqualToString:startBoundary]) {
                endBoundary = [NSString stringWithFormat:@"-----END %@-----", boundaryPhrase];
                break;
            }
        }
        if (endBoundary != nil)
            break;
    }
    if (s == nil)
	[NSException raise:X509CertificateExcParameterError format:@"Start boundary \"%@\" not found", startBoundary];
    while ((s = [e nextObject]) != nil) {
	if ([s isEqualToString:endBoundary])
	    break;
	[b64 appendString:s];
    }
    if (s == nil)
	[NSException raise:X509CertificateExcParameterError format:@"End boundary \"%@\" not found", endBoundary];
    return base64Decode(b64);
}

The initWithDer method is simply:

- (id)initWithDer:(NSData *)der
{
    if ((self = [super init]) != nil) {
        _der = [der copy];
    }
    return self;
}

All of the above works as expected, but when I call d2i_RSAPrivateKey on the _der object, it fails.  Here is the code that throws the exception:

// validate; throws exception if key invalid
- (void)validate
{
    const unsigned char *p = (unsigned char *)[_der bytes];
    RSA *r = d2i_RSAPrivateKey(0, &p, [_der length]);
    int n;
    if (r == 0)
	[NSException raise:X509CertificateExcInvalidPrivateKey format:@"cannot decode RSA private key"];
    NS_DURING {
	switch (n = RSA_check_key(r)) {
	    case 1:		// ok
		break;
	    default:
		[NSException raise:X509CertificateExcInvalidPrivateKey format:@"RSA_check_key() returned %d", n];
	}
    } NS_HANDLER {
	RSA_free(r);
	[localException raise];
    } NS_ENDHANDLER
    RSA_free(r);

}

Thanks for any help you can give here!

--David


-------------- next part --------------
A non-text attachment was scrubbed...
Name: smime.p7s
Type: application/pkcs7-signature
Size: 2863 bytes
Desc: not available
URL: <http://mta.openssl.org/pipermail/openssl-users/attachments/20151012/aa54b63e/attachment.bin>


More information about the openssl-users mailing list