blob: ac082fa4435a78e8e273dd11172de2222433d347 [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
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* 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.preauth.pkinit;
import org.apache.kerby.asn1.type.Asn1ObjectIdentifier;
import org.apache.kerby.cms.type.CertificateSet;
import org.apache.kerby.cms.type.DigestAlgorithmIdentifiers;
import org.apache.kerby.cms.type.EncapsulatedContentInfo;
import org.apache.kerby.cms.type.RevocationInfoChoices;
import org.apache.kerby.cms.type.SignedContentInfo;
import org.apache.kerby.cms.type.SignedData;
import org.apache.kerby.cms.type.SignerInfos;
import org.apache.kerby.kerberos.kerb.KrbCodec;
import org.apache.kerby.kerberos.kerb.KrbErrorCode;
import org.apache.kerby.kerberos.kerb.KrbException;
import org.apache.kerby.kerberos.kerb.type.base.PrincipalName;
import org.apache.kerby.x509.type.Certificate;
import org.apache.kerby.x509.type.DhParameter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.interfaces.DHPublicKey;
import javax.crypto.spec.DHParameterSpec;
import javax.crypto.spec.DHPublicKeySpec;
import java.io.IOException;
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.cert.CertPathValidatorException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.util.ArrayList;
import java.util.List;
public class PkinitCrypto {
private static final Logger LOG = LoggerFactory.getLogger(PkinitCrypto.class);
/**
* Verify CMS Signed Data
* @param cmsMsgType The CMS message type
* @param signedData The signed data
*/
public static void verifyCMSSignedData(CMSMessageType cmsMsgType, SignedData signedData)
throws KrbException {
String oid = pkinitType2OID(cmsMsgType);
if (oid == null) {
throw new KrbException("Can't get the right oid ");
}
Asn1ObjectIdentifier etype = signedData.getEncapContentInfo().getContentType();
if (oid.equals(etype.getValue())) {
LOG.info("CMS Verification successful");
} else {
LOG.error("Wrong oid in eContentType");
throw new KrbException(KrbErrorCode.KDC_ERR_PREAUTH_FAILED, "Wrong oid in eContentType");
}
}
/**
* Check whether signed of data, true if the SignerInfos are not null
* @param signedData The signed data
* @return boolean
*/
public static boolean isSigned(SignedData signedData) {
/* Not actually signed; anonymous case */
if (signedData.getSignerInfos().getElements().size() == 0) {
return false;
} else {
return true;
}
}
/**
* Change the CMS message type to oid
* @param cmsMsgType The CMS message type
* @return oid
*/
public static String pkinitType2OID(CMSMessageType cmsMsgType) {
switch (cmsMsgType) {
case UNKNOWN:
return null;
case CMS_SIGN_CLIENT:
return PkinitPlgCryptoContext.getIdPkinitAuthDataOID();
case CMS_SIGN_SERVER:
return PkinitPlgCryptoContext.getIdPkinitDHKeyDataOID();
case CMS_ENVEL_SERVER:
return PkinitPlgCryptoContext.getIdPkinitRkeyDataOID();
default:
return null;
}
}
/**
* KDC check the key parameter
* @param pluginOpts The PluginOpts
* @param cryptoctx The PkinitPlgCryptoContext
* @param dhParameter The DhParameter
*/
public static void serverCheckDH(PluginOpts pluginOpts, PkinitPlgCryptoContext cryptoctx,
DhParameter dhParameter) throws KrbException {
/* KDC SHOULD check to see if the key parameters satisfy its policy */
int dhPrimeBits = dhParameter.getP().bitLength();
if (dhPrimeBits < pluginOpts.dhMinBits) {
String errMsg = "client sent dh params with " + dhPrimeBits
+ "bits, we require " + pluginOpts.dhMinBits;
LOG.error(errMsg);
throw new KrbException(KrbErrorCode.KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED, errMsg);
}
checkDHWellknown(cryptoctx, dhParameter, dhPrimeBits);
}
/**
* Check DH wellknown
* @param cryptoctx The PkinitPlgCryptoContext
* @param dhParameter The DhParameter
* @param dhPrimeBits The dh prime bits
* @return boolean
*/
public static boolean checkDHWellknown(PkinitPlgCryptoContext cryptoctx,
DhParameter dhParameter, int dhPrimeBits) throws KrbException {
boolean valid = false;
switch (dhPrimeBits) {
case 1024:
/* Oakley MODP group 2 */
case 2048:
/* Oakley MODP group 14 */
case 4096:
/* Oakley MODP group 16 */
valid = pkinitCheckDhParams(cryptoctx.createDHParameterSpec(dhPrimeBits), dhParameter);
break;
default:
break;
}
return valid;
}
/**
* Check parameters against a well-known DH group
*
* @param dh1 The DHParameterSpec
* @param dh2 The DhParameter
*/
public static boolean pkinitCheckDhParams(DHParameterSpec dh1, DhParameter dh2) {
if (!dh1.getP().equals(dh2.getP())) {
LOG.error("p is not well-known group dhparameter");
return false;
}
if (!dh1.getG().equals(dh2.getG())) {
LOG.error("bad g dhparameter");
return false;
}
LOG.info("Good dhparams", dh1.getP().bitLength());
return true;
}
/**
* Create DH public key
*
* @param p The prime modulus
* @param g The base generator
* @param y The public value
* @return DHPublicKey
*/
public static DHPublicKey createDHPublicKey(BigInteger p, BigInteger g, BigInteger y) {
DHPublicKeySpec dhPublicKeySpec = new DHPublicKeySpec(y, p, g);
KeyFactory keyFactory = null;
try {
keyFactory = KeyFactory.getInstance("DH");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
DHPublicKey dhPublicKey = null;
try {
dhPublicKey = (DHPublicKey) keyFactory.generatePublic(dhPublicKeySpec);
} catch (InvalidKeySpecException e) {
e.printStackTrace();
}
return dhPublicKey;
}
/**
* The contentType field of the type ContentInfo
* is id-signedData (1.2.840.113549.1.7.2),
* and the content field is a SignedData.
* The eContentType field for the type SignedData is
* id-pkinit-authData (1.3.6.1.5.2.3.1), and the
* eContent field contains the DER encoding of the
* type AuthPack.
*
* @param data The data
* @param oid The oid for eContentType
* @param version The SignedData version
* @param digestAlgorithmIdentifiers The digest algorithmIdentifiers
* @param certificateSet The certificate set
* @param crls The revocation info choices
* @param signerInfos The signerInfos
* @return The encoded ContentInfo
*/
public static byte[] cmsSignedDataCreate(byte[] data, String oid, int version,
DigestAlgorithmIdentifiers digestAlgorithmIdentifiers,
CertificateSet certificateSet,
RevocationInfoChoices crls, SignerInfos signerInfos) throws KrbException {
SignedContentInfo contentInfo = new SignedContentInfo();
contentInfo.setContentType("1.2.840.113549.1.7.2");
SignedData signedData = new SignedData();
signedData.setVersion(version);
if (digestAlgorithmIdentifiers != null) {
signedData.setDigestAlgorithms(digestAlgorithmIdentifiers);
}
EncapsulatedContentInfo eContentInfo = new EncapsulatedContentInfo();
eContentInfo.setContentType(oid);
eContentInfo.setContent(data);
signedData.setEncapContentInfo(eContentInfo);
if (certificateSet != null) {
signedData.setCertificates(certificateSet);
}
if (crls != null) {
signedData.setCrls(crls);
}
if (signerInfos != null) {
signedData.setSignerInfos(signerInfos);
}
contentInfo.setSignedData(signedData);
return KrbCodec.encode(contentInfo);
}
public static X509Certificate[] createCertChain(PkinitPlgCryptoContext cryptoContext)
throws CertificateNotYetValidException, CertificateExpiredException {
LOG.info("Building certificate chain.");
// TODO Build client chain.
X509Certificate[] clientChain = new X509Certificate[3];
return clientChain;
}
public static boolean verifyKdcSan(String hostname, PrincipalName kdcPrincipal,
List<Certificate> certificates) throws KrbException {
if (hostname == null) {
LOG.info("No pkinit_kdc_hostname values found in config file");
} else {
LOG.info("pkinit_kdc_hostname values found in config file");
}
try {
List<PrincipalName> princs = cryptoRetrieveCertSans(certificates);
if (princs != null) {
for (PrincipalName princ : princs) {
LOG.info("PKINIT client found id-pkinit-san in KDC cert: " + princ.getName());
}
LOG.info("Checking pkinit sans.");
if (princs.contains(kdcPrincipal)) {
LOG.info("pkinit san match found");
return true;
} else {
LOG.info("no pkinit san match found");
return false;
}
} else {
return false;
}
} catch (KrbException e) {
String errMessage = "PKINIT client failed to decode SANs in KDC cert." + e;
LOG.error(errMessage);
throw new KrbException(KrbErrorCode.KDC_NAME_MISMATCH, errMessage);
}
}
public static List<PrincipalName> cryptoRetrieveCertSans(List<Certificate> certificates)
throws KrbException {
if (certificates.size() == 0) {
LOG.info("no certificate!");
return null;
}
return cryptoRetrieveX509Sans(certificates);
}
public static List<PrincipalName> cryptoRetrieveX509Sans(List<Certificate> certificates)
throws KrbException {
List<PrincipalName> principalNames = new ArrayList<>();
for (Certificate cert : certificates) {
LOG.info("Looking for SANs in cert: " + cert.getTBSCertificate().getSubject());
//TODO
}
return principalNames;
}
/**
* Validates a chain of {@link X509Certificate}s.
*
* @throws CertificateException
* @throws NoSuchAlgorithmException
* @throws InvalidAlgorithmParameterException
* @throws CertPathValidatorException
*/
public static void validateChain(List<Certificate> certificateList, Certificate anchor)
throws CertificateException, NoSuchAlgorithmException, NoSuchProviderException,
InvalidAlgorithmParameterException, CertPathValidatorException {
//TODO
// CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
// CertPath certPath = certificateFactory.generateCertPath(certificateList);
//
// CertPathValidator cpv = CertPathValidator.getInstance("PKIX");
//
// TrustAnchor trustAnchor = new TrustAnchor(anchor, null);
//
// PKIXParameters parameters = new PKIXParameters(Collections.singleton(trustAnchor));
// parameters.setRevocationEnabled(false);
//
// cpv.validate(certPath, parameters);
}
/**
* Create oid
*
* @param content The hex content
* @return The oid
*/
public static Asn1ObjectIdentifier createOid(String content) {
Asn1ObjectIdentifier oid = new Asn1ObjectIdentifier();
oid.useDER();
try {
oid.decode(Util.hex2bytes(content));
} catch (IOException e) {
e.printStackTrace();
}
return oid;
}
/**
* Change the X509Certificate to Certificate
*
* @param x509Certificate The X509Certificate
* @return The Certificate
*/
public static Certificate changeToCertificate(X509Certificate x509Certificate) {
Certificate certificate = new Certificate();
try {
certificate.decode(x509Certificate.getEncoded());
} catch (IOException e) {
e.printStackTrace();
} catch (CertificateEncodingException e) {
e.printStackTrace();
}
return certificate;
}
}