|
16 | 16 | using Newtonsoft.Json; |
17 | 17 |
|
18 | 18 | using Org.BouncyCastle.Asn1.X509; |
| 19 | +using Org.BouncyCastle.Pqc.Crypto.Falcon; |
19 | 20 |
|
20 | 21 | using System.Collections.Concurrent; |
21 | 22 | using System.Runtime.InteropServices; |
@@ -300,33 +301,56 @@ public async Task<EnrollmentResult> Enroll(string csr, string subject, Dictionar |
300 | 301 | _logger.LogWarning($"{CertCentralConstants.Config.INCLUDE_CLIENT_AUTH}: Ability to include client auth EKU in SSL certs is currently planned to cease in May 2026. Make sure any workflows that depend on this feature are updated before then to avoid interruptions."); |
301 | 302 | } |
302 | 303 |
|
303 | | - // Current gateway core leaves it up to the integration to determine if it is a renewal or a reissue |
| 304 | + bool dupe = false; |
| 305 | + // Current gateway core leaves it up to the integration to determine if it is a renewal, a reissue, or a duplicate |
304 | 306 | if (enrollmentType == EnrollmentType.RenewOrReissue) |
305 | 307 | { |
306 | | - //// Determine if we're going to do a renew or a reissue. |
| 308 | + //// Determine if we're going to do a renew, reissue, or duplicate. |
307 | 309 | priorCertSnString = productInfo.ProductParameters["PriorCertSN"]; |
308 | 310 | _logger.LogTrace($"Attempting to retrieve the certificate with serial number {priorCertSnString}."); |
309 | | - var reqId = _certificateDataReader.GetRequestIDBySerialNumber(priorCertSnString).Result; |
310 | | - if (string.IsNullOrEmpty(reqId)) |
| 311 | + priorCertReqID = await _certificateDataReader.GetRequestIDBySerialNumber(priorCertSnString); |
| 312 | + if (string.IsNullOrEmpty(priorCertReqID)) |
311 | 313 | { |
312 | 314 | throw new Exception($"No certificate with serial number '{priorCertSnString}' could be found."); |
313 | 315 | } |
314 | | - var expDate = _certificateDataReader.GetExpirationDateByRequestId(reqId); |
315 | 316 |
|
316 | | - var renewCutoff = DateTime.Now.AddDays(renewWindow * -1); |
317 | | - |
318 | | - if (expDate > renewCutoff) |
| 317 | + if (productInfo.ProductParameters.ContainsKey(CertCentralConstants.Config.DUPLICATE)) |
| 318 | + { |
| 319 | + string dupStr = productInfo.ProductParameters[CertCentralConstants.Config.DUPLICATE].ToString(); |
| 320 | + if (!bool.TryParse(dupStr, out dupe)) |
| 321 | + { |
| 322 | + _logger.LogError($"Could not parse 'Duplicate' field as true or false. Check configuration. Value: {dupStr}"); |
| 323 | + throw new Exception($"Could not parse 'Duplicate' field as true or false. Check configuration"); |
| 324 | + } |
| 325 | + } |
| 326 | + if (!dupe) |
319 | 327 | { |
320 | | - _logger.LogTrace($"Certificate with serial number {priorCertSnString} is within renewal window"); |
321 | | - enrollmentType = EnrollmentType.Renew; |
| 328 | + var expDate = _certificateDataReader.GetExpirationDateByRequestId(priorCertReqID); |
| 329 | + |
| 330 | + var renewCutoff = DateTime.Now.AddDays(renewWindow * -1); |
| 331 | + |
| 332 | + if (expDate > renewCutoff) |
| 333 | + { |
| 334 | + _logger.LogTrace($"Certificate with serial number {priorCertSnString} is within renewal window"); |
| 335 | + enrollmentType = EnrollmentType.Renew; |
| 336 | + } |
| 337 | + else |
| 338 | + { |
| 339 | + _logger.LogTrace($"Certificate with serial number {priorCertSnString} is not within renewal window. Reissuing..."); |
| 340 | + enrollmentType = EnrollmentType.Reissue; |
| 341 | + } |
322 | 342 | } |
323 | 343 | else |
324 | 344 | { |
325 | | - _logger.LogTrace($"Certificate with serial number {priorCertSnString} is not within renewal window. Reissuing..."); |
326 | | - enrollmentType = EnrollmentType.Reissue; |
| 345 | + _logger.LogTrace($"'Duplicate' flag set, performing duplication"); |
327 | 346 | } |
328 | 347 | } |
329 | 348 |
|
| 349 | + if (dupe) |
| 350 | + { |
| 351 | + return await Duplicate(client, productInfo, priorCertReqID, commonName, csr, dnsNames, signatureHash, caCertId); |
| 352 | + } |
| 353 | + |
330 | 354 | // Check if the order has more validity in it (multi-year cert). If so, do a reissue instead of a renew |
331 | 355 | if (enrollmentType == EnrollmentType.Renew) |
332 | 356 | { |
@@ -1459,6 +1483,46 @@ private async Task<EnrollmentResult> Reissue(CertCentralClient client, Enrollmen |
1459 | 1483 | return await ExtractEnrollmentResult(client, client.ReissueCertificate(reissueRequest), commonName); |
1460 | 1484 | } |
1461 | 1485 |
|
| 1486 | + /// <summary> |
| 1487 | + /// Duplicates a certificate. |
| 1488 | + /// </summary> |
| 1489 | + /// <param name="client">The client used to contact DigiCert.</param> |
| 1490 | + /// <param name="request">The <see cref="OrderRequest"/>.</param> |
| 1491 | + /// <param name="enrollmentProductInfo">Information about the DigiCert product this certificate uses.</param> |
| 1492 | + /// <returns></returns> |
| 1493 | + private async Task<EnrollmentResult> Duplicate(CertCentralClient client, EnrollmentProductInfo enrollmentProductInfo, string caRequestId, string commonName, string csr, List<string> dnsNames, string signatureHash, string caCertId) |
| 1494 | + { |
| 1495 | + CheckProductExistence(enrollmentProductInfo.ProductID); |
| 1496 | + |
| 1497 | + // Get order ID |
| 1498 | + _logger.LogTrace("Attempting to parse the order ID from the AnyGateway certificate."); |
| 1499 | + uint orderId = 0; |
| 1500 | + try |
| 1501 | + { |
| 1502 | + orderId = uint.Parse(caRequestId.Split('-').First()); |
| 1503 | + } |
| 1504 | + catch (Exception e) |
| 1505 | + { |
| 1506 | + throw new Exception($"There was an error parsing the order ID from the certificate: {e.Message}", e); |
| 1507 | + } |
| 1508 | + |
| 1509 | + // Duplicate certificate. |
| 1510 | + DuplicateRequest duplicateRequest = new DuplicateRequest(orderId) |
| 1511 | + { |
| 1512 | + Certificate = new CertificateDuplicateRequest |
| 1513 | + { |
| 1514 | + CommonName = commonName, |
| 1515 | + CSR = csr, |
| 1516 | + DnsNames = dnsNames, |
| 1517 | + SignatureHash = signatureHash, |
| 1518 | + CACertID = caCertId |
| 1519 | + } |
| 1520 | + }; |
| 1521 | + |
| 1522 | + _logger.LogTrace("Attempting to duplicate certificate."); |
| 1523 | + return await ExtractEnrollmentResult(client, client.DuplicateCertificate(duplicateRequest), commonName); |
| 1524 | + } |
| 1525 | + |
1462 | 1526 | /// <summary> |
1463 | 1527 | /// Verify that the given product ID is valid |
1464 | 1528 | /// </summary> |
|
0 commit comments