mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
bug 1178988 - convert test_ocsp_url to generate certificates at build time r=Cykesiopka
Also enable loading of certificates and private keys into GenerateOCSPResponse
This commit is contained in:
parent
893b8954ec
commit
359bd103d4
@ -12,6 +12,7 @@ TEST_DIRS += [
|
||||
'test_cert_version',
|
||||
'test_intermediate_basic_usage_constraints',
|
||||
'test_pinning_dynamic',
|
||||
'test_ocsp_url',
|
||||
]
|
||||
|
||||
if not CONFIG['MOZ_NO_SMART_CARDS']:
|
||||
|
@ -26,6 +26,7 @@ extKeyUsage:[serverAuth,clientAuth,codeSigning,emailProtection
|
||||
nsSGC, # Netscape Server Gated Crypto
|
||||
OCSPSigning,timeStamping]
|
||||
subjectAlternativeName:[<dNSName>,...]
|
||||
authorityInformationAccess:<OCSP URI>
|
||||
|
||||
Where:
|
||||
[] indicates an optional field or component of a field
|
||||
@ -120,6 +121,18 @@ def getASN1Tag(asn1Type):
|
||||
type from the pyasn1 package"""
|
||||
return asn1Type.baseTagSet.getBaseTag().asTuple()[2]
|
||||
|
||||
def stringToAccessDescription(string):
|
||||
"""Helper function that takes a string representing a URI
|
||||
presumably identifying an OCSP authority information access
|
||||
location. Returns an AccessDescription usable by pyasn1."""
|
||||
accessMethod = rfc2459.id_ad_ocsp
|
||||
accessLocation = rfc2459.GeneralName()
|
||||
accessLocation.setComponentByName('uniformResourceIdentifier', string)
|
||||
sequence = univ.Sequence()
|
||||
sequence.setComponentByPosition(0, accessMethod)
|
||||
sequence.setComponentByPosition(1, accessLocation)
|
||||
return sequence
|
||||
|
||||
def stringToAlgorithmIdentifier(string):
|
||||
"""Helper function that converts a description of an algorithm
|
||||
to a representation usable by the pyasn1 package"""
|
||||
@ -247,6 +260,8 @@ class Certificate:
|
||||
self.addExtKeyUsage(value)
|
||||
elif extensionType == 'subjectAlternativeName':
|
||||
self.addSubjectAlternativeName(value)
|
||||
elif extensionType == 'authorityInformationAccess':
|
||||
self.addAuthorityInformationAccess(value)
|
||||
else:
|
||||
raise UnknownExtensionTypeError(extensionType)
|
||||
|
||||
@ -321,6 +336,12 @@ class Certificate:
|
||||
count += 1
|
||||
self.addExtension(rfc2459.id_ce_subjectAltName, subjectAlternativeName)
|
||||
|
||||
def addAuthorityInformationAccess(self, ocspURI):
|
||||
sequence = univ.Sequence()
|
||||
accessDescription = stringToAccessDescription(ocspURI)
|
||||
sequence.setComponentByPosition(0, accessDescription)
|
||||
self.addExtension(rfc2459.id_pe_authorityInfoAccess, sequence)
|
||||
|
||||
def getVersion(self):
|
||||
return rfc2459.Version(self.versionValue).subtype(
|
||||
explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))
|
||||
|
@ -5,8 +5,10 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
"""
|
||||
Provides methods for signing data and representing a key as a
|
||||
subject public key info for use with pyasn1.
|
||||
Reads a key specification from stdin or a file and outputs a
|
||||
PKCS #8 file representing the (private) key. Also provides
|
||||
methods for signing data and representing the key as a subject
|
||||
public key info for use with pyasn1.
|
||||
|
||||
The key specification format is currently very simple. If it is
|
||||
empty, one RSA key is used. If it consists of the string
|
||||
@ -18,8 +20,10 @@ strength, signature algorithm, etc.).
|
||||
from pyasn1.codec.der import encoder
|
||||
from pyasn1.type import univ, namedtype
|
||||
from pyasn1_modules import rfc2459
|
||||
import base64
|
||||
import binascii
|
||||
import rsa
|
||||
import sys
|
||||
|
||||
def byteStringToHexifiedBitString(string):
|
||||
"""Takes a string of bytes and returns a hex string representing
|
||||
@ -53,6 +57,30 @@ class RSAPublicKey(univ.Sequence):
|
||||
namedtype.NamedType('E', univ.Integer()))
|
||||
|
||||
|
||||
class RSAPrivateKey(univ.Sequence):
|
||||
"""Helper type for encoding an RSA private key"""
|
||||
componentType = namedtype.NamedTypes(
|
||||
namedtype.NamedType('version', univ.Integer()),
|
||||
namedtype.NamedType('modulus', univ.Integer()),
|
||||
namedtype.NamedType('publicExponent', univ.Integer()),
|
||||
namedtype.NamedType('privateExponent', univ.Integer()),
|
||||
namedtype.NamedType('prime1', univ.Integer()),
|
||||
namedtype.NamedType('prime2', univ.Integer()),
|
||||
namedtype.NamedType('exponent1', univ.Integer()),
|
||||
namedtype.NamedType('exponent2', univ.Integer()),
|
||||
namedtype.NamedType('coefficient', univ.Integer()),
|
||||
)
|
||||
|
||||
|
||||
class PrivateKeyInfo(univ.Sequence):
|
||||
"""Helper type for encoding a PKCS #8 private key info"""
|
||||
componentType = namedtype.NamedTypes(
|
||||
namedtype.NamedType('version', univ.Integer()),
|
||||
namedtype.NamedType('privateKeyAlgorithm', rfc2459.AlgorithmIdentifier()),
|
||||
namedtype.NamedType('privateKey', univ.OctetString())
|
||||
)
|
||||
|
||||
|
||||
class RSAKey:
|
||||
# For reference, when encoded as a subject public key info, the
|
||||
# base64-encoded sha-256 hash of this key is
|
||||
@ -186,6 +214,37 @@ class RSAKey:
|
||||
else:
|
||||
raise UnknownKeySpecificationError(specification)
|
||||
|
||||
def toDER(self):
|
||||
privateKeyInfo = PrivateKeyInfo()
|
||||
privateKeyInfo.setComponentByName('version', 0)
|
||||
algorithmIdentifier = rfc2459.AlgorithmIdentifier()
|
||||
algorithmIdentifier.setComponentByName('algorithm', rfc2459.rsaEncryption)
|
||||
algorithmIdentifier.setComponentByName('parameters', univ.Null())
|
||||
privateKeyInfo.setComponentByName('privateKeyAlgorithm', algorithmIdentifier)
|
||||
rsaPrivateKey = RSAPrivateKey()
|
||||
rsaPrivateKey.setComponentByName('version', 0)
|
||||
rsaPrivateKey.setComponentByName('modulus', self.RSA_N)
|
||||
rsaPrivateKey.setComponentByName('publicExponent', self.RSA_E)
|
||||
rsaPrivateKey.setComponentByName('privateExponent', self.RSA_D)
|
||||
rsaPrivateKey.setComponentByName('prime1', self.RSA_P)
|
||||
rsaPrivateKey.setComponentByName('prime2', self.RSA_Q)
|
||||
rsaPrivateKey.setComponentByName('exponent1', self.RSA_exp1)
|
||||
rsaPrivateKey.setComponentByName('exponent2', self.RSA_exp2)
|
||||
rsaPrivateKey.setComponentByName('coefficient', self.RSA_coef)
|
||||
rsaPrivateKeyEncoded = encoder.encode(rsaPrivateKey)
|
||||
privateKeyInfo.setComponentByName('privateKey', univ.OctetString(rsaPrivateKeyEncoded))
|
||||
return encoder.encode(privateKeyInfo)
|
||||
|
||||
def toPEM(self):
|
||||
output = '-----BEGIN PRIVATE KEY-----'
|
||||
der = self.toDER()
|
||||
b64 = base64.b64encode(der)
|
||||
while b64:
|
||||
output += '\n' + b64[:64]
|
||||
b64 = b64[64:]
|
||||
output += '\n-----END PRIVATE KEY-----'
|
||||
return output
|
||||
|
||||
def asSubjectPublicKeyInfo(self):
|
||||
"""Returns a subject public key info representing
|
||||
this key for use by pyasn1."""
|
||||
@ -208,3 +267,16 @@ class RSAKey:
|
||||
rsaPrivateKey = rsa.PrivateKey(self.RSA_N, self.RSA_E, self.RSA_D, self.RSA_P, self.RSA_Q)
|
||||
signature = rsa.sign(data, rsaPrivateKey, 'SHA-256')
|
||||
return byteStringToHexifiedBitString(signature)
|
||||
|
||||
|
||||
# The build harness will call this function with an output file-like
|
||||
# object and a path to a file containing a specification. This will
|
||||
# read the specification and output the key as ASCII-encoded PKCS #8.
|
||||
def main(output, inputPath):
|
||||
with open(inputPath) as configStream:
|
||||
output.write(RSAKey(configStream.read()).toPEM())
|
||||
|
||||
# When run as a standalone program, this will read a specification from
|
||||
# stdin and output the certificate as PEM to stdout.
|
||||
if __name__ == '__main__':
|
||||
print RSAKey(sys.stdin.read()).toPEM()
|
||||
|
@ -25,14 +25,14 @@ function start_ocsp_responder(expectedCertNames, expectedPaths) {
|
||||
}
|
||||
|
||||
function check_cert_err(cert_name, expected_error) {
|
||||
let cert = constructCertFromFile("test_ocsp_url/" + cert_name + ".der");
|
||||
let cert = constructCertFromFile("test_ocsp_url/" + cert_name + ".pem");
|
||||
return checkCertErrorGeneric(certdb, cert, expected_error,
|
||||
certificateUsageSSLServer);
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
addCertFromFile(certdb, "test_ocsp_url/ca.der", 'CTu,CTu,CTu');
|
||||
addCertFromFile(certdb, "test_ocsp_url/int.der", ',,');
|
||||
addCertFromFile(certdb, "test_ocsp_url/ca.pem", 'CTu,CTu,CTu');
|
||||
addCertFromFile(certdb, "test_ocsp_url/int.pem", ',,');
|
||||
|
||||
// Enabled so that we can force ocsp failure responses.
|
||||
Services.prefs.setBoolPref("security.OCSP.require", true);
|
||||
@ -44,7 +44,7 @@ function run_test() {
|
||||
add_test(function() {
|
||||
clearOCSPCache();
|
||||
let ocspResponder = failingOCSPResponder();
|
||||
check_cert_err("bad-scheme",SEC_ERROR_CERT_BAD_ACCESS_LOCATION);
|
||||
check_cert_err("bad-scheme", SEC_ERROR_CERT_BAD_ACCESS_LOCATION);
|
||||
ocspResponder.stop(run_next_test);
|
||||
});
|
||||
|
||||
|
Binary file not shown.
@ -0,0 +1,3 @@
|
||||
issuer:int
|
||||
subject:bad-scheme
|
||||
extension:authorityInformationAccess:/www.example.com
|
Binary file not shown.
@ -0,0 +1,4 @@
|
||||
issuer:ca
|
||||
subject:ca
|
||||
extension:basicConstraints:cA,
|
||||
extension:keyUsage:keyCertSign,cRLSign
|
Binary file not shown.
Binary file not shown.
@ -0,0 +1,3 @@
|
||||
issuer:int
|
||||
subject:empty-port
|
||||
extension:authorityInformationAccess:http://www.example.com:/
|
Binary file not shown.
@ -0,0 +1,3 @@
|
||||
issuer:int
|
||||
subject:empty-scheme-url
|
||||
extension:authorityInformationAccess:://www.example.com:8888/
|
Binary file not shown.
@ -0,0 +1,3 @@
|
||||
issuer:int
|
||||
subject:ftp-url
|
||||
extension:authorityInformationAccess:ftp://www.example.com:8888/
|
@ -1,40 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import tempfile, os, sys
|
||||
|
||||
libpath = os.path.abspath('../psm_common_py')
|
||||
sys.path.append(libpath)
|
||||
import CertUtils
|
||||
|
||||
srcdir = os.getcwd()
|
||||
db = tempfile.mkdtemp()
|
||||
|
||||
def generate_ca_cert(db_dir, dest_dir, noise_file, name):
|
||||
return CertUtils.generate_ca_cert(db_dir, dest_dir, noise_file, name,
|
||||
3, True)
|
||||
|
||||
def generate_child_cert(db_dir, dest_dir, noise_file, name, ca_nick, is_ee,
|
||||
ocsp_url):
|
||||
return CertUtils.generate_child_cert(db_dir, dest_dir, noise_file, name,
|
||||
ca_nick, 3, True, is_ee, ocsp_url)
|
||||
|
||||
def generate_certs():
|
||||
[noise_file, pwd_file] = CertUtils.init_nss_db(srcdir)
|
||||
generate_ca_cert(srcdir, srcdir, noise_file, 'ca')
|
||||
generate_child_cert(srcdir, srcdir, noise_file, 'int', 'ca', False, '')
|
||||
nick_baseurl = { 'no-path-url': "http://www.example.com:8888",
|
||||
'ftp-url': "ftp://www.example.com:8888/",
|
||||
'no-scheme-url': "www.example.com:8888/",
|
||||
'empty-scheme-url': "://www.example.com:8888/",
|
||||
'no-host-url': "http://:8888/",
|
||||
'hTTp-url': "hTTp://www.example.com:8888/hTTp-url",
|
||||
'https-url': "https://www.example.com:8888/https-url",
|
||||
'bad-scheme': "/www.example.com",
|
||||
'empty-port': "http://www.example.com:/",
|
||||
'unknown-scheme': "ttp://www.example.com",
|
||||
'negative-port': "http://www.example.com:-1",
|
||||
'no-scheme-host-port': "/" }
|
||||
for nick, url in nick_baseurl.iteritems():
|
||||
generate_child_cert(srcdir, srcdir, noise_file, nick, 'int', True, url)
|
||||
|
||||
generate_certs()
|
Binary file not shown.
@ -0,0 +1,3 @@
|
||||
issuer:int
|
||||
subject:hTTp-url
|
||||
extension:authorityInformationAccess:hTTp://www.example.com:8888/hTTp-url
|
Binary file not shown.
@ -0,0 +1,3 @@
|
||||
issuer:int
|
||||
subject:https-url
|
||||
extension:authorityInformationAccess:https://www.example.com:8888/https-url
|
Binary file not shown.
@ -0,0 +1,4 @@
|
||||
issuer:ca
|
||||
subject:int
|
||||
extension:basicConstraints:cA,
|
||||
extension:keyUsage:keyCertSign,cRLSign
|
Binary file not shown.
42
security/manager/ssl/tests/unit/test_ocsp_url/moz.build
Normal file
42
security/manager/ssl/tests/unit/test_ocsp_url/moz.build
Normal file
@ -0,0 +1,42 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
test_certificates = (
|
||||
'bad-scheme.pem',
|
||||
'ca.pem',
|
||||
'empty-port.pem',
|
||||
'empty-scheme-url.pem',
|
||||
'ftp-url.pem',
|
||||
'hTTp-url.pem',
|
||||
'https-url.pem',
|
||||
'int.pem',
|
||||
'negative-port.pem',
|
||||
'no-host-url.pem',
|
||||
'no-path-url.pem',
|
||||
'no-scheme-host-port.pem',
|
||||
'no-scheme-url.pem',
|
||||
'unknown-scheme.pem',
|
||||
)
|
||||
|
||||
for test_certificate in test_certificates:
|
||||
input_file = test_certificate + '.certspec'
|
||||
GENERATED_FILES += [test_certificate]
|
||||
props = GENERATED_FILES[test_certificate]
|
||||
props.script = '../pycert.py'
|
||||
props.inputs = [input_file, '!/config/buildid']
|
||||
TEST_HARNESS_FILES.xpcshell.security.manager.ssl.tests.unit.test_ocsp_url += ['!%s' % test_certificate]
|
||||
|
||||
test_keys = (
|
||||
'int.key',
|
||||
)
|
||||
|
||||
for test_key in test_keys:
|
||||
input_file = test_key + '.keyspec'
|
||||
GENERATED_FILES += [test_key]
|
||||
props = GENERATED_FILES[test_key]
|
||||
props.script = '../pykey.py'
|
||||
props.inputs = [input_file]
|
||||
TEST_HARNESS_FILES.xpcshell.security.manager.ssl.tests.unit.test_ocsp_url += ['!%s' % test_key]
|
Binary file not shown.
@ -0,0 +1,3 @@
|
||||
issuer:int
|
||||
subject:negative-port
|
||||
extension:authorityInformationAccess:http://www.example.com:-1
|
Binary file not shown.
@ -0,0 +1,3 @@
|
||||
issuer:int
|
||||
subject:no-host-url
|
||||
extension:authorityInformationAccess:http://:8888/
|
Binary file not shown.
@ -0,0 +1,3 @@
|
||||
issuer:int
|
||||
subject:no-path-url
|
||||
extension:authorityInformationAccess:http://www.example.com:8888
|
Binary file not shown.
@ -0,0 +1,3 @@
|
||||
issuer:int
|
||||
subject:no-scheme-host-port
|
||||
extension:authorityInformationAccess:/
|
Binary file not shown.
@ -0,0 +1,3 @@
|
||||
issuer:int
|
||||
subject:no-scheme-url
|
||||
extension:authorityInformationAccess:www.example.com:8888/
|
@ -1,5 +0,0 @@
|
||||
library=
|
||||
name=NSS Internal PKCS #11 Module
|
||||
parameters=configdir='sql:./security/manager/ssl/tests/unit/test_ocsp_url' certPrefix='' keyPrefix='' secmod='secmod.db' flags= updatedir='' updateCertPrefix='' updateKeyPrefix='' updateid='' updateTokenDescription=''
|
||||
NSS=Flags=internal,critical trustOrder=75 cipherOrder=100 slotParams=(1={slotFlags=[RSA,DSA,DH,RC2,RC4,DES,RANDOM,SHA1,MD5,MD2,SSL,TLS,AES,Camellia,SEED,SHA256,SHA512] askpw=any timeout=30})
|
||||
|
Binary file not shown.
@ -0,0 +1,3 @@
|
||||
issuer:int
|
||||
subject:unknown-scheme-url
|
||||
extension:authorityInformationAccess:ttp://www.example.com
|
@ -13,6 +13,8 @@
|
||||
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
|
||||
#include "base64.h"
|
||||
#include "cert.h"
|
||||
#include "nspr.h"
|
||||
#include "nss.h"
|
||||
#include "plarenas.h"
|
||||
@ -66,9 +68,8 @@ const static OCSPResponseName kOCSPResponseNameList[] = {
|
||||
// two years old
|
||||
};
|
||||
|
||||
|
||||
bool
|
||||
stringToOCSPResponseType(const char* respText,
|
||||
StringToOCSPResponseType(const char* respText,
|
||||
/*out*/ OCSPResponseType* OCSPType)
|
||||
{
|
||||
if (!OCSPType) {
|
||||
@ -107,7 +108,201 @@ WriteResponse(const char* filename, const SECItem* item)
|
||||
return true;
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
SECStatus
|
||||
ReadFileToBuffer(const char* basePath, const char* filename, char (&buf)[N])
|
||||
{
|
||||
static_assert(N > 0, "input buffer too small for ReadFileToBuffer");
|
||||
if (PR_snprintf(buf, N - 1, "%s/%s", basePath, filename) == 0) {
|
||||
PrintPRError("PR_snprintf failed");
|
||||
return SECFailure;
|
||||
}
|
||||
ScopedPRFileDesc fd(PR_OpenFile(buf, PR_RDONLY, 0));
|
||||
if (!fd) {
|
||||
PrintPRError("PR_Open failed");
|
||||
return SECFailure;
|
||||
}
|
||||
int32_t fileSize = PR_Available(fd);
|
||||
if (fileSize < 0) {
|
||||
PrintPRError("PR_Available failed");
|
||||
return SECFailure;
|
||||
}
|
||||
if (static_cast<size_t>(fileSize) > N - 1) {
|
||||
PR_fprintf(PR_STDERR, "file too large - not reading\n");
|
||||
return SECFailure;
|
||||
}
|
||||
int32_t bytesRead = PR_Read(fd, buf, fileSize);
|
||||
if (bytesRead != fileSize) {
|
||||
PrintPRError("PR_Read failed");
|
||||
return SECFailure;
|
||||
}
|
||||
buf[bytesRead] = 0;
|
||||
return SECSuccess;
|
||||
}
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPRDir, PRDir, PR_CloseDir);
|
||||
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPORTString, unsigned char, PORT_Free);
|
||||
|
||||
};
|
||||
|
||||
void
|
||||
AddKeyFromFile(const char* basePath, const char* filename)
|
||||
{
|
||||
const char* PRIVATE_KEY_HEADER = "-----BEGIN PRIVATE KEY-----";
|
||||
const char* PRIVATE_KEY_FOOTER = "-----END PRIVATE KEY-----";
|
||||
|
||||
char buf[16384] = { 0 };
|
||||
SECStatus rv = ReadFileToBuffer(basePath, filename, buf);
|
||||
if (rv != SECSuccess) {
|
||||
return;
|
||||
}
|
||||
if (strncmp(buf, PRIVATE_KEY_HEADER, strlen(PRIVATE_KEY_HEADER)) != 0) {
|
||||
PR_fprintf(PR_STDERR, "invalid key - not importing\n");
|
||||
return;
|
||||
}
|
||||
const char* bufPtr = buf + strlen(PRIVATE_KEY_HEADER);
|
||||
size_t bufLen = strlen(buf);
|
||||
char base64[16384] = { 0 };
|
||||
char* base64Ptr = base64;
|
||||
while (bufPtr < buf + bufLen) {
|
||||
if (strncmp(bufPtr, PRIVATE_KEY_FOOTER, strlen(PRIVATE_KEY_FOOTER)) == 0) {
|
||||
break;
|
||||
}
|
||||
if (*bufPtr != '\r' && *bufPtr != '\n') {
|
||||
*base64Ptr = *bufPtr;
|
||||
base64Ptr++;
|
||||
}
|
||||
bufPtr++;
|
||||
}
|
||||
|
||||
unsigned int binLength;
|
||||
ScopedPORTString bin(ATOB_AsciiToData(base64, &binLength));
|
||||
if (!bin || binLength == 0) {
|
||||
PrintPRError("ATOB_AsciiToData failed");
|
||||
return;
|
||||
}
|
||||
ScopedSECItem secitem(SECITEM_AllocItem(nullptr, nullptr, binLength));
|
||||
if (!secitem) {
|
||||
PrintPRError("SECITEM_AllocItem failed");
|
||||
return;
|
||||
}
|
||||
memcpy(secitem->data, bin, binLength);
|
||||
ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
|
||||
if (!slot) {
|
||||
PrintPRError("PK11_GetInternalKeySlot failed");
|
||||
return;
|
||||
}
|
||||
if (PK11_NeedUserInit(slot)) {
|
||||
if (PK11_InitPin(slot, nullptr, nullptr) != SECSuccess) {
|
||||
PrintPRError("PK11_InitPin failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
SECKEYPrivateKey* privateKey;
|
||||
if (PK11_ImportDERPrivateKeyInfoAndReturnKey(slot, secitem, nullptr, nullptr,
|
||||
true, false, KU_ALL,
|
||||
&privateKey, nullptr)
|
||||
!= SECSuccess) {
|
||||
PrintPRError("PK11_ImportDERPrivateKeyInfoAndReturnKey failed");
|
||||
return;
|
||||
}
|
||||
SECKEY_DestroyPrivateKey(privateKey);
|
||||
}
|
||||
|
||||
SECStatus
|
||||
DecodeCertCallback(void* arg, SECItem** certs, int numcerts)
|
||||
{
|
||||
if (numcerts != 1) {
|
||||
PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0);
|
||||
return SECFailure;
|
||||
}
|
||||
SECItem* certDEROut = static_cast<SECItem*>(arg);
|
||||
return SECITEM_CopyItem(nullptr, certDEROut, *certs);
|
||||
}
|
||||
|
||||
void
|
||||
AddCertificateFromFile(const char* basePath, const char* filename)
|
||||
{
|
||||
char buf[16384] = { 0 };
|
||||
SECStatus rv = ReadFileToBuffer(basePath, filename, buf);
|
||||
if (rv != SECSuccess) {
|
||||
return;
|
||||
}
|
||||
SECItem certDER;
|
||||
rv = CERT_DecodeCertPackage(buf, strlen(buf), DecodeCertCallback, &certDER);
|
||||
if (rv != SECSuccess) {
|
||||
PrintPRError("CERT_DecodeCertPackage failed");
|
||||
return;
|
||||
}
|
||||
ScopedCERTCertificate cert(CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
|
||||
&certDER, nullptr, false,
|
||||
true));
|
||||
PORT_Free(certDER.data);
|
||||
if (!cert) {
|
||||
PrintPRError("CERT_NewTempCertificate failed");
|
||||
return;
|
||||
}
|
||||
const char* extension = strstr(filename, ".pem");
|
||||
if (!extension) {
|
||||
PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
|
||||
return;
|
||||
}
|
||||
size_t nicknameLength = extension - filename;
|
||||
memset(buf, 0, sizeof(buf));
|
||||
memcpy(buf, filename, nicknameLength);
|
||||
buf[nicknameLength] = 0;
|
||||
ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
|
||||
if (!slot) {
|
||||
PrintPRError("PK11_GetInternalKeySlot failed");
|
||||
return;
|
||||
}
|
||||
rv = PK11_ImportCert(slot, cert, CK_INVALID_HANDLE, buf, false);
|
||||
if (rv != SECSuccess) {
|
||||
PrintPRError("PK11_ImportCert failed");
|
||||
}
|
||||
}
|
||||
|
||||
SECStatus
|
||||
InitializeNSS(const char* nssCertDBDir)
|
||||
{
|
||||
// First attempt to initialize NSS in read-only mode, in case the specified
|
||||
// directory contains NSS DBs that are tracked by revision control.
|
||||
// If this succeeds, we're done.
|
||||
if (NSS_Initialize(nssCertDBDir, "", "", SECMOD_DB, NSS_INIT_READONLY)
|
||||
== SECSuccess) {
|
||||
return SECSuccess;
|
||||
}
|
||||
// Otherwise, create a new read-write DB and load all .pem and .key files.
|
||||
if (NSS_Initialize(nssCertDBDir, "", "", SECMOD_DB, 0) != SECSuccess) {
|
||||
PrintPRError("NSS_Initialize failed");
|
||||
return SECFailure;
|
||||
}
|
||||
const char* basePath = nssCertDBDir;
|
||||
// The NSS cert DB path could have been specified as "sql:path". Trim off
|
||||
// the leading "sql:" if so.
|
||||
if (strncmp(basePath, "sql:", 4) == 0) {
|
||||
basePath = basePath + 4;
|
||||
}
|
||||
ScopedPRDir fdDir(PR_OpenDir(basePath));
|
||||
if (!fdDir) {
|
||||
PrintPRError("PR_OpenDir failed");
|
||||
return SECFailure;
|
||||
}
|
||||
for (PRDirEntry* dirEntry = PR_ReadDir(fdDir, PR_SKIP_BOTH); dirEntry;
|
||||
dirEntry = PR_ReadDir(fdDir, PR_SKIP_BOTH)) {
|
||||
size_t nameLength = strlen(dirEntry->name);
|
||||
if (nameLength > 4) {
|
||||
if (strncmp(dirEntry->name + nameLength - 4, ".pem", 4) == 0) {
|
||||
AddCertificateFromFile(basePath, dirEntry->name);
|
||||
} else if (strncmp(dirEntry->name + nameLength - 4, ".key", 4) == 0) {
|
||||
AddKeyFromFile(basePath, dirEntry->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
return SECSuccess;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
@ -120,12 +315,9 @@ main(int argc, char* argv[])
|
||||
argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
const char* dbdir = argv[1];
|
||||
|
||||
SECStatus rv;
|
||||
rv = NSS_Init(dbdir);
|
||||
SECStatus rv = InitializeNSS(argv[1]);
|
||||
if (rv != SECSuccess) {
|
||||
PrintPRError("Failed to initialize NSS");
|
||||
PR_fprintf(PR_STDERR, "Failed to initialize NSS\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
PLArenaPool* arena = PORT_NewArena(256 * argc);
|
||||
@ -141,15 +333,15 @@ main(int argc, char* argv[])
|
||||
const char* filename = argv[i + 3];
|
||||
|
||||
OCSPResponseType ORT;
|
||||
if (!stringToOCSPResponseType(ocspTypeText, &ORT)) {
|
||||
if (!StringToOCSPResponseType(ocspTypeText, &ORT)) {
|
||||
PR_fprintf(PR_STDERR, "Cannot generate OCSP response of type %s\n",
|
||||
ocspTypeText);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
ScopedCERTCertificate cert;
|
||||
cert = PK11_FindCertFromNickname(certNick, nullptr);
|
||||
ScopedCERTCertificate cert(PK11_FindCertFromNickname(certNick, nullptr));
|
||||
if (!cert) {
|
||||
PrintPRError("PK11_FindCertFromNickname failed");
|
||||
PR_fprintf(PR_STDERR, "Failed to find certificate with nick '%s'\n",
|
||||
certNick);
|
||||
exit(EXIT_FAILURE);
|
||||
|
Loading…
Reference in New Issue
Block a user