2009-02-24 27 views
8

Chúng tôi đang phát triển một số dịch vụ WCF. các yêu cầu sẽ vượt qua giới hạn miền; nghĩa là, các máy khách đang chạy trong một miền và các máy chủ xử lý các yêu cầu nằm trong một miền (sản xuất) khác nhau. Tôi biết cách bảo mật liên kết này bằng SSL và chứng chỉ. Chúng tôi sẽ trợ giúp người dùng về tên người dùng và mật khẩu của họ trên miền sản xuất và chuyển những người dùng đó vào các tiêu đề SOAP.Làm cách nào để tránh chứng chỉ SSL trong quá trình phát triển cho điểm cuối WCF sẽ được bảo đảm trong quá trình sản xuất

Vấn đề của tôi là phải làm gì trong quá trình phát triển và thử nghiệm "beta". Tôi biết rằng tôi có thể nhận được chứng chỉ tạm thời và sử dụng chứng chỉ đó trong quá trình phát triển. Tôi tự hỏi những lựa chọn thay thế của tôi cho phương pháp này là gì. Những người khác đã làm gì trong tình huống này?

Cập nhật: Tôi không chắc chắn rằng tôi đã nhận được câu trả lời "tốt" cho câu hỏi của mình. Tôi là thành viên của một nhóm lớn (hơn 50) nhà phát triển. Tổ chức này khá nhanh nhẹn. Bất kỳ một trong những nhà phát triển có thể kết thúc làm việc trên dự án đang sử dụng WCF. Trong thực tế, một số dự án khác đang làm một cái gì đó tương tự nhưng đối với các trang web và dịch vụ khác nhau. Những gì tôi đang tìm kiếm là một cách mà tôi có thể có bất cứ ai đến và làm việc trong dự án cụ thể này trong một vài ngày mà không cần phải nhảy qua một số vòng. Cài đặt chứng chỉ phát triển là một trong những hoops đó. Tôi hoàn toàn hiểu rằng "dogfooding" cấu trúc WCF trong quá trình phát triển là cách thực hành tốt nhất. Hầu hết các câu trả lời cho rằng đó là câu trả lời. Tôi muốn biết cái gì, nếu có, có nghĩa là khác hơn là "có được một giấy chứng nhận kiểm tra (hoặc hai) và cài đặt nó trên tất cả các hộp phát triển."

Jon

Trả lời

0

Làm cách nào để thay đổi cấu hình giữa phát triển và sản xuất?

0

Đề nghị của tôi sẽ được xem xét một vài phương pháp khác nhau:

Đối với phát triển -> Có nhiều cách để tạo ra một giấy chứng nhận SSL tại địa phương để kiểm tra với https có thể được thực hiện trong một môi trường mà bạn có toàn quyền kiểm soát .

Để thử nghiệm "beta" -> Cân nhắc nhận chứng chỉ thứ hai cho điều này vì có thể cần phải thực hiện một số thử nghiệm beta giữa các lần phát hành để có thể sử dụng nó nhiều lần.

2

Thực sự bạn muốn môi trường phát triển của mình phù hợp với sản xuất nhiều nhất có thể. WCF sẽ kiểm tra danh sách thu hồi trong quá trình đàm phán vận chuyển hoặc kiểm tra chữ ký và chứng nhận tự ký, hoặc xác nhận giả mạo bằng makecert không hỗ trợ CRL.

Nếu bạn có một máy dự phòng, bạn có thể sử dụng Dịch vụ chứng chỉ Windows (miễn phí với Server 2003 và 2008). Điều này cung cấp một CA và bạn có thể yêu cầu chứng chỉ (SSL hoặc máy khách) từ nó. Nó cần phải là một máy phụ tùng vì nó đặt chính nó lên theo trang web mặc định và hoàn toàn messes lên nếu bạn đã tinh chỉnh đó. Nó cũng xuất bản CRL. Tất cả những gì bạn cần làm là cài đặt chứng chỉ gốc cho CA trên các hộp phát triển của bạn và bạn sẽ đi.

1

Bạn có tùy chọn tạo chứng chỉ để sử dụng trong quá trình phát triển hoặc vô hiệu hóa việc sử dụng chứng chỉ thông qua tệp cấu hình. Tôi khuyên bạn nên sử dụng chứng chỉ cũng đang được phát triển.

5

CẬP NHẬT: Chúng tôi thực sự sử dụng đơn giản hơn nhiều Keith Brown solution thay vì điều này ngay bây giờ, xem mã nguồn mà anh đã cung cấp. Ưu điểm: Không có mã không được quản lý để duy trì.

