001/*
002 * Copyright 2021-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2021-2024 Ping Identity Corporation
007 *
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *    http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 */
020/*
021 * Copyright (C) 2021-2024 Ping Identity Corporation
022 *
023 * This program is free software; you can redistribute it and/or modify
024 * it under the terms of the GNU General Public License (GPLv2 only)
025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
026 * as published by the Free Software Foundation.
027 *
028 * This program is distributed in the hope that it will be useful,
029 * but WITHOUT ANY WARRANTY; without even the implied warranty of
030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
031 * GNU General Public License for more details.
032 *
033 * You should have received a copy of the GNU General Public License
034 * along with this program; if not, see <http://www.gnu.org/licenses>.
035 */
036package com.unboundid.ldap.sdk.unboundidds.extensions;
037
038
039
040import java.util.ArrayList;
041import java.util.List;
042
043import com.unboundid.asn1.ASN1Boolean;
044import com.unboundid.asn1.ASN1Element;
045import com.unboundid.asn1.ASN1OctetString;
046import com.unboundid.asn1.ASN1Sequence;
047import com.unboundid.ldap.sdk.Control;
048import com.unboundid.ldap.sdk.ExtendedRequest;
049import com.unboundid.ldap.sdk.ExtendedResult;
050import com.unboundid.ldap.sdk.LDAPConnection;
051import com.unboundid.ldap.sdk.LDAPException;
052import com.unboundid.ldap.sdk.ResultCode;
053import com.unboundid.util.Debug;
054import com.unboundid.util.NotMutable;
055import com.unboundid.util.NotNull;
056import com.unboundid.util.Nullable;
057import com.unboundid.util.StaticUtils;
058import com.unboundid.util.ThreadSafety;
059import com.unboundid.util.ThreadSafetyLevel;
060import com.unboundid.util.Validator;
061
062import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*;
063
064
065
066/**
067 * This class defines an extended request that may be used to request that a
068 * Ping Identity Directory Server instance (or related Ping Identity server
069 * product) replace its inter-server certificate.  The new certificate data may
070 * be contained in a key store file on the server filesystem or included in the
071 * extended request itself.
072 * <BR>
073 * <BLOCKQUOTE>
074 *   <B>NOTE:</B>  This class, and other classes within the
075 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
076 *   supported for use against Ping Identity, UnboundID, and
077 *   Nokia/Alcatel-Lucent 8661 server products.  These classes provide support
078 *   for proprietary functionality or for external specifications that are not
079 *   considered stable or mature enough to be guaranteed to work in an
080 *   interoperable way with other types of LDAP servers.
081 * </BLOCKQUOTE>
082 * <BR>
083 * This extended request has an OID of 1.3.6.1.4.1.30221.2.6.69 and a value with
084 * the following encoding:
085 * <PRE>
086 *   ReplaceInterServerCertificateValue ::= SEQUENCE {
087 *     keyStoreContent           CHOICE {
088 *       keyStoreFile              [0] KeyStoreFileSequence,
089 *       keyStoreData              [1] KeyStoreDataSequence,
090 *       certificateData            [2] CertificateDataSequence,
091 *       ... },
092 *    skipCertificateValidation  [16] BOOLEAN DEFAULT FALSE,
093 *    ... }
094 *
095 *   KeyStoreFileSequence ::= SEQUENCE {
096 *     path                    [8]  OCTET STRING,
097 *     keyStorePIN             [9]  OCTET STRING,
098 *     privateKeyPIN           [10] OCTET STRING OPTIONAL,
099 *     keyStoreType            [11] OCTET STRING OPTIONAL,
100 *     sourceCertificateAlias  [12] OCTET STRING OPTIONAL,
101 *     ... }
102 *
103 *   KeyStoreDataSequence ::= SEQUENCE {
104 *     keyStoreData            [13] OCTET STRING,
105 *     keyStorePIN             [9]  OCTET STRING,
106 *     privateKeyPIN           [10]  OCTET STRING OPTIONAL,
107 *     keyStoreType            [11] OCTET STRING OPTIONAL,
108 *     sourceCertificateAlias  [12] OCTET STRING OPTIONAL,
109 *     ... }
110 *
111 *   CertificateDataSequence ::= SEQUENCE {
112 *     certificateChain  [14] SEQUENCE SIZE (1..MAX) OF OCTET STRING,
113 *     privateKey        [15] OCTET STRING OPTIONAL,
114 *     ... }
115 * </PRE>
116 * <BR><BR>
117 *
118 * @see  ReplaceInterServerCertificateExtendedResult
119 */
120@NotMutable()
121@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
122public final class ReplaceInterServerCertificateExtendedRequest
123       extends ExtendedRequest
124{
125  /**
126   * The OID (1.3.6.1.4.1.30221.2.6.69) for the replace inter-server certificate
127   * extended request.
128   */
129  @NotNull public static final String REPLACE_INTER_SERVER_CERT_REQUEST_OID =
130       "1.3.6.1.4.1.30221.2.6.69";
131
132
133
134  /**
135   * The BER type for the request value element that indicates whether to
136   * skip validation for the new certificate chain.
137   */
138  private static final byte TYPE_SKIP_CERT_VALIDATION = (byte) 0x90;
139
140
141
142  /**
143   * The serial version UID for this serializable class.
144   */
145  private static final long serialVersionUID = 2244751901271649579L;
146
147
148
149  // Indicates whether to skip validation for the new certificate chain.
150  private final boolean skipCertificateValidation;
151
152  // The object providing information about how the server should obtain the new
153  // inter-server certificate data.
154  @NotNull private final ReplaceCertificateKeyStoreContent keyStoreContent;
155
156
157
158  /**
159   * Creates a new replace inter-server certificate extended request with the
160   * provided information.
161   *
162   * @param  keyStoreContent
163   *              An object with information about how the server should obtain
164   *              the new inter-server certificate data.  It must not be
165   *              {@code null}.
166   * @param  skipCertificateValidation
167   *              Indicates whether to skip validation for the new certificate
168   *              chain.
169   * @param  requestControls
170   *              The set of controls to include in the extended request.  It
171   *              may be {@code null} or empty if no request controls should be
172   *              included.
173   */
174  public ReplaceInterServerCertificateExtendedRequest(
175              @NotNull final ReplaceCertificateKeyStoreContent keyStoreContent,
176              final boolean skipCertificateValidation,
177              @Nullable final Control... requestControls)
178  {
179    super(REPLACE_INTER_SERVER_CERT_REQUEST_OID,
180         encodeValue(keyStoreContent, skipCertificateValidation),
181         requestControls);
182
183    this.keyStoreContent = keyStoreContent;
184    this.skipCertificateValidation = skipCertificateValidation;
185  }
186
187
188
189  /**
190   * Encodes the provided information into an ASN.1 octet string suitable for
191   * use as the encoded value for a replace inter-server certificate extended
192   * request.
193   *
194   * @param  keyStoreContent
195   *              An object with information about how the server should obtain
196   *              the new inter-server certificate data.  It must not be
197   *              {@code null}.
198   * @param  skipCertificateValidation
199   *              Indicates whether to skip validation for the new certificate
200   *              chain.
201   *
202   * @return  An ASN.1 octet string containing the encoded request value.
203   */
204  @NotNull()
205  private static ASN1OctetString encodeValue(
206              @NotNull final ReplaceCertificateKeyStoreContent keyStoreContent,
207              final boolean skipCertificateValidation)
208  {
209    Validator.ensureNotNullWithMessage(keyStoreContent,
210         "ReplaceInterServerCertificateExtendedRequest.keyStoreContent must " +
211              "not be null.");
212
213    final List<ASN1Element> elements = new ArrayList<>();
214    elements.add(keyStoreContent.encode());
215
216    if (skipCertificateValidation)
217    {
218      elements.add(new ASN1Boolean(TYPE_SKIP_CERT_VALIDATION, true));
219    }
220
221    return new ASN1OctetString(new ASN1Sequence(elements).encode());
222  }
223
224
225
226  /**
227   * Creates a new replace inter-server certificate extended request that is
228   * decoded from the provided generic extended request.
229   *
230   * @param  request  The generic extended request to be decoded as a replace
231   *                  inter-server certificate extended request.  It must not be
232   *                  {@code null}.
233   *
234   * @throws  LDAPException  If a problem occurs while attempting to decode the
235   *                         provided extended request as a replace inter-server
236   *                         certificate request.
237   */
238  public ReplaceInterServerCertificateExtendedRequest(
239              @NotNull final ExtendedRequest request)
240         throws LDAPException
241  {
242    super(request);
243
244    final ASN1OctetString value = request.getValue();
245    if (value == null)
246    {
247      throw new LDAPException(ResultCode.DECODING_ERROR,
248           ERR_REPLACE_INTER_SERVER_CERT_REQ_NO_VALUE.get());
249    }
250
251    try
252    {
253      final ASN1Element[] elements =
254           ASN1Sequence.decodeAsSequence(value.getValue()).elements();
255      keyStoreContent = ReplaceCertificateKeyStoreContent.decode(elements[0]);
256
257      boolean skipValidation = false;
258      for (int i=1; i < elements.length; i++)
259      {
260        switch (elements[i].getType())
261        {
262          case TYPE_SKIP_CERT_VALIDATION:
263            skipValidation = elements[i].decodeAsBoolean().booleanValue();
264            break;
265        }
266      }
267
268      skipCertificateValidation = skipValidation;
269    }
270    catch (final Exception e)
271    {
272      Debug.debugException(e);
273      throw new LDAPException(ResultCode.DECODING_ERROR,
274         ERR_REPLACE_INTER_SERVER_CERT_DECODE_ERROR.get(
275              StaticUtils.getExceptionMessage(e)),
276           e);
277    }
278  }
279
280
281
282  /**
283   * Retrieves an object with information about how the server should obtain the
284   * new inter-server certificate data.
285   *
286   * @return  An object with information about how the server should obtain the
287   *          new inter-server certificate data.
288   */
289  @NotNull()
290  public ReplaceCertificateKeyStoreContent getKeyStoreContent()
291  {
292    return keyStoreContent;
293  }
294
295
296
297  /**
298   * Indicates whether the server should skip validation processing for the
299   * new certificate chain.
300   *
301   * @return  {@code true} if the server should skip validation processing for
302   *          the new certificate chain, or {@code false} if not.
303   */
304  public boolean skipCertificateValidation()
305  {
306    return skipCertificateValidation;
307  }
308
309
310
311  /**
312   * {@inheritDoc}
313   */
314  @Override()
315  @NotNull()
316  public ReplaceInterServerCertificateExtendedResult process(
317              @NotNull final LDAPConnection connection, final int depth)
318         throws LDAPException
319  {
320    final ExtendedResult extendedResponse = super.process(connection, depth);
321    return new ReplaceInterServerCertificateExtendedResult(extendedResponse);
322  }
323
324
325
326  /**
327   * {@inheritDoc}
328   */
329  @Override()
330  @NotNull()
331  public String getExtendedRequestName()
332  {
333    return INFO_REPLACE_INTER_SERVER_CERT_REQUEST_NAME.get();
334  }
335
336
337
338  /**
339   * {@inheritDoc}
340   */
341  @Override()
342  public void toString(@NotNull final StringBuilder buffer)
343  {
344    buffer.append("ReplaceInterServerCertificateExtendedRequest(oid='");
345    buffer.append(getOID());
346    buffer.append("', keyStoreContent=");
347    keyStoreContent.toString(buffer);
348    buffer.append(", skipCertificateValidation=");
349    buffer.append(skipCertificateValidation);
350
351    final Control[] controls = getControls();
352    if (controls.length > 0)
353    {
354      buffer.append(", controls={");
355      for (int i=0; i < controls.length; i++)
356      {
357        if (i > 0)
358        {
359          buffer.append(", ");
360        }
361
362        buffer.append(controls[i]);
363      }
364      buffer.append('}');
365    }
366
367    buffer.append(')');
368  }
369}