blob: f00cf0f490365f15cbf3e63405ea336cab533515 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
package org.apache.kerby.kerberos.kerb.client.preauth.pkinit;
import org.apache.kerby.KOptions;
import org.apache.kerby.asn1.type.Asn1Integer;
import org.apache.kerby.asn1.type.Asn1ObjectIdentifier;
import org.apache.kerby.kerberos.kerb.KrbException;
import org.apache.kerby.kerberos.kerb.client.KrbContext;
import org.apache.kerby.kerberos.kerb.client.PkinitOption;
import org.apache.kerby.kerberos.kerb.client.preauth.AbstractPreauthPlugin;
import org.apache.kerby.kerberos.kerb.client.request.KdcRequest;
import org.apache.kerby.kerberos.kerb.common.CheckSumUtil;
import org.apache.kerby.kerberos.kerb.crypto.dh.DhClient;
import org.apache.kerby.kerberos.kerb.crypto.dh.DhGroup;
import org.apache.kerby.kerberos.kerb.preauth.PaFlag;
import org.apache.kerby.kerberos.kerb.preauth.PaFlags;
import org.apache.kerby.kerberos.kerb.preauth.PluginRequestContext;
import org.apache.kerby.kerberos.kerb.preauth.pkinit.PkinitCrypto;
import org.apache.kerby.kerberos.kerb.preauth.pkinit.PkinitIdenity;
import org.apache.kerby.kerberos.kerb.preauth.pkinit.PkinitPreauthMeta;
import org.apache.kerby.kerberos.kerb.type.KerberosTime;
import org.apache.kerby.kerberos.kerb.type.base.CheckSum;
import org.apache.kerby.kerberos.kerb.type.base.CheckSumType;
import org.apache.kerby.kerberos.kerb.type.base.EncryptionKey;
import org.apache.kerby.kerberos.kerb.type.base.EncryptionType;
import org.apache.kerby.kerberos.kerb.type.pa.PaData;
import org.apache.kerby.kerberos.kerb.type.pa.PaDataEntry;
import org.apache.kerby.kerberos.kerb.type.pa.PaDataType;
import org.apache.kerby.kerberos.kerb.type.pa.pkinit.AuthPack;
import org.apache.kerby.kerberos.kerb.type.pa.pkinit.PaPkAsReq;
import org.apache.kerby.kerberos.kerb.type.pa.pkinit.PkAuthenticator;
import org.apache.kerby.kerberos.kerb.type.pa.pkinit.TrustedCertifiers;
import org.apache.kerby.x509.type.AlgorithmIdentifier;
import org.apache.kerby.x509.type.DHParameter;
import org.apache.kerby.x509.type.SubjectPublicKeyInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.interfaces.DHPublicKey;
import javax.crypto.spec.DHParameterSpec;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
@SuppressWarnings("PMD.UnusedFormalParameter")
public class PkinitPreauth extends AbstractPreauthPlugin {
private static final Logger LOG = LoggerFactory.getLogger(PkinitPreauth.class);
private PkinitContext pkinitContext;
public PkinitPreauth() {
super(new PkinitPreauthMeta());
}
/**
* {@inheritDoc}
*/
@Override
public void init(KrbContext context) {
super.init(context);
this.pkinitContext = new PkinitContext();
}
/**
* {@inheritDoc}
*/
@Override
public PluginRequestContext initRequestContext(KdcRequest kdcRequest) {
PkinitRequestContext reqCtx = new PkinitRequestContext();
reqCtx.updateRequestOpts(pkinitContext.pluginOpts);
return reqCtx;
}
/**
* {@inheritDoc}
*/
@Override
public void setPreauthOptions(KdcRequest kdcRequest,
PluginRequestContext requestContext,
KOptions options) {
if (options.contains(PkinitOption.X509_IDENTITY)) {
pkinitContext.identityOpts.identity =
options.getStringOption(PkinitOption.X509_IDENTITY);
}
if (options.contains(PkinitOption.X509_ANCHORS)) {
String anchorsString = options.getStringOption(PkinitOption.X509_ANCHORS);
List<String> anchors;
if (anchorsString == null) {
anchors = kdcRequest.getContext().getConfig().getPkinitAnchors();
} else {
anchors = Arrays.asList(anchorsString);
}
pkinitContext.identityOpts.anchors.addAll(anchors);
}
if (options.contains(PkinitOption.USING_RSA)) {
pkinitContext.pluginOpts.usingRsa =
options.getBooleanOption(PkinitOption.USING_RSA, true);
}
}
/**
* {@inheritDoc}
*/
@Override
public void prepareQuestions(KdcRequest kdcRequest,
PluginRequestContext requestContext) {
PkinitRequestContext reqCtx = (PkinitRequestContext) requestContext;
if (!reqCtx.identityInitialized) {
PkinitIdenity.initialize(reqCtx.identityOpts, kdcRequest.getClientPrincipal());
reqCtx.identityInitialized = true;
}
// Might have questions asking for password to access the private key
}
/**
* {@inheritDoc}
*/
@Override
public void tryFirst(KdcRequest kdcRequest,
PluginRequestContext requestContext,
PaData outPadata) throws KrbException {
/* XXX PKINIT RFC says that nonce in PKAuthenticator doesn't have be the
* same as in the AS_REQ. However, if we pick a different nonce, then we
* need to remember that info when AS_REP is returned. Here choose to
* reuse the AS_REQ nonce.
*/
int nonce = kdcRequest.getChosenNonce();
// Get the current time
long now = System.currentTimeMillis();
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date(now));
int cusec = calendar.get(Calendar.SECOND);
KerberosTime ctime = new KerberosTime(now);
/* checksum of the encoded KDC-REQ-BODY */
CheckSum checkSum = null;
try {
checkSum = CheckSumUtil.makeCheckSum(CheckSumType.NIST_SHA,
kdcRequest.getKdcReq().getReqBody().encode());
} catch (KrbException e) {
throw new KrbException("Fail to encode checksum.", e);
}
PaPkAsReq paPkAsReq = makePaPkAsReq(kdcRequest, (PkinitRequestContext) requestContext,
cusec, ctime, nonce, checkSum);
outPadata.addElement(makeEntry(paPkAsReq));
}
/**
* {@inheritDoc}
*/
@Override
public boolean process(KdcRequest kdcRequest,
PluginRequestContext requestContext,
PaDataEntry inPadata,
PaData outPadata) throws KrbException {
PkinitRequestContext reqCtx = (PkinitRequestContext) requestContext;
if (inPadata == null) {
return false;
}
boolean processingRequest = false;
switch (inPadata.getPaDataType()) {
case PK_AS_REQ:
processingRequest = true;
break;
case PK_AS_REP:
break;
}
if (processingRequest) {
generateRequest(reqCtx, kdcRequest, outPadata);
} else {
EncryptionType encType = kdcRequest.getEncType();
processReply(kdcRequest, reqCtx, inPadata, encType);
}
return false;
}
private void generateRequest(PkinitRequestContext reqCtx, KdcRequest kdcRequest,
PaData outPadata) {
}
private PaPkAsReq makePaPkAsReq(KdcRequest kdcRequest,
PkinitRequestContext reqCtx,
int cusec, KerberosTime ctime, int nonce, CheckSum checkSum) {
LOG.info("Making the PK_AS_REQ.");
PaPkAsReq paPkAsReq = new PaPkAsReq();
AuthPack authPack = new AuthPack();
PkAuthenticator pkAuthen = new PkAuthenticator();
boolean usingRsa = pkinitContext.pluginOpts.usingRsa;
reqCtx.paType = PaDataType.PK_AS_REQ;
pkAuthen.setCusec(cusec);
pkAuthen.setCtime(ctime);
pkAuthen.setNonce(nonce);
pkAuthen.setPaChecksum(checkSum.getChecksum());
authPack.setPkAuthenticator(pkAuthen);
authPack.setsupportedCmsTypes(pkinitContext.pluginOpts.createSupportedCMSTypes());
if (!usingRsa) {
// DH case
LOG.info("DH key transport algorithm.");
String content = "0x06 07 2A 86 48 ce 3e 02 01";
Asn1ObjectIdentifier dhOid = PkinitCrypto.createOid(content);
AlgorithmIdentifier dhAlg = new AlgorithmIdentifier();
dhAlg.setAlgorithm(dhOid);
DhClient client = new DhClient();
DHPublicKey clientPubKey = null;
try {
clientPubKey = client.init(DhGroup.MODP_GROUP2);
} catch (Exception e) {
e.printStackTrace();
}
kdcRequest.setDhClient(client);
DHParameterSpec type = clientPubKey.getParams();
BigInteger q = type.getP().shiftRight(1);
DHParameter dhParameter = new DHParameter();
dhParameter.setP(type.getP());
dhParameter.setG(type.getG());
dhParameter.setQ(q);
dhAlg.setParameters(dhParameter);
SubjectPublicKeyInfo pubInfo = new SubjectPublicKeyInfo();
pubInfo.setAlgorithm(dhAlg);
Asn1Integer publickey = new Asn1Integer(clientPubKey.getY());
pubInfo.setSubjectPubKey(publickey.encode());
authPack.setClientPublicValue(pubInfo);
// DHNonce dhNonce = new DHNonce();
// authPack.setClientDhNonce(dhNonce);
} else {
LOG.info("RSA key transport algorithm");
// authPack.setClientPublicValue(null);
}
byte[] signedAuthPack = signAuthPack(authPack);
paPkAsReq.setSignedAuthPack(signedAuthPack);
TrustedCertifiers trustedCertifiers = pkinitContext.pluginOpts.createTrustedCertifiers();
paPkAsReq.setTrustedCertifiers(trustedCertifiers);
// byte[] kdcPkId = pkinitContext.pluginOpts.createIssuerAndSerial();
// paPkAsReq.setKdcPkId(kdcPkId);
return paPkAsReq;
}
private byte[] signAuthPack(AuthPack authPack) {
Asn1ObjectIdentifier oid = pkinitContext.cryptoctx.getIdPkinitAuthDataOID();
byte[] signedDataBytes = PkinitCrypto.cmsSignedDataCreate(
authPack.encode(), oid, 3, null, null, null, null);
return signedDataBytes;
}
private void processReply(KdcRequest kdcRequest,
PkinitRequestContext reqCtx,
PaDataEntry inPadata,
EncryptionType encType) {
EncryptionKey asKey = null;
// TODO
kdcRequest.setAsKey(asKey);
}
/**
* {@inheritDoc}
*/
@Override
public boolean tryAgain(KdcRequest kdcRequest,
PluginRequestContext requestContext,
PaDataType preauthType,
PaData errPadata,
PaData outPadata) {
PkinitRequestContext reqCtx = (PkinitRequestContext) requestContext;
if (reqCtx.paType != preauthType && errPadata == null) {
return false;
}
boolean doAgain = false;
for (PaDataEntry pde : errPadata.getElements()) {
// switch (pde.getPaDataType()) {
// TODO
// }
System.out.println(pde.getPaDataType());
}
if (doAgain) {
generateRequest(reqCtx, kdcRequest, outPadata);
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public PaFlags getFlags(PaDataType paType) {
PaFlags paFlags = new PaFlags(0);
paFlags.setFlag(PaFlag.PA_REAL);
return paFlags;
}
/**
* Make padata entry.
*
* @param paPkAsReq The PaPkAsReq
* @return PaDataEntry to be made.
*/
private PaDataEntry makeEntry(PaPkAsReq paPkAsReq) throws KrbException {
PaDataEntry paDataEntry = new PaDataEntry();
paDataEntry.setPaDataType(PaDataType.PK_AS_REQ);
paDataEntry.setPaDataValue(paPkAsReq.encode());
return paDataEntry;
}
}