Nếu bạn vẫn muốn xem cách thực hiện bằng C/C++, hãy đọc tiếp ...

Chỉ được khuyến nghị sử dụng trong quá trình phát triển, không phải sản xuất, nhưng có một cách ma sát thấp để tạo chứng chỉ X.509 (không cần sử dụng makecert.exe).

Nếu bạn có quyền truy cập vào CryptoAPI trên Windows, ý tưởng là bạn sử dụng các cuộc gọi CryptoAPI để tạo khóa công khai và riêng tư, ký và mã hóa chứng chỉ X.509 mới, đặt nó vào kho lưu trữ chứng chỉ chỉ bộ nhớ và sau đó sử dụng PFXExportCertStore() để tạo các byte .pfx mà sau đó bạn có thể chuyển đến hàm tạo X509Certificate2.

Khi bạn có phiên bản X509Certificate2, bạn có thể đặt nó làm tài sản của các đối tượng WCF thích hợp và mọi thứ chỉ bắt đầu hoạt động.

Tôi có một số mã ví dụ mà tôi đã viết, không đảm bảo bất kỳ loại khóa học nào và bạn sẽ cần một chút kinh nghiệm để viết các bit không được quản lý (nó sẽ đau đớn hơn nhiều viết P/Invoke cho tất cả các cuộc gọi CryptoAPI hơn là có phần đó trong C/C++).

Ví dụ C# mã mà sử dụng hàm helper không được quản lý:

public X509Certificate2 GenerateSelfSignedCertificate(string issuerCommonName, string keyPassword) 
    { 
     int pfxSize = -1; 
     IntPtr pfxBufferPtr = IntPtr.Zero; 
     IntPtr errorMessagePtr = IntPtr.Zero; 

     try 
     { 
      if (!X509GenerateSelfSignedCertificate(KeyContainerName, issuerCommonName, keyPassword, ref pfxSize, ref pfxBufferPtr, ref errorMessagePtr)) 
      { 
       string errorMessage = null; 

       if (errorMessagePtr != IntPtr.Zero) 
       { 
        errorMessage = Marshal.PtrToStringUni(errorMessagePtr); 
       } 

       throw new ApplicationException(string.Format("Failed to generate X.509 server certificate. {0}", errorMessage ?? "Unspecified error.")); 
      } 
      if (pfxBufferPtr == IntPtr.Zero) 
      { 
       throw new ApplicationException("Failed to generate X.509 server certificate. PFX buffer not initialized."); 
      } 
      if (pfxSize <= 0) 
      { 
       throw new ApplicationException("Failed to generate X.509 server certificate. PFX buffer size invalid."); 
      } 

      byte[] pfxBuffer = new byte[pfxSize]; 
      Marshal.Copy(pfxBufferPtr, pfxBuffer, 0, pfxSize); 
      return new X509Certificate2(pfxBuffer, keyPassword); 
     } 
     finally 
     { 
      if (pfxBufferPtr != IntPtr.Zero) 
      { 
       Marshal.FreeHGlobal(pfxBufferPtr); 
      } 
      if (errorMessagePtr != IntPtr.Zero) 
      { 
       Marshal.FreeHGlobal(errorMessagePtr); 
      } 
     } 
    } 

Việc thực hiện X509GenerateSelfSignedCertificate chức năng có thể đi một cái gì đó như thế này (bạn cần WinCrypt.h):

BOOL X509GenerateSelfSignedCertificate(LPCTSTR keyContainerName, LPCTSTR issuerCommonName, LPCTSTR keyPassword, DWORD *pfxSize, BYTE **pfxBuffer, LPTSTR *errorMessage) 
{ 
    // Constants 
#define CERT_DN_ATTR_COUNT 1 
#define SIZE_SERIALNUMBER  8 
#define EXPIRY_YEARS_FROM_NOW 2 
#define MAX_COMMON_NAME  8192 
#define MAX_PFX_SIZE   65535 

    // Declarations 
    HCRYPTPROV hProv = NULL; 
    BOOL result = FALSE; 

    // Sanity 

    if (pfxSize != NULL) 
    { 
     *pfxSize = -1; 
    } 
    if (pfxBuffer != NULL) 
    { 
     *pfxBuffer = NULL; 
    } 
    if (errorMessage != NULL) 
    { 
     *errorMessage = NULL; 
    } 

    if (keyContainerName == NULL || _tcslen(issuerCommonName) <= 0) 
    { 
     SetOutputErrorMessage(errorMessage, _T("Key container name must not be NULL or an empty string.")); 
     return FALSE; 
    } 
    if (issuerCommonName == NULL || _tcslen(issuerCommonName) <= 0) 
    { 
     SetOutputErrorMessage(errorMessage, _T("Issuer common name must not be NULL or an empty string.")); 
     return FALSE; 
    } 
    if (keyPassword == NULL || _tcslen(keyPassword) <= 0) 
    { 
     SetOutputErrorMessage(errorMessage, _T("Key password must not be NULL or an empty string.")); 
     return FALSE; 
    } 

    // Start generating 
    USES_CONVERSION; 

    if (CryptAcquireContext(&hProv, keyContainerName, MS_DEF_RSA_SCHANNEL_PROV, PROV_RSA_SCHANNEL, CRYPT_MACHINE_KEYSET) || 
     CryptAcquireContext(&hProv, keyContainerName, MS_DEF_RSA_SCHANNEL_PROV, PROV_RSA_SCHANNEL, CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET)) 
    { 
     HCRYPTKEY hKey = NULL; 

     // Generate 1024-bit RSA keypair. 
     if (CryptGenKey(hProv, AT_KEYEXCHANGE, CRYPT_EXPORTABLE | RSA1024BIT_KEY, &hKey)) 
     { 
      DWORD pkSize = 0; 
      PCERT_PUBLIC_KEY_INFO pkInfo = NULL; 

      // Export public key for use by certificate signing. 
      if (CryptExportPublicKeyInfo(hProv, AT_KEYEXCHANGE, X509_ASN_ENCODING, NULL, &pkSize) && 
       (pkInfo = (PCERT_PUBLIC_KEY_INFO)LocalAlloc(0, pkSize)) && 
       CryptExportPublicKeyInfo(hProv, AT_KEYEXCHANGE, X509_ASN_ENCODING, pkInfo, &pkSize)) 
      { 
       CERT_RDN_ATTR certDNAttrs[CERT_DN_ATTR_COUNT]; 
       CERT_RDN certDN[CERT_DN_ATTR_COUNT] = {{1, &certDNAttrs[0]}}; 
       CERT_NAME_INFO certNameInfo = {CERT_DN_ATTR_COUNT, &certDN[0]}; 
       DWORD certNameSize = -1; 
       BYTE *certNameData = NULL; 

       certDNAttrs[0].dwValueType = CERT_RDN_UNICODE_STRING; 
       certDNAttrs[0].pszObjId = szOID_COMMON_NAME; 
       certDNAttrs[0].Value.cbData = (DWORD)(_tcslen(issuerCommonName) * sizeof(WCHAR)); 
       certDNAttrs[0].Value.pbData = (BYTE*)T2W((LPTSTR)issuerCommonName); 

       // Encode issuer name into certificate name blob. 
       if (CryptEncodeObject(X509_ASN_ENCODING, X509_NAME, &certNameInfo, NULL, &certNameSize) && 
        (certNameData = (BYTE*)LocalAlloc(0, certNameSize)) && 
        CryptEncodeObject(X509_ASN_ENCODING, X509_NAME, &certNameInfo, certNameData, &certNameSize)) 
       { 
        CERT_NAME_BLOB issuerName; 
        CERT_INFO certInfo; 
        SYSTEMTIME systemTime; 
        FILETIME notBefore; 
        FILETIME notAfter; 
        BYTE serialNumber[SIZE_SERIALNUMBER]; 
        DWORD certSize = -1; 
        BYTE *certData = NULL; 

        issuerName.cbData = certNameSize; 
        issuerName.pbData = certNameData; 

        // Certificate should be valid for a decent window of time. 
        ZeroMemory(&certInfo, sizeof(certInfo)); 
        GetSystemTime(&systemTime); 
        systemTime.wYear -= 1; 
        SystemTimeToFileTime(&systemTime, &notBefore); 
        systemTime.wYear += EXPIRY_YEARS_FROM_NOW; 
        SystemTimeToFileTime(&systemTime, &notAfter); 

        // Generate a throwaway serial number. 
        if (CryptGenRandom(hProv, SIZE_SERIALNUMBER, serialNumber)) 
        { 
         certInfo.dwVersion = CERT_V3; 
         certInfo.SerialNumber.cbData = SIZE_SERIALNUMBER; 
         certInfo.SerialNumber.pbData = serialNumber; 
         certInfo.SignatureAlgorithm.pszObjId = szOID_RSA_MD5RSA; 
         certInfo.Issuer = issuerName; 
         certInfo.NotBefore = notBefore; 
         certInfo.NotAfter = notAfter; 
         certInfo.Subject = issuerName; 
         certInfo.SubjectPublicKeyInfo = *pkInfo; 

         // Now sign and encode it. 
         if (CryptSignAndEncodeCertificate(hProv, AT_KEYEXCHANGE, X509_ASN_ENCODING, X509_CERT_TO_BE_SIGNED, (LPVOID)&certInfo, &(certInfo.SignatureAlgorithm), NULL, NULL, &certSize) && 
          (certData = (BYTE*)LocalAlloc(0, certSize)) && 
          CryptSignAndEncodeCertificate(hProv, AT_KEYEXCHANGE, X509_ASN_ENCODING, X509_CERT_TO_BE_SIGNED, (LPVOID)&certInfo, &(certInfo.SignatureAlgorithm), NULL, certData, &certSize)) 
         { 
          HCERTSTORE hCertStore = NULL; 

          // Open a new temporary store. 
          if ((hCertStore = CertOpenStore(CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING, NULL, CERT_STORE_CREATE_NEW_FLAG, NULL))) 
          { 
           PCCERT_CONTEXT certContext = NULL; 

           // Add to temporary store so we can use the PFX functions to export a store + private keys in PFX format. 
           if (CertAddEncodedCertificateToStore(hCertStore, X509_ASN_ENCODING, certData, certSize, CERT_STORE_ADD_NEW, &certContext)) 
           { 
            CRYPT_KEY_PROV_INFO keyProviderInfo; 

            // Link keypair to certificate (without this the keypair gets "lost" on export). 
            ZeroMemory(&keyProviderInfo, sizeof(keyProviderInfo)); 
            keyProviderInfo.pwszContainerName = T2W((LPTSTR)keyContainerName); 
            keyProviderInfo.pwszProvName = MS_DEF_RSA_SCHANNEL_PROV_W; /* _W used intentionally. struct hardcodes LPWSTR. */ 
            keyProviderInfo.dwProvType = PROV_RSA_SCHANNEL; 
            keyProviderInfo.dwFlags = CRYPT_MACHINE_KEYSET; 
            keyProviderInfo.dwKeySpec = AT_KEYEXCHANGE; 

            // Finally, export to PFX and provide to caller. 
            if (CertSetCertificateContextProperty(certContext, CERT_KEY_PROV_INFO_PROP_ID, 0, (LPVOID)&keyProviderInfo)) 
            { 
             CRYPT_DATA_BLOB pfxBlob; 
             DWORD pfxExportFlags = EXPORT_PRIVATE_KEYS | REPORT_NO_PRIVATE_KEY | REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY; 

             // Calculate size required. 
             ZeroMemory(&pfxBlob, sizeof(pfxBlob)); 
             if (PFXExportCertStore(hCertStore, &pfxBlob, T2CW(keyPassword), pfxExportFlags)) 
             { 
              pfxBlob.pbData = (BYTE *)LocalAlloc(0, pfxBlob.cbData); 

              if (pfxBlob.pbData != NULL) 
              { 
               // Now export. 
               if (PFXExportCertStore(hCertStore, &pfxBlob, T2CW(keyPassword), pfxExportFlags)) 
               { 
                if (pfxSize != NULL) 
                { 
                 *pfxSize = pfxBlob.cbData; 
                } 
                if (pfxBuffer != NULL) 
                { 
                 // Caller must free this. 
                 *pfxBuffer = pfxBlob.pbData; 
                } 
                else 
                { 
                 // Caller did not provide target pointer to receive buffer, free ourselves. 
                 LocalFree(pfxBlob.pbData); 
                } 

                result = TRUE; 
               } 
               else 
               { 
                SetOutputErrorMessage(errorMessage, _T("Failed to export certificate in PFX format (0x%08x)."), GetLastError()); 
               } 
              } 
              else 
              { 
               SetOutputErrorMessage(errorMessage, _T("Failed to export certificate in PFX format, buffer allocation failure (0x%08x)."), GetLastError()); 
              } 
             } 
             else 
             { 
              SetOutputErrorMessage(errorMessage, _T("Failed to export certificate in PFX format, failed to calculate buffer size (0x%08x)."), GetLastError()); 
             } 
            } 
            else 
            { 
             SetOutputErrorMessage(errorMessage, _T("Failed to set certificate key context property (0x%08x)."), GetLastError()); 
            } 
           } 
           else 
           { 
            SetOutputErrorMessage(errorMessage, _T("Failed to add certificate to temporary certificate store (0x%08x)."), GetLastError()); 
           } 

           CertCloseStore(hCertStore, 0); 
           hCertStore = NULL; 
          } 
          else 
          { 
           SetOutputErrorMessage(errorMessage, _T("Failed to create temporary certificate store (0x%08x)."), GetLastError()); 
          } 
         } 
         else 
         { 
          SetOutputErrorMessage(errorMessage, _T("Failed to sign/encode certificate or out of memory (0x%08x)."), GetLastError()); 
         } 

         if (certData != NULL) 
         { 
          LocalFree(certData); 
          certData = NULL; 
         } 
        } 
        else 
        { 
         SetOutputErrorMessage(errorMessage, _T("Failed to generate certificate serial number (0x%08x)."), GetLastError()); 
        } 
       } 
       else 
       { 
        SetOutputErrorMessage(errorMessage, _T("Failed to encode X.509 certificate name into ASN.1 or out of memory (0x%08x)."), GetLastError()); 
       } 

       if (certNameData != NULL) 
       { 
        LocalFree(certNameData); 
        certNameData = NULL; 
       } 
      } 
      else 
      { 
       SetOutputErrorMessage(errorMessage, _T("Failed to export public key blob or out of memory (0x%08x)."), GetLastError()); 
      } 

      if (pkInfo != NULL) 
      { 
       LocalFree(pkInfo); 
       pkInfo = NULL; 
      } 
      CryptDestroyKey(hKey); 
      hKey = NULL; 
     } 
     else 
     { 
      SetOutputErrorMessage(errorMessage, _T("Failed to generate public/private keypair for certificate (0x%08x)."), GetLastError()); 
     } 

     CryptReleaseContext(hProv, 0); 
     hProv = NULL; 
    } 
    else 
    { 
     SetOutputErrorMessage(errorMessage, _T("Failed to acquire cryptographic context (0x%08x)."), GetLastError()); 
    } 

    return result; 
} 

void 
SetOutputErrorMessage(LPTSTR *errorMessage, LPCTSTR formatString, ...) 
{ 
#define MAX_ERROR_MESSAGE 1024 
    va_list va; 

    if (errorMessage != NULL) 
    { 
     size_t sizeInBytes = (MAX_ERROR_MESSAGE * sizeof(TCHAR)) + 1; 
     LPTSTR message = (LPTSTR)LocalAlloc(0, sizeInBytes); 

     va_start(va, formatString); 
     ZeroMemory(message, sizeInBytes); 
     if (_vstprintf_s(message, MAX_ERROR_MESSAGE, formatString, va) == -1) 
     { 
      ZeroMemory(message, sizeInBytes); 
      _tcscpy_s(message, MAX_ERROR_MESSAGE, _T("Failed to build error message")); 
     } 

     *errorMessage = message; 

     va_end(va); 
    } 
} 

Chúng tôi đã sử dụng này để tạo Chứng chỉ SSL khi khởi động, điều này rất tốt khi bạn chỉ muốn kiểm tra mã hóa và không xác minh niềm tin/nhận dạng và chỉ mất khoảng 2-3 giây để tạo.

1

Mở rộng câu trả lời của Leon Breedt, để tạo chứng chỉ x509 trong bộ nhớ, bạn có thể sử dụng mã nguồn từ Keith Elder's SelfCert.

using (CryptContext ctx = new CryptContext()) 
{ 
    ctx.Open(); 

    var cert = ctx.CreateSelfSignedCertificate(
     new SelfSignedCertProperties 
     { 
      IsPrivateKeyExportable = true, 
      KeyBitLength = 4096, 
      Name = new X500DistinguishedName("cn=InMemoryTestCert"), 
      ValidFrom = DateTime.Today.AddDays(-1), 
      ValidTo = DateTime.Today.AddYears(5), 
     }); 

    var creds = new ServiceCredentials(); 
    creds.UserNameAuthentication.CustomUserNamePasswordValidator = new MyUserNamePasswordValidator(); 
    creds.ServiceCertificate.Certificate = cert; 
    serviceHost.Description.Behaviors.Add(creds); 
} 
+0

Nơi đệm jQuery của bạn! –

+0

Chúng tôi thực sự sử dụng giải pháp của Keith Elder ngay từ bây giờ, duy trì các DLL C++ riêng biệt là quá nhiều nỗi đau. –

+0

@Leon Breedt @Paul Stovell: Tôi nghĩ cả hai đều có nghĩa là Keith Brown, đúng không. –

Các vấn đề liên quan