You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
377 lines
13 KiB
377 lines
13 KiB
//+-------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
//
|
|
// Copyright (C) Microsoft Corporation, 1998 - 1999
|
|
//
|
|
// File: selfsign.cpp
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
#include "global.hxx"
|
|
#include <dbgdef.h>
|
|
|
|
PCCERT_CONTEXT
|
|
WINAPI
|
|
CertCreateSelfSignCertificate(
|
|
IN HCRYPTPROV hProv,
|
|
IN PCERT_NAME_BLOB pSubjectIssuerBlob,
|
|
IN DWORD dwFlags,
|
|
OPTIONAL PCRYPT_KEY_PROV_INFO pKeyProvInfo,
|
|
OPTIONAL PCRYPT_ALGORITHM_IDENTIFIER pSignatureAlgorithm,
|
|
OPTIONAL PSYSTEMTIME pStartTime,
|
|
OPTIONAL PSYSTEMTIME pEndTime,
|
|
OPTIONAL PCERT_EXTENSIONS pExtensions
|
|
) {
|
|
|
|
PCCERT_CONTEXT pCertContext = NULL;
|
|
DWORD errBefore = GetLastError();
|
|
DWORD err = ERROR_SUCCESS;
|
|
DWORD cbPubKeyInfo = 0;
|
|
PCERT_PUBLIC_KEY_INFO pPubKeyInfo = NULL;
|
|
BYTE * pbCert = NULL;
|
|
DWORD cbCert = 0;
|
|
LPSTR sz = NULL;
|
|
DWORD cb = 0;
|
|
BYTE * pbToBeSigned = NULL;
|
|
BYTE * pbSignature = NULL;
|
|
|
|
CERT_INFO certInfo;
|
|
GUID serialNbr;
|
|
CRYPT_KEY_PROV_INFO keyProvInfo;
|
|
CERT_SIGNED_CONTENT_INFO sigInfo;
|
|
|
|
CRYPT_ALGORITHM_IDENTIFIER algID;
|
|
|
|
LPWSTR wsz = NULL;
|
|
BOOL fFreehProv = FALSE;
|
|
HCRYPTKEY hKey = NULL;
|
|
UUID guidContainerName;
|
|
RPC_STATUS RpcStatus;
|
|
|
|
memset(&certInfo, 0, sizeof(CERT_INFO));
|
|
memset(&serialNbr, 0, sizeof(serialNbr));
|
|
memset(&keyProvInfo, 0, sizeof(CRYPT_KEY_PROV_INFO));
|
|
memset(&sigInfo, 0, sizeof(CERT_SIGNED_CONTENT_INFO));
|
|
|
|
// do key spec now because we need it
|
|
if(pKeyProvInfo == NULL)
|
|
keyProvInfo.dwKeySpec = AT_SIGNATURE;
|
|
else
|
|
keyProvInfo.dwKeySpec = pKeyProvInfo->dwKeySpec;
|
|
|
|
// see if we have an hProv, if not, create one
|
|
if(hProv == NULL) {
|
|
|
|
fFreehProv = TRUE;
|
|
|
|
// if not prov info, make one up, signing RSA cert, default provider
|
|
if(pKeyProvInfo == NULL) {
|
|
|
|
RpcStatus = UuidCreate(&guidContainerName);
|
|
if (!(RPC_S_OK == RpcStatus ||
|
|
RPC_S_UUID_LOCAL_ONLY == RpcStatus)) {
|
|
// Use stack randomness
|
|
;
|
|
}
|
|
|
|
// note: unlike UUidToString, always must LocalFree() returned memory
|
|
UuidToStringU(&guidContainerName, &wsz);
|
|
|
|
if( !CryptAcquireContextU(
|
|
&hProv,
|
|
wsz,
|
|
NULL,
|
|
PROV_RSA_FULL,
|
|
CRYPT_NEWKEYSET) ) {
|
|
hProv = NULL;
|
|
goto ErrorCryptAcquireContext;
|
|
}
|
|
}
|
|
else {
|
|
|
|
// first use the existing keyset
|
|
if( !CryptAcquireContextU(
|
|
&hProv,
|
|
pKeyProvInfo->pwszContainerName,
|
|
pKeyProvInfo->pwszProvName,
|
|
pKeyProvInfo->dwProvType,
|
|
pKeyProvInfo->dwFlags) ) {
|
|
|
|
// otherwise generate a keyset
|
|
if( !CryptAcquireContextU(
|
|
&hProv,
|
|
pKeyProvInfo->pwszContainerName,
|
|
pKeyProvInfo->pwszProvName,
|
|
pKeyProvInfo->dwProvType,
|
|
pKeyProvInfo->dwFlags | CRYPT_NEWKEYSET) ) {
|
|
hProv = NULL;
|
|
goto ErrorCryptAcquireContext;
|
|
}
|
|
}
|
|
}
|
|
|
|
// we have the keyset, now make sure we have the key gen'ed
|
|
if( !CryptGetUserKey( hProv,
|
|
keyProvInfo.dwKeySpec,
|
|
&hKey) ) {
|
|
|
|
// doesn't exist so gen it
|
|
assert(hKey == NULL);
|
|
if(!CryptGenKey( hProv,
|
|
keyProvInfo.dwKeySpec,
|
|
0,
|
|
&hKey) ) {
|
|
goto ErrorCryptGenKey;
|
|
}
|
|
}
|
|
}
|
|
|
|
// get the exportable public key bits
|
|
if( !CryptExportPublicKeyInfo( hProv,
|
|
keyProvInfo.dwKeySpec,
|
|
X509_ASN_ENCODING,
|
|
NULL,
|
|
&cbPubKeyInfo) ||
|
|
(pPubKeyInfo =
|
|
(PCERT_PUBLIC_KEY_INFO) PkiNonzeroAlloc(cbPubKeyInfo)) == NULL ||
|
|
!CryptExportPublicKeyInfo( hProv,
|
|
keyProvInfo.dwKeySpec,
|
|
X509_ASN_ENCODING,
|
|
pPubKeyInfo,
|
|
&cbPubKeyInfo) )
|
|
goto ErrorCryptExportPublicKeyInfo;
|
|
|
|
// default if we don't have an algid
|
|
if(pSignatureAlgorithm == NULL) {
|
|
memset(&algID, 0, sizeof(algID));
|
|
algID.pszObjId = szOID_OIWSEC_sha1RSASign;
|
|
pSignatureAlgorithm = &algID;
|
|
}
|
|
|
|
// make a temp cert, only care about key info
|
|
// and serial number for uniqueness
|
|
RpcStatus = UuidCreate(&serialNbr);
|
|
if (!(RPC_S_OK == RpcStatus || RPC_S_UUID_LOCAL_ONLY == RpcStatus)) {
|
|
// Use stack randomness.
|
|
;
|
|
}
|
|
certInfo.dwVersion = CERT_V3;
|
|
certInfo.SubjectPublicKeyInfo = *pPubKeyInfo;
|
|
certInfo.SerialNumber.cbData = sizeof(serialNbr);
|
|
certInfo.SerialNumber.pbData = (BYTE *) &serialNbr;
|
|
certInfo.SignatureAlgorithm = *pSignatureAlgorithm;
|
|
certInfo.Issuer = *pSubjectIssuerBlob;
|
|
certInfo.Subject = *pSubjectIssuerBlob;
|
|
|
|
// only put in extensions if we have them
|
|
if( pExtensions != NULL) {
|
|
certInfo.cExtension = pExtensions->cExtension;
|
|
certInfo.rgExtension = pExtensions->rgExtension;
|
|
}
|
|
|
|
//default if we don't have times
|
|
if(pStartTime == NULL)
|
|
GetSystemTimeAsFileTime(&certInfo.NotBefore);
|
|
else if(!SystemTimeToFileTime(pStartTime, &certInfo.NotBefore))
|
|
goto ErrorSystemTimeToFileTime;
|
|
|
|
if(pEndTime == NULL)
|
|
*(((DWORDLONG UNALIGNED *) &certInfo.NotAfter)) =
|
|
*(((DWORDLONG UNALIGNED *) &certInfo.NotBefore)) +
|
|
0x11F03C3613000i64;
|
|
else if(!SystemTimeToFileTime(pEndTime, &certInfo.NotAfter))
|
|
goto ErrorSystemTimeToFileTime;
|
|
|
|
// encode the cert
|
|
if( !CryptEncodeObject(
|
|
CRYPT_ASN_ENCODING, X509_CERT_TO_BE_SIGNED,
|
|
&certInfo,
|
|
NULL, // pbEncoded
|
|
&sigInfo.ToBeSigned.cbData
|
|
) ||
|
|
(pbToBeSigned = (BYTE *)
|
|
PkiNonzeroAlloc(sigInfo.ToBeSigned.cbData)) == NULL ||
|
|
!CryptEncodeObject(
|
|
CRYPT_ASN_ENCODING, X509_CERT_TO_BE_SIGNED,
|
|
&certInfo,
|
|
pbToBeSigned,
|
|
&sigInfo.ToBeSigned.cbData
|
|
) )
|
|
goto ErrorEncodeTempCertToBeSigned;
|
|
sigInfo.ToBeSigned.pbData = pbToBeSigned;
|
|
|
|
// sign the certificate
|
|
sigInfo.SignatureAlgorithm = certInfo.SignatureAlgorithm;
|
|
|
|
// this is to work around an OSS bug of not accepting zero length bit strings
|
|
// this is only needed if we don't actually sign the code.
|
|
sigInfo.Signature.pbData = (BYTE *) &sigInfo;
|
|
sigInfo.Signature.cbData = 1;
|
|
|
|
if( (CERT_CREATE_SELFSIGN_NO_SIGN & dwFlags) == 0 ) {
|
|
if( !CryptSignCertificate(
|
|
hProv,
|
|
keyProvInfo.dwKeySpec,
|
|
CRYPT_ASN_ENCODING,
|
|
sigInfo.ToBeSigned.pbData,
|
|
sigInfo.ToBeSigned.cbData,
|
|
&sigInfo.SignatureAlgorithm,
|
|
NULL,
|
|
NULL,
|
|
&sigInfo.Signature.cbData) ||
|
|
(pbSignature = (BYTE *)
|
|
PkiNonzeroAlloc(sigInfo.Signature.cbData)) == NULL ||
|
|
!CryptSignCertificate(
|
|
hProv,
|
|
keyProvInfo.dwKeySpec,
|
|
CRYPT_ASN_ENCODING,
|
|
sigInfo.ToBeSigned.pbData,
|
|
sigInfo.ToBeSigned.cbData,
|
|
&sigInfo.SignatureAlgorithm,
|
|
NULL,
|
|
pbSignature,
|
|
&sigInfo.Signature.cbData) )
|
|
goto ErrorCryptSignCertificate;
|
|
sigInfo.Signature.pbData = pbSignature;
|
|
}
|
|
|
|
// encode the final cert.
|
|
if( !CryptEncodeObject(
|
|
CRYPT_ASN_ENCODING,
|
|
X509_CERT,
|
|
&sigInfo,
|
|
NULL,
|
|
&cbCert
|
|
) ||
|
|
(pbCert = (BYTE *)
|
|
PkiNonzeroAlloc(cbCert)) == NULL ||
|
|
!CryptEncodeObject(
|
|
CRYPT_ASN_ENCODING,
|
|
X509_CERT,
|
|
&sigInfo,
|
|
pbCert,
|
|
&cbCert ) )
|
|
goto ErrorEncodeTempCert;
|
|
|
|
// get a cert context from the encoding
|
|
if( (pCertContext = CertCreateCertificateContext(
|
|
CRYPT_ASN_ENCODING,
|
|
pbCert,
|
|
cbCert)) == NULL )
|
|
goto ErrorCreateTempCertContext;
|
|
|
|
if( (CERT_CREATE_SELFSIGN_NO_KEY_INFO & dwFlags) == 0 ) {
|
|
|
|
// get the key prov info
|
|
if(pKeyProvInfo == NULL) {
|
|
|
|
// get a key prov info from the hProv
|
|
if( !CryptGetProvParam( hProv,
|
|
PP_NAME,
|
|
NULL,
|
|
&cb,
|
|
0) ||
|
|
(sz = (char *) PkiNonzeroAlloc(cb)) == NULL ||
|
|
!CryptGetProvParam( hProv,
|
|
PP_NAME,
|
|
(BYTE *) sz,
|
|
&cb,
|
|
0) )
|
|
goto ErrorGetProvName;
|
|
keyProvInfo.pwszProvName = MkWStr(sz);
|
|
PkiFree(sz);
|
|
sz = NULL;
|
|
|
|
cb = 0;
|
|
if( !CryptGetProvParam( hProv,
|
|
PP_CONTAINER,
|
|
NULL,
|
|
&cb,
|
|
0) ||
|
|
(sz = (char *) PkiNonzeroAlloc(cb)) == NULL ||
|
|
!CryptGetProvParam( hProv,
|
|
PP_CONTAINER,
|
|
(BYTE *) sz,
|
|
&cb,
|
|
0) )
|
|
goto ErrorGetContainerName;
|
|
keyProvInfo.pwszContainerName = MkWStr(sz);
|
|
|
|
cb = sizeof(keyProvInfo.dwProvType);
|
|
if( !CryptGetProvParam( hProv,
|
|
PP_PROVTYPE,
|
|
(BYTE *) &keyProvInfo.dwProvType,
|
|
&cb,
|
|
0) )
|
|
goto ErrorGetProvType;
|
|
|
|
pKeyProvInfo = &keyProvInfo;
|
|
}
|
|
|
|
// put the key property on the certificate
|
|
if( !CertSetCertificateContextProperty(
|
|
pCertContext,
|
|
CERT_KEY_PROV_INFO_PROP_ID,
|
|
0,
|
|
pKeyProvInfo) )
|
|
goto ErrorSetTempCertPropError;
|
|
}
|
|
|
|
CommonReturn:
|
|
|
|
if(hKey != NULL)
|
|
CryptDestroyKey(hKey);
|
|
|
|
if(fFreehProv && hProv != NULL)
|
|
CryptReleaseContext(hProv, 0);
|
|
|
|
if(keyProvInfo.pwszProvName != NULL)
|
|
FreeWStr(keyProvInfo.pwszProvName);
|
|
|
|
if(keyProvInfo.pwszContainerName != NULL)
|
|
FreeWStr(keyProvInfo.pwszContainerName);
|
|
|
|
if(wsz != NULL)
|
|
LocalFree(wsz);
|
|
|
|
PkiFree(pPubKeyInfo);
|
|
PkiFree(pbToBeSigned);
|
|
PkiFree(pbSignature);
|
|
PkiFree(pbCert);
|
|
PkiFree(sz);
|
|
|
|
// don't know if we have an error or not
|
|
// but I do know the errBefore is set properly
|
|
SetLastError(errBefore);
|
|
|
|
return(pCertContext);
|
|
|
|
ErrorReturn:
|
|
|
|
if(GetLastError() == ERROR_SUCCESS)
|
|
SetLastError((DWORD) E_UNEXPECTED);
|
|
err = GetLastError();
|
|
|
|
// We have an error, make sure we set it.
|
|
errBefore = GetLastError();
|
|
|
|
if(pCertContext != NULL)
|
|
CertFreeCertificateContext(pCertContext);
|
|
pCertContext = NULL;
|
|
|
|
goto CommonReturn;
|
|
|
|
TRACE_ERROR(ErrorCryptGenKey);
|
|
TRACE_ERROR(ErrorCryptAcquireContext);
|
|
TRACE_ERROR(ErrorCryptExportPublicKeyInfo);
|
|
TRACE_ERROR(ErrorEncodeTempCertToBeSigned);
|
|
TRACE_ERROR(ErrorEncodeTempCert);
|
|
TRACE_ERROR(ErrorCreateTempCertContext);
|
|
TRACE_ERROR(ErrorGetProvName);
|
|
TRACE_ERROR(ErrorGetContainerName);
|
|
TRACE_ERROR(ErrorGetProvType);
|
|
TRACE_ERROR(ErrorSetTempCertPropError);
|
|
TRACE_ERROR(ErrorCryptSignCertificate);
|
|
TRACE_ERROR(ErrorSystemTimeToFileTime);
|
|
}
|