我正在测试 CMTS 设备的证书吊销功能。这需要我设置 OCSP 响应器。由于它仅用于测试,我认为 OpenSSL 提供的最小实现就足够了。
我已经从电缆调制解调器中提取了证书,将其复制到我的电脑并将其转换为 PEM 格式。现在我想在 OpenSSL OCSP 数据库中注册它并启动服务器。
我已经完成了所有这些步骤,但当我发出客户端请求时,我的服务器总是响应“未知”。它似乎完全不知道我的证书的存在。
如果有人愿意看一下我的代码,我将不胜感激。为了方便起见,我创建了一个脚本,其中包含所有使用的命令的顺序列表,从设置 CA 到启动服务器: http://code.google.com/p/stacked-crooked/source/browse/trunk/Misc/OpenSSL/AllCommands.sh
您还可以找到我正在测试的自定义配置文件和证书: http://code.google.com/p/stacked-crooked/source/browse/trunk/Misc/OpenSSL/
任何帮助将不胜感激。
答案1
如果您感兴趣的话:这是我的解决方案。
首先,我放弃了 OpenSSL 的 OCSP 响应器。它不能用于未使用自签名 CA 创建的证书。
然后我使用 OpenCA 库创建了一个命令行实用程序,该实用程序能够根据 OSCP 请求创建 OCSP 响应。它完全基于文件工作:您使用 DER 格式的 OSCP 请求作为输入文件运行它,它会输出 DER 格式的 OCSP 响应文件。
命令行实用程序的第二个参数是撤销状态:良好、已撤销或未知。响应文件中的证书状态将与传递的参数相同。代码基于此样本,向我指出车在这个帖子。
最后一步是创建一个基于命令行实用程序的 HTTP 服务器。我为此使用了 Ruby 的 Merb 框架。一个很棒的功能是可以将撤销状态作为 HTTP url 进行请求,例如:http://127.0.0.1:5000/good。
更新
由于上述链接已损坏,我将在此处发布脚本内容:
AllCommands.sh:
#################
# Create the CA #
#################
# Create folder stucture and default files
mkdir -p CA/private
mkdir -p CA/newcerts
touch CA/index.txt
echo "00" >> CA/serial
# Create the CA
openssl req -new -x509 -keyout ./CA/private/cakey.pem -out ./CA/cacert.pem -days 365 -passin pass:secret1 -passout pass:secret1 -config openssl.cnf
##############################
# Create a test certificate #
##############################
# Create the request
openssl req -batch -config ./openssl.cnf -new -keyout test-certificate.key -out test-certificate.request -days 365 -passin pass:secret1 -passout pass:secret1
# Create the certificate
openssl ca -batch -config ./openssl.cnf -policy policy_anything -passin pass:secret1 -out test-certificate.pem -infiles test-certificate.request
# Cleanup request file
rm test-certificate.request
echo "test-certificate.pem has been generated."
##################################
# Sign a third party certificate #
##################################
# Create a key
openssl genrsa -des3 -passout pass:secret1 -out tmp.key 1024
# Remove the password from the key
cp tmp.key tmp.key.old
openssl rsa -in tmp.key.old -passin pass:secret1 -passout pass:secret1 -out tmp.key
rm tmp.key.old
# Sign the certificate with this key
openssl x509 -days 365 -in thirdparty.pem -signkey tmp.key -passin pass:secret1 -text -out thirdparty-signed.pem
# Remove tempfile
rm tmp.key
########################################
# Add the signed certificate to the CA #
########################################
# Create the certs dir if it doesn't already exist
mkdir -p CA/certs
# Copy our signed third party certificate
cp thirdparty-signed.pem CA/certs
# Create the hash-link
cd CA/certs
ln -s thirdparty-signed.pem `openssl x509 -hash -noout -in thirdparty-signed.pem`.0
cd -
#################################################
# Add the signed certificate to the CA database #
#################################################
openssl ca -config openssl.cnf -policy policy_anything -passin pass:secret1 -ss_cert thirdparty-signed.pem
####################
# Start the server #
####################
# Use test-certificate.pem as response signer.
openssl ocsp -CApath ./CA/certs -index CA/index.txt -port 5000 -rkey test-certificate.key -rsigner test-certificate.pem -CA CA/cacert.pem -text
# The client request:
# $ openssl ocsp -issuer CA/cacert.pem -VAfile test-certificate.pem -cert thirdparty.pem -url http://10.5.5.247:5000
# => Response verify OK
# => thirdparty.pem: unknown
# This Update: Apr 13 13:00:59 2010 GMT
openssl.cnf:
#
# OpenSSL example configuration file.
# This is mostly being used for generation of certificate requests.
#
# This definition stops the following lines choking if HOME isn't
# defined.
HOME = $ENV::HOME
RANDFILE= $ENV::HOME/.rnd
# Extra OBJECT IDENTIFIER info:
#oid_file= $ENV::HOME/.oid
oid_section= new_oids
# To use this configuration file with the "-extfile" option of the
# "openssl x509" utility, name here the section containing the
# X.509v3 extensions to use:
# extensions=
# (Alternatively, use a configuration file that has only
# X.509v3 extensions in its main [= default] section.)
[ new_oids ]
# We can add new OIDs in here for use by 'ca' and 'req'.
# Add a simple OID like this:
# testoid1=1.2.3.4
# Or use config file substitution like this:
# testoid2=${testoid1}.5.6
####################################################################
[ ca ]
default_ca = CA_default # The default ca section
####################################################################
[ CA_default ]
dir = ./CA # Where everything is kept
certs = $dir/certs # Where the issued certs are kept
crl_dir = $dir/crl # Where the issued crl are kept
database = $dir/index.txt # database index file.
new_certs_dir = $dir/newcerts# default place for new certs.
certificate = $dir/cacert.pem # The CA certificate
serial = $dir/serial # The current serial number
crlnumber = $dir/crlnumber # the current crl number
# must be commented out to leave a V1 CRL
crl = $dir/crl.pem# The current CRL
private_key = $dir/private/cakey.pem # The private key
RANDFILE = $dir/private/.rand # private random number file
x509_extensions = usr_cert # The extentions to add to the cert
# Comment out the following two lines for the "traditional"
# (and highly broken) format.
name_opt = ca_default # Subject Name options
cert_opt = ca_default # Certificate field options
# Extension copying option: use with caution.
# copy_extensions = copy
# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs
# so this is commented out by default to leave a V1 CRL.
# crlnumber must also be commented out to leave a V1 CRL.
# crl_extensions = crl_ext
default_days = 365 # how long to certify for
default_crl_days = 30 # how long before next CRL
default_md = sha1 # which md to use.
preserve = no # keep passed DN ordering
# A few difference way of specifying how similar the request should look
# For type CA, the listed attributes must be the same, and the optional
# and supplied fields are just that :-)
policy = policy_match
# For the CA policy
[ policy_match ]
countryName = match
stateOrProvinceName = match
organizationName = match
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
# For the 'anything' policy
# At this point in time, you must list all acceptable 'object'
# types.
[ policy_anything ]
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
####################################################################
[ req ]
default_bits = 1024
default_keyfile = privkey.pem
distinguished_name = req_distinguished_name
prompt = no
attributes = req_attributes
x509_extensions = v3_ca # The extentions to add to the self signed cert
# Passwords for private keys if not present they will be prompted for
# input_password = secret
# output_password = secret
# This sets a mask for permitted string types. There are several options.
# default: PrintableString, T61String, BMPString.
# pkix: PrintableString, BMPString.
# utf8only: only UTF8Strings.
# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings).
# MASK:XXXX a literal mask value.
# WARNING: current versions of Netscape crash on BMPStrings or UTF8Strings
# so use this option with caution!
string_mask = nombstr
# req_extensions = v3_req # The extensions to add to a certificate request
[ req_attributes ]
challengePassword = A challenge password
[ req_distinguished_name ]
C=DE
ST=Oost-Vlaanderen
L=Gent
O=Corp
OU=Testing Team
CN=johnsmith
[email protected]
[ usr_cert ]
# These extensions are added when 'ca' signs a request.
# This goes against PKIX guidelines but some CAs do it and some software
# requires this to avoid interpreting an end user certificate as a CA.
basicConstraints=CA:TRUE
# Here are some examples of the usage of nsCertType. If it is omitted
# the certificate can be used for anything *except* object signing.
# This is OK for an SSL server.
# nsCertType = server
# For an object signing certificate this would be used.
# nsCertType = objsign
# For normal client use this is typical
# nsCertType = client, email
# and for everything including object signing:
# nsCertType = client, email, objsign
# This is typical in keyUsage for a client certificate.
# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
# This will be displayed in Netscape's comment listbox.
nsComment = "OpenSSL Generated Certificate"
# PKIX recommendations harmless if included in all certificates.
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
# This stuff is for subjectAltName and issuerAltname.
# Import the email address.
# subjectAltName=email:copy
# An alternative to produce certificates that aren't
# deprecated according to PKIX.
# subjectAltName=email:move
# Copy subject details
# issuerAltName=issuer:copy
#nsCaRevocationUrl= http://www.domain.dom/ca-crl.pem
#nsBaseUrl
#nsRevocationUrl
#nsRenewalUrl
#nsCaPolicyUrl
#nsSslServerName
[ v3_req ]
# Extensions to add to a certificate request
basicConstraints = CA:TRUE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
[ v3_ca ]
# Extensions for a typical CA
# PKIX recommendation.
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer:always
# This is what PKIX recommends but some broken software chokes on critical
# extensions.
#basicConstraints = critical,CA:true
# So we do this instead.
basicConstraints = CA:true
# Key usage: this is typical for a CA certificate. However since it will
# prevent it being used as an test self-signed certificate it is best
# left out by default.
# keyUsage = cRLSign, keyCertSign
# Some might want this also
# nsCertType = sslCA, emailCA
# Include email address in subject alt name: another PKIX recommendation
# subjectAltName=email:copy
# Copy issuer details
# issuerAltName=issuer:copy
# DER hex encoding of an extension: beware experts only!
# obj=DER:02:03
# Where 'obj' is a standard or added object
# You can even override a supported extension:
# basicConstraints= critical, DER:30:03:01:01:FF
[ crl_ext ]
# CRL extensions.
# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL.
# issuerAltName=issuer:copy
authorityKeyIdentifier=keyid:always,issuer:always
[ proxy_cert_ext ]
# These extensions should be added when creating a proxy certificate
# This goes against PKIX guidelines but some CAs do it and some software
# requires this to avoid interpreting an end user certificate as a CA.
basicConstraints=CA:FALSE
# Here are some examples of the usage of nsCertType. If it is omitted
# the certificate can be used for anything *except* object signing.
# This is OK for an SSL server.
# nsCertType = server
# For an object signing certificate this would be used.
# nsCertType = objsign
# For normal client use this is typical
# nsCertType = client, email
# and for everything including object signing:
# nsCertType = client, email, objsign
# This is typical in keyUsage for a client certificate.
# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
# This will be displayed in Netscape's comment listbox.
nsComment = "OpenSSL Generated Certificate"
# PKIX recommendations harmless if included in all certificates.
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer:always
# This stuff is for subjectAltName and issuerAltname.
# Import the email address.
# subjectAltName=email:copy
# An alternative to produce certificates that aren't
# deprecated according to PKIX.
# subjectAltName=email:move
# Copy subject details
# issuerAltName=issuer:copy
#nsCaRevocationUrl= http://www.domain.dom/ca-crl.pem
#nsBaseUrl
#nsRevocationUrl
#nsRenewalUrl
#nsCaPolicyUrl
#nsSslServerName
# This really needs to be in place for it to be a proxy certificate.
proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo
[ my_v3_ext ]
basicConstraints = CA:true
第三方.pem:
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
d9:6f:db:ad:33:69:ce:63
Signature Algorithm: sha1WithRSAEncryption
Issuer: C=DE, ST=Berlin, L=Berlin, O=AVM GmbH, OU=Euro-DOCSIS, OU=Germany, CN=AVM GmbH Cable Modem Root Certificate Authority
Validity
Not Before: Jan 20 13:28:06 2010 GMT
Not After : Jun 7 13:28:06 2037 GMT
Subject: C=DE, O=AVM GmbH, OU=Euro-DOCSIS, CN=00:04:0E:C4:AE:94
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public Key: (1024 bit)
Modulus (1024 bit):
00:d5:73:b6:23:c6:23:ff:97:25:84:21:b7:25:f1:
8f:f7:c1:ae:dc:13:ea:56:69:c1:f4:9c:74:d7:b8:
50:74:2b:0b:4c:56:0f:ab:8e:b3:8e:04:26:74:e6:
7c:8d:23:2e:34:74:6c:cc:d6:d6:81:b1:ba:1a:ed:
80:fa:fd:c5:65:09:23:5e:6d:b9:15:9e:52:9f:d2:
66:f7:3d:b9:4b:2b:ef:ab:80:72:26:47:24:17:35:
c9:2a:c7:df:53:a6:15:c0:f4:b7:bc:40:37:29:51:
c5:50:9a:90:d8:af:56:42:9a:07:3c:8c:77:f0:93:
09:c8:0b:52:7a:3d:64:5f:d9
Exponent: 65537 (0x10001)
Signature Algorithm: sha1WithRSAEncryption
27:64:a3:d5:76:d2:86:32:6e:c0:e5:45:39:1f:2a:1a:04:d9:
46:d9:92:ee:71:a1:7d:07:77:ac:1a:0f:2a:e2:a1:3c:d2:aa:
13:e2:df:55:6a:3c:93:54:e4:63:b8:5d:3c:6b:97:3b:b2:2e:
e6:c0:17:6c:6b:e2:0c:ea:66:48:42:02:f3:41:d2:cc:ca:ed:
64:a6:b9:78:97:b3:df:87:75:61:fb:86:49:18:03:86:7c:4c:
89:cc:ba:6b:6d:ad:6c:4b:da:ad:1c:65:b8:42:9d:ad:c5:b1:
35:60:36:b4:e2:1f:a3:c9:74:e4:34:3a:b5:d2:d6:1c:80:97:
4d:8f:b3:ab:c4:35:95:c4:ca:e5:93:2b:dc:36:54:51:04:d3:
ea:34:c6:2f:7b:fd:7e:24:04:35:9d:c2:6b:59:e0:ef:dc:0e:
6b:27:96:d3:9c:63:87:4d:c3:17:b6:4b:24:39:25:08:4e:d8:
0e:14:8f:a9:8d:63:ff:74:d3:62:de:d7:11:71:e6:fd:fc:94:
0d:77:27:9f:a3:e5:4c:84:9b:7a:d0:4b:6e:ba:b6:d5:35:9b:
97:2f:c4:ff:b9:6b:fa:de:50:2f:38:12:4f:40:2c:55:8c:e7:
db:42:fc:df:f9:65:74:52:81:42:b0:88:c8:9b:0f:9b:09:93:
de:ae:e1:19
-----BEGIN CERTIFICATE-----
MIIC9DCCAdygAwIBAgIJANlv260zac5jMA0GCSqGSIb3DQEBBQUAMIGkMQswCQYD
VQQGEwJERTEPMA0GA1UECBMGQmVybGluMQ8wDQYDVQQHEwZCZXJsaW4xETAPBgNV
BAoTCEFWTSBHbWJIMRQwEgYDVQQLEwtFdXJvLURPQ1NJUzEQMA4GA1UECxMHR2Vy
bWFueTE4MDYGA1UEAxMvQVZNIEdtYkggQ2FibGUgTW9kZW0gUm9vdCBDZXJ0aWZp
Y2F0ZSBBdXRob3JpdHkwHhcNMTAwMTIwMTMyODA2WhcNMzcwNjA3MTMyODA2WjBS
MQswCQYDVQQGEwJERTERMA8GA1UEChMIQVZNIEdtYkgxFDASBgNVBAsTC0V1cm8t
RE9DU0lTMRowGAYDVQQDExEwMDowNDowRTpDNDpBRTo5NDCBnzANBgkqhkiG9w0B
AQEFAAOBjQAwgYkCgYEA1XO2I8Yj/5clhCG3JfGP98Gu3BPqVmnB9Jx017hQdCsL
TFYPq46zjgQmdOZ8jSMuNHRszNbWgbG6Gu2A+v3FZQkjXm25FZ5Sn9Jm9z25Syvv
q4ByJkckFzXJKsffU6YVwPS3vEA3KVHFUJqQ2K9WQpoHPIx38JMJyAtSej1kX9kC
AwEAATANBgkqhkiG9w0BAQUFAAOCAQEAJ2Sj1XbShjJuwOVFOR8qGgTZRtmS7nGh
fQd3rBoPKuKhPNKqE+LfVWo8k1TkY7hdPGuXO7Iu5sAXbGviDOpmSEIC80HSzMrt
ZKa5eJez34d1YfuGSRgDhnxMicy6a22tbEvarRxluEKdrcWxNWA2tOIfo8l05DQ6
tdLWHICXTY+zq8Q1lcTK5ZMr3DZUUQTT6jTGL3v9fiQENZ3Ca1ng79wOayeW05xj
h03DF7ZLJDklCE7YDhSPqY1j/3TTYt7XEXHm/fyUDXcnn6PlTISbetBLbrq21TWb
ly/E/7lr+t5QLzgST0AsVYzn20L83/lldFKBQrCIyJsPmwmT3q7hGQ==
-----END CERTIFICATE-----
使用 OpenCA 库的命令行实用程序是用 C 编写的:
#include <libpki/pki.h>
PKI_X509_OCSP_REQ * CreateOCSPRequest(PKI_X509_CERT * cacert, long long serial)
{
PKI_X509_OCSP_REQ *request = NULL;
request = PKI_X509_OCSP_REQ_new();
if (request == NULL )
{
printf("Memory Errorequest!");
exit(1);
}
// Adds the details about certificate (serial issued by cacert)
if (PKI_X509_OCSP_REQ_add_longlong( request, serial, cacert, NULL) == PKI_ERR)
{
printf ("Can not add serial!");
exit (1);
}
if (PKI_X509_OCSP_REQ_add_nonce(request, 0) == PKI_ERR )
{
printf ("Can not add NONCE to REQUEST!");
exit(1);
}
return request;
}
PKI_X509_OCSP_RESP * CreateOCSPResponse(PKI_X509_OCSP_REQ *request, PKI_OCSP_CERTSTATUS ocspStatus)
{
PKI_X509_OCSP_RESP * resp = NULL;
PKI_OCSP_CERTID *cid = NULL;
// Create a new OCSP response
if ((resp = PKI_X509_OCSP_RESP_new()) == NULL)
{
printf("Memory allocation errorequest!\n");
exit (1);
}
// Now, let's set the status of the response
if (PKI_X509_OCSP_RESP_set_status(resp, PKI_X509_OCSP_RESP_STATUS_SUCCESSFUL) == PKI_ERR)
{
printf("ERROR: setting response status failed.\n");
exit (1);
}
printf ("Set response status.\n");
// We now want to copy the NONCE from the request...
if (PKI_X509_OCSP_RESP_copy_nonce(resp, request) == PKI_ERR)
{
printf("ERROR::can not copy NONCE!!!\n");
exit (1);
}
printf ("Added NONCE.\n");
// We also need the Certificate Identifier from the request
if ((cid = PKI_X509_OCSP_REQ_get_cid(request, 0)) == NULL)
{
printf("Can not get CID from request!\n");
exit (1);
}
// Let's add the status of the requested certificate to the response
int ret = PKI_ERR;
if (ocspStatus == PKI_OCSP_CERTSTATUS_REVOKED)
{
PKI_TIME * revocationTime = PKI_TIME_new(0);
PKI_TIME * thisUpdate = PKI_TIME_new(100);
PKI_TIME * nextUpdate = PKI_TIME_new(200);
ret = PKI_X509_OCSP_RESP_add(resp, cid, ocspStatus, revocationTime, thisUpdate, nextUpdate, CRL_REASON_KEY_COMPROMISE, NULL);
PKI_TIME_free(revocationTime);
PKI_TIME_free(thisUpdate);
PKI_TIME_free(nextUpdate);
}
else
{
ret = PKI_X509_OCSP_RESP_add(resp, cid, ocspStatus, NULL, NULL, NULL, CRL_REASON_UNSPECIFIED, NULL);
}
if (ret == PKI_ERR)
{
printf("Add CID to response failed!\n");
exit (1);
}
printf ("Added CID.\n");
return resp;
}
PKI_X509_CERT * ReadCertificate(char * file)
{
PKI_X509_CERT * cert = NULL;
if ((cert = PKI_X509_CERT_get(file, NULL, NULL)) == NULL)
{
printf("Failed to load certificate: %s\n", file);
exit(1);
}
return cert;
}
void SignResponse(PKI_X509_OCSP_RESP * resp)
{
PKI_X509_KEYPAIR * keyPair = PKI_X509_KEYPAIR_get("root.key", NULL, NULL);
if (!keyPair)
{
printf("Obtaining keypair from root.key failed!");
exit (1);
}
PKI_X509_CERT * caCertificate = ReadCertificate("CA/cacert.pem");
PKI_X509_CERT * signerCertificate = ReadCertificate("root.pem");
PKI_X509_OCSP_RESP_sign(resp, keyPair, signerCertificate, caCertificate, NULL, NULL);
}
void genresponse(PKI_OCSP_CERTSTATUS ocspStatus)
{
PKI_X509_OCSP_REQ *request = NULL;
PKI_X509_OCSP_RESP * resp = NULL;
PKI_X509_CERT * cacert = NULL;
PKI_TOKEN * tk = NULL;
if ((request = PKI_X509_OCSP_REQ_get("req.der", NULL, NULL)) == NULL)
{
printf("ERROR, can't load req.der!\n");
exit(1);
}
resp = CreateOCSPResponse(request, ocspStatus);
// Now sign the response
SignResponse(resp);
printf("Response has been signed.\n");
PKI_X509_OCSP_RESP_put(resp, PKI_DATA_FORMAT_ASN1, "resp.der", NULL, NULL, NULL);
printf("Response file resp.der has been written.\n");
}
void genrequest(long long serial)
{
PKI_X509_OCSP_REQ *request = NULL;
PKI_X509_OCSP_RESP * resp = NULL;
PKI_X509_CERT * cacert = NULL;
if ((cacert = PKI_X509_CERT_get("cacert.pem", NULL, NULL)) == NULL)
{
printf("ERROR, can not load cacert.pem!\n");
exit(1);
}
request = CreateOCSPRequest(cacert, serial);
PKI_X509_OCSP_REQ_put(request, PKI_DATA_FORMAT_ASN1, "req.der", NULL, NULL, NULL);
printf("req.der written.\n");
}
void PrintUsage()
{
printf("Usage: ocsp { genrequest <serial> | genresponse <good | revoked | unknown> } \n");
}
/**
* Main function
*
*/
int main(int argc, char** argv)
{
PKI_init_all();
if (argc < 3)
{
PrintUsage();
exit(1);
}
if (0 == strcmp(argv[1], "genrequest"))
{
long long serial = 0;
sscanf(argv[2], "%lld", &serial);
genrequest(serial);
}
else if (0 == strcmp(argv[1], "genresponse"))
{
PKI_OCSP_CERTSTATUS certStatus = -1;
if (0 == strcmp(argv[2], "good"))
{
certStatus = PKI_OCSP_CERTSTATUS_GOOD;
}
else if (0 == strcmp(argv[2], "revoked"))
{
certStatus = PKI_OCSP_CERTSTATUS_REVOKED;
}
else if (0 == strcmp(argv[2], "unknown"))
{
certStatus = PKI_OCSP_CERTSTATUS_UNKNOWN;
}
else
{
printf("Unknown response type: %s.\n", argv[2]);
PrintUsage();
exit(1);
}
genresponse(certStatus);
}
else
{
PrintUsage();
exit(1);
}
return 0;
}