/*
*
* Copyright (c) 2013 - 2017 Lijun Liao
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3
* as published by the Free Software Foundation with the addition of the
* following permission added to Section 15 as permitted in Section 7(a):
*
* FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
* THE AUTHOR LIJUN LIAO. LIJUN LIAO DISCLAIMS THE WARRANTY OF NON INFRINGEMENT
* OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License.
*
* You can be released from the requirements of the license by purchasing
* a commercial license. Buying such a license is mandatory as soon as you
* develop commercial activities involving the XiPKI software without
* disclosing the source code of your own applications.
*
* For more information, please contact Lijun Liao at this
* address: lijun.liao@gmail.com
*/
package org.xipki.pki.scep.client;
import java.io.IOException;
import java.math.BigInteger;
import java.net.MalformedURLException;
import java.security.PrivateKey;
import java.security.cert.CRLException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509CRL;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.cms.ContentInfo;
import org.bouncycastle.asn1.cms.IssuerAndSerialNumber;
import org.bouncycastle.asn1.cms.SignedData;
import org.bouncycastle.asn1.pkcs.CertificationRequest;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cms.CMSAlgorithm;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.util.CollectionStore;
import org.bouncycastle.util.encoders.Base64;
import org.eclipse.jdt.annotation.NonNull;
import org.xipki.commons.common.util.ParamUtil;
import org.xipki.pki.scep.client.exception.OperationNotSupportedException;
import org.xipki.pki.scep.client.exception.ScepClientException;
import org.xipki.pki.scep.crypto.ScepHashAlgoType;
import org.xipki.pki.scep.exception.MessageDecodingException;
import org.xipki.pki.scep.exception.MessageEncodingException;
import org.xipki.pki.scep.message.AuthorityCertStore;
import org.xipki.pki.scep.message.CaCaps;
import org.xipki.pki.scep.message.DecodedNextCaMessage;
import org.xipki.pki.scep.message.DecodedPkiMessage;
import org.xipki.pki.scep.message.IssuerAndSubject;
import org.xipki.pki.scep.message.PkiMessage;
import org.xipki.pki.scep.transaction.CaCapability;
import org.xipki.pki.scep.transaction.MessageType;
import org.xipki.pki.scep.transaction.Operation;
import org.xipki.pki.scep.transaction.TransactionId;
import org.xipki.pki.scep.util.ScepConstants;
import org.xipki.pki.scep.util.ScepUtil;
/**
* @author Lijun Liao
* @since 2.0.0
*/
public abstract class Client {
public static final String REQ_CONTENT_TYPE = "application/octet-stream";
// 5 minutes
public static final long DEFAULT_SIGNINGTIME_BIAS = 5L * 60 * 1000;
protected final CaIdentifier caId;
protected CaCaps caCaps;
private final CaCertValidator caCertValidator;
private long maxSigningTimeBiasInMs = DEFAULT_SIGNINGTIME_BIAS;
private AuthorityCertStore authorityCertStore;
private CollectionStore<X509CertificateHolder> responseSignerCerts;
private boolean httpGetOnly;
private boolean useInsecureAlgorithms;
public Client(final CaIdentifier caId, final CaCertValidator caCertValidator)
throws MalformedURLException {
this.caId = ParamUtil.requireNonNull("caId", caId);
this.caCertValidator = ParamUtil.requireNonNull("caCertValidator", caCertValidator);
}
protected abstract ScepHttpResponse httpPost(@NonNull final String url,
@NonNull final String requestContentType, @NonNull final byte[] request)
throws ScepClientException;
protected abstract ScepHttpResponse httpGet(@NonNull final String url)
throws ScepClientException;
public boolean isHttpGetOnly() {
return httpGetOnly;
}
public void setHttpGetOnly(final boolean httpGetOnly) {
this.httpGetOnly = httpGetOnly;
}
public boolean isUseInsecureAlgorithms() {
return useInsecureAlgorithms;
}
public void setUseInsecureAlgorithms(final boolean useInsecureAlgorithms) {
this.useInsecureAlgorithms = useInsecureAlgorithms;
}
public long getMaxSigningTimeBiasInMs() {
return maxSigningTimeBiasInMs;
}
/**
* Set the maximal signing time bias in milliseconds.
* @param maxSigningTimeBiasInMs zero or negative value deactivates the message time check
*/
public void setMaxSigningTimeBiasInMs(final long maxSigningTimeBiasInMs) {
this.maxSigningTimeBiasInMs = maxSigningTimeBiasInMs;
}
private ScepHttpResponse httpSend(final Operation operation, final ContentInfo pkiMessage)
throws ScepClientException {
byte[] request = null;
if (pkiMessage != null) {
try {
request = pkiMessage.getEncoded();
} catch (IOException ex) {
throw new ScepClientException(ex);
}
}
if (Operation.GetCACaps == operation || Operation.GetCACert == operation
|| Operation.GetNextCACert == operation) {
String url = caId.buildGetUrl(operation, caId.getProfile());
return httpGet(url);
} else {
if (!httpGetOnly && caCaps.containsCapability(CaCapability.POSTPKIOperation)) {
String url = caId.buildPostUrl(operation);
return httpPost(url, REQ_CONTENT_TYPE, request);
} else {
String url = caId.buildGetUrl(operation,
(request == null) ? null : Base64.toBase64String(request));
return httpGet(url);
}
} // end if
}
private ScepHttpResponse httpSend(final Operation operation) throws ScepClientException {
return httpSend(operation, null);
}
public void init() throws ScepClientException {
refresh();
}
public void refresh() throws ScepClientException {
// getCACaps
ScepHttpResponse getCaCapsResp = httpSend(Operation.GetCACaps);
this.caCaps = CaCaps.getInstance(new String(getCaCapsResp.getContentBytes()));
// getCACert
ScepHttpResponse getCaCertResp = httpSend(Operation.GetCACert);
this.authorityCertStore = retrieveCaCertStore(getCaCertResp, caCertValidator);
X509CertificateHolder certHolder;
try {
certHolder = new X509CertificateHolder(
this.authorityCertStore.getSignatureCert().getEncoded());
} catch (CertificateEncodingException ex) {
throw new ScepClientException(ex);
} catch (IOException ex) {
throw new ScepClientException(ex);
}
this.responseSignerCerts = new CollectionStore<X509CertificateHolder>(
Arrays.asList(certHolder));
}
public CaCaps getCaCaps() throws ScepClientException {
initIfNotInited();
return caCaps;
}
public CaIdentifier getCaId() throws ScepClientException {
initIfNotInited();
return caId;
}
public CaCertValidator getCaCertValidator() throws ScepClientException {
initIfNotInited();
return caCertValidator;
}
public AuthorityCertStore getAuthorityCertStore() throws ScepClientException {
initIfNotInited();
return authorityCertStore;
}
public X509CRL scepGetCrl(final PrivateKey identityKey, final X509Certificate identityCert,
final X500Name issuer, final BigInteger serialNumber) throws ScepClientException {
ParamUtil.requireNonNull("identityKey", identityKey);
ParamUtil.requireNonNull("identityCert", identityCert);
ParamUtil.requireNonNull("issuer", issuer);
ParamUtil.requireNonNull("serialNumber", serialNumber);
initIfNotInited();
PkiMessage pkiMessage = new PkiMessage(TransactionId.randomTransactionId(),
MessageType.GetCRL);
IssuerAndSerialNumber isn = new IssuerAndSerialNumber(issuer, serialNumber);
pkiMessage.setMessageData(isn);
ContentInfo request = encryptThenSign(pkiMessage, identityKey, identityCert);
ScepHttpResponse httpResp = httpSend(Operation.PKIOperation, request);
CMSSignedData cmsSignedData = parsePkiMessage(httpResp.getContentBytes());
PkiMessage response = decode(cmsSignedData, identityKey, identityCert);
ContentInfo messageData = ContentInfo.getInstance(response.getMessageData());
try {
return ScepUtil.getCrlFromPkiMessage(SignedData.getInstance(messageData.getContent()));
} catch (CRLException ex) {
throw new ScepClientException(ex.getMessage(), ex);
}
}
public List<X509Certificate> scepGetCert(final PrivateKey identityKey,
final X509Certificate identityCert, final X500Name issuer,
final BigInteger serialNumber) throws ScepClientException {
ParamUtil.requireNonNull("identityKey", identityKey);
ParamUtil.requireNonNull("identityCert", identityCert);
ParamUtil.requireNonNull("issuer", issuer);
ParamUtil.requireNonNull("serialNumber", serialNumber);
initIfNotInited();
PkiMessage request = new PkiMessage(TransactionId.randomTransactionId(),
MessageType.GetCert);
IssuerAndSerialNumber isn = new IssuerAndSerialNumber(issuer, serialNumber);
request.setMessageData(isn);
ContentInfo envRequest = encryptThenSign(request, identityKey, identityCert);
ScepHttpResponse httpResp = httpSend(Operation.PKIOperation, envRequest);
CMSSignedData cmsSignedData = parsePkiMessage(httpResp.getContentBytes());
DecodedPkiMessage response = decode(cmsSignedData, identityKey, identityCert);
ContentInfo messageData = ContentInfo.getInstance(response.getMessageData());
try {
return ScepUtil.getCertsFromSignedData(
SignedData.getInstance(messageData.getContent()));
} catch (CertificateException ex) {
throw new ScepClientException(ex.getMessage(), ex);
}
}
public EnrolmentResponse scepCertPoll(final PrivateKey identityKey,
final X509Certificate identityCert, final CertificationRequest csr,
final X500Name issuer) throws ScepClientException {
ParamUtil.requireNonNull("csr", csr);
TransactionId tid;
try {
tid = TransactionId.sha1TransactionId(
csr.getCertificationRequestInfo().getSubjectPublicKeyInfo());
} catch (InvalidKeySpecException ex) {
throw new ScepClientException(ex.getMessage(), ex);
}
return scepCertPoll(identityKey, identityCert, tid, issuer,
csr.getCertificationRequestInfo().getSubject());
}
public EnrolmentResponse scepCertPoll(final PrivateKey identityKey,
final X509Certificate identityCert, final TransactionId transactionId,
final X500Name issuer, final X500Name subject) throws ScepClientException {
ParamUtil.requireNonNull("identityKey", identityKey);
ParamUtil.requireNonNull("identityCert", identityCert);
ParamUtil.requireNonNull("issuer", issuer);
ParamUtil.requireNonNull("transactionId", transactionId);
initIfNotInited();
PkiMessage pkiMessage = new PkiMessage(transactionId, MessageType.CertPoll);
IssuerAndSubject is = new IssuerAndSubject(issuer, subject);
pkiMessage.setMessageData(is);
ContentInfo envRequest = encryptThenSign(pkiMessage, identityKey, identityCert);
ScepHttpResponse httpResp = httpSend(Operation.PKIOperation, envRequest);
CMSSignedData cmsSignedData = parsePkiMessage(httpResp.getContentBytes());
DecodedPkiMessage response = decode(cmsSignedData, identityKey, identityCert);
assertSameNonce(pkiMessage, response);
return new EnrolmentResponse(response);
}
public EnrolmentResponse scepEnrol(final CertificationRequest csr, final PrivateKey identityKey,
final X509Certificate identityCert) throws ScepClientException {
ParamUtil.requireNonNull("csr", csr);
ParamUtil.requireNonNull("identityKey", identityKey);
ParamUtil.requireNonNull("identityCert", identityCert);
initIfNotInited();
// draft-nourse-scep
if (!isGutmannScep()) {
return scepPkcsReq(csr, identityKey, identityCert);
}
// draft-gutmann-scep
if (!ScepUtil.isSelfSigned(identityCert)) {
X509Certificate caCert = authorityCertStore.getCaCert();
if (identityCert.getIssuerX500Principal().equals(caCert.getSubjectX500Principal())) {
if (caCaps.containsCapability(CaCapability.Renewal)) {
return scepRenewalReq(csr, identityKey, identityCert);
}
} else {
if (caCaps.containsCapability(CaCapability.Update)) {
return scepUpdateReq(csr, identityKey, identityCert);
}
}
} // end if
return scepPkcsReq(csr, identityKey, identityCert);
}
public EnrolmentResponse scepPkcsReq(final CertificationRequest csr,
final PrivateKey identityKey, final X509Certificate identityCert)
throws ScepClientException {
ParamUtil.requireNonNull("csr", csr);
ParamUtil.requireNonNull("identityKey", identityKey);
ParamUtil.requireNonNull("identityCert", identityCert);
initIfNotInited();
boolean selfSigned = ScepUtil.isSelfSigned(identityCert);
if (!selfSigned) {
throw new IllegalArgumentException("identityCert is not self-signed");
}
return doEnrol(MessageType.PKCSReq, csr, identityKey, identityCert);
}
public EnrolmentResponse scepRenewalReq(final CertificationRequest csr,
final PrivateKey identityKey, final X509Certificate identityCert)
throws ScepClientException {
initIfNotInited();
if (!caCaps.containsCapability(CaCapability.Renewal)) {
throw new OperationNotSupportedException(
"unsupported messageType '" + MessageType.RenewalReq + "'");
}
boolean selfSigned = ScepUtil.isSelfSigned(identityCert);
if (selfSigned) {
throw new IllegalArgumentException("identityCert must not be self-signed");
}
return doEnrol(MessageType.RenewalReq, csr, identityKey, identityCert);
}
public EnrolmentResponse scepUpdateReq(final CertificationRequest csr,
final PrivateKey identityKey, final X509Certificate identityCert)
throws ScepClientException {
initIfNotInited();
if (!caCaps.containsCapability(CaCapability.Update)) {
throw new OperationNotSupportedException(
"unsupported messageType '" + MessageType.UpdateReq + "'");
}
boolean selfSigned = ScepUtil.isSelfSigned(identityCert);
if (selfSigned) {
throw new IllegalArgumentException("identityCert must not be self-signed");
}
return doEnrol(MessageType.UpdateReq, csr, identityKey, identityCert);
}
private EnrolmentResponse doEnrol(final MessageType messageType, final CertificationRequest csr,
final PrivateKey identityKey, final X509Certificate identityCert)
throws ScepClientException {
TransactionId tid;
try {
tid = TransactionId.sha1TransactionId(
csr.getCertificationRequestInfo().getSubjectPublicKeyInfo());
} catch (InvalidKeySpecException ex) {
throw new ScepClientException(ex.getMessage(), ex);
}
PkiMessage pkiMessage = new PkiMessage(tid, messageType);
pkiMessage.setMessageData(csr);
ContentInfo envRequest = encryptThenSign(pkiMessage, identityKey, identityCert);
ScepHttpResponse httpResp = httpSend(Operation.PKIOperation, envRequest);
CMSSignedData cmsSignedData = parsePkiMessage(httpResp.getContentBytes());
DecodedPkiMessage response = decode(cmsSignedData, identityKey, identityCert);
assertSameNonce(pkiMessage, response);
return new EnrolmentResponse(response);
}
public AuthorityCertStore scepNextCaCert() throws ScepClientException {
initIfNotInited();
if (!this.caCaps.containsCapability(CaCapability.GetNextCACert)) {
throw new OperationNotSupportedException(
"unsupported operation '" + Operation.GetNextCACert.getCode() + "'");
}
ScepHttpResponse resp = httpSend(Operation.GetNextCACert);
return retrieveNextCaAuthorityCertStore(resp);
}
private ContentInfo encryptThenSign(final PkiMessage request, final PrivateKey identityKey,
final X509Certificate identityCert) throws ScepClientException {
ScepHashAlgoType hashAlgo = caCaps.getMostSecureHashAlgo();
if (hashAlgo == ScepHashAlgoType.MD5 && !useInsecureAlgorithms) {
throw new ScepClientException(
"Scep server supports only MD5 but it not permitted in client");
}
String signatureAlgorithm = ScepUtil.getSignatureAlgorithm(identityKey, hashAlgo);
ASN1ObjectIdentifier encAlgId;
if (caCaps.containsCapability(CaCapability.AES)) {
encAlgId = CMSAlgorithm.AES128_CBC;
} else if (caCaps.containsCapability(CaCapability.DES3)) {
encAlgId = CMSAlgorithm.DES_EDE3_CBC;
} else if (useInsecureAlgorithms) {
encAlgId = CMSAlgorithm.DES_CBC;
} else { // no support of DES
throw new ScepClientException("DES will not be supported by this client");
}
try {
return request.encode(identityKey, signatureAlgorithm, identityCert,
new X509Certificate[]{identityCert}, authorityCertStore.getEncryptionCert(),
encAlgId);
} catch (MessageEncodingException ex) {
throw new ScepClientException(ex);
}
}
public void destroy() {
}
private AuthorityCertStore retrieveNextCaAuthorityCertStore(final ScepHttpResponse httpResp)
throws ScepClientException {
String ct = httpResp.getContentType();
if (!ScepConstants.CT_X509_NEXT_CA_CERT.equalsIgnoreCase(ct)) {
throw new ScepClientException("invalid Content-Type '" + ct + "'");
}
CMSSignedData cmsSignedData;
try {
cmsSignedData = new CMSSignedData(httpResp.getContentBytes());
} catch (CMSException ex) {
throw new ScepClientException("invalid SignedData message: " + ex.getMessage(), ex);
} catch (IllegalArgumentException ex) {
throw new ScepClientException("invalid SignedData message: " + ex.getMessage(), ex);
}
DecodedNextCaMessage resp;
try {
resp = DecodedNextCaMessage.decode(cmsSignedData, responseSignerCerts);
} catch (MessageDecodingException ex) {
throw new ScepClientException("could not decode response: " + ex.getMessage(), ex);
}
if (resp.getFailureMessage() != null) {
throw new ScepClientException("Error: " + resp.getFailureMessage());
}
Boolean bo = resp.isSignatureValid();
if (bo != null && !bo.booleanValue()) {
throw new ScepClientException("Signature is invalid");
}
Date signingTime = resp.getSigningTime();
long maxSigningTimeBias = getMaxSigningTimeBiasInMs();
if (maxSigningTimeBias > 0) {
if (signingTime == null) {
throw new ScepClientException("CMS signingTime attribute is not present");
}
long now = System.currentTimeMillis();
long diff = now - signingTime.getTime();
if (diff < 0) {
diff = -1 * diff;
}
if (diff > maxSigningTimeBias) {
throw new ScepClientException("CMS signingTime is out of permitted period");
}
}
if (!resp.getSignatureCert().equals(authorityCertStore.getSignatureCert())) {
throw new ScepClientException("the signature certificate must not be trusted");
}
return resp.getAuthorityCertStore();
} // method retrieveNextCaAuthorityCertStore
private void initIfNotInited() throws ScepClientException {
if (caCaps == null) {
init();
}
}
private DecodedPkiMessage decode(final CMSSignedData pkiMessage, final PrivateKey recipientKey,
final X509Certificate recipientCert) throws ScepClientException {
DecodedPkiMessage resp;
try {
resp = DecodedPkiMessage.decode(pkiMessage, recipientKey, recipientCert,
responseSignerCerts);
} catch (MessageDecodingException ex) {
throw new ScepClientException(ex);
}
if (resp.getFailureMessage() != null) {
throw new ScepClientException("Error: " + resp.getFailureMessage());
}
Boolean bo = resp.isSignatureValid();
if (bo != null && !bo.booleanValue()) {
throw new ScepClientException("Signature is invalid");
}
bo = resp.isDecryptionSuccessful();
if (bo != null && !bo.booleanValue()) {
throw new ScepClientException("Decryption failed");
}
Date signingTime = resp.getSigningTime();
long maxSigningTimeBias = getMaxSigningTimeBiasInMs();
if (maxSigningTimeBias > 0) {
if (signingTime == null) {
throw new ScepClientException("CMS signingTime attribute is not present");
}
long now = System.currentTimeMillis();
long diff = now - signingTime.getTime();
if (diff < 0) {
diff = -1 * diff;
}
if (diff > maxSigningTimeBias) {
throw new ScepClientException("CMS signingTime is out of permitted period");
}
}
if (!resp.getSignatureCert().equals(authorityCertStore.getSignatureCert())) {
throw new ScepClientException("the signature certificate must not be trusted");
}
return resp;
} // method decode
private boolean isGutmannScep() {
return caCaps.containsCapability(CaCapability.AES)
|| caCaps.containsCapability(CaCapability.Update);
}
private static X509Certificate parseCert(final byte[] certBytes) throws ScepClientException {
try {
return ScepUtil.parseCert(certBytes);
} catch (CertificateException ex) {
throw new ScepClientException(ex);
}
}
private static CMSSignedData parsePkiMessage(final byte[] messageBytes)
throws ScepClientException {
try {
return new CMSSignedData(messageBytes);
} catch (CMSException ex) {
throw new ScepClientException(ex);
}
}
private static AuthorityCertStore retrieveCaCertStore(final ScepHttpResponse resp,
final CaCertValidator caValidator) throws ScepClientException {
String ct = resp.getContentType();
X509Certificate caCert = null;
List<X509Certificate> raCerts = new LinkedList<X509Certificate>();
if (ScepConstants.CT_X509_CA_CERT.equalsIgnoreCase(ct)) {
caCert = parseCert(resp.getContentBytes());
} else if (ScepConstants.CT_X509_CA_RA_CERT.equalsIgnoreCase(ct)) {
ContentInfo contentInfo = ContentInfo.getInstance(resp.getContentBytes());
SignedData signedData;
try {
signedData = SignedData.getInstance(contentInfo.getContent());
} catch (IllegalArgumentException ex) {
throw new ScepClientException("invalid SignedData message: " + ex.getMessage(), ex);
}
List<X509Certificate> certs;
try {
certs = ScepUtil.getCertsFromSignedData(signedData);
} catch (CertificateException ex) {
throw new ScepClientException(ex.getMessage(), ex);
}
final int n = certs.size();
if (n < 2) {
throw new ScepClientException(
"at least 2 certificates are expected, but only " + n + " is available");
}
for (int i = 0; i < n; i++) {
X509Certificate cert = certs.get(i);
if (cert.getBasicConstraints() > -1) {
if (caCert != null) {
throw new ScepClientException(
"multiple CA certificates is returned, but exactly 1 is expected");
}
caCert = cert;
} else {
raCerts.add(cert);
}
}
if (caCert == null) {
throw new ScepClientException("no CA certificate is returned");
}
} else {
throw new ScepClientException("invalid Content-Type '" + ct + "'");
}
if (!caValidator.isTrusted(caCert)) {
throw new ScepClientException(
"CA certificate '" + caCert.getSubjectX500Principal() + "' is not trusted");
}
if (raCerts.isEmpty()) {
return AuthorityCertStore.getInstance(caCert);
} else {
AuthorityCertStore cs = AuthorityCertStore.getInstance(
caCert, raCerts.toArray(new X509Certificate[0]));
X509Certificate raEncCert = cs.getEncryptionCert();
X509Certificate raSignCert = cs.getSignatureCert();
try {
if (!ScepUtil.issues(caCert, raEncCert)) {
throw new ScepClientException("RA certificate '"
+ raEncCert.getSubjectX500Principal() + " is not issued by the CA");
}
if (raSignCert != raEncCert && ScepUtil.issues(caCert, raSignCert)) {
throw new ScepClientException("RA certificate '"
+ raSignCert.getSubjectX500Principal() + " is not issued by the CA");
}
} catch (CertificateException ex) {
throw new ScepClientException("invalid certificate: " + ex.getMessage(), ex);
}
return cs;
}
} // method retrieveCaCertStore
private static void assertSameNonce(final PkiMessage request, final PkiMessage response)
throws ScepClientException {
if (request.getSenderNonce().equals(response.getRecipientNonce())) {
throw new ScepClientException(
"SenderNonce of the request and RecipientNonce of response are not the same");
}
}
}