All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.xipki.security.bc.XiXDHContentVerifierProvider Maven / Gradle / Ivy

/*
 *
 * Copyright (c) 2013 - 2019 Lijun Liao
 *
 * Licensed 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.xipki.security.bc;

import java.io.IOException;
import java.io.OutputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PublicKey;
import java.util.Arrays;

import javax.crypto.KeyAgreement;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.crmf.DhSigStatic;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.crypto.RuntimeCryptoException;
import org.bouncycastle.operator.ContentVerifier;
import org.bouncycastle.operator.ContentVerifierProvider;
import org.bouncycastle.operator.OperatorCreationException;
import org.xipki.security.DHSigStaticKeyCertPair;
import org.xipki.security.EdECConstants;
import org.xipki.security.HashAlgo;
import org.xipki.security.ObjectIdentifiers.Xipki;
import org.xipki.util.Args;

/**
 * {@link ContentVerifierProvider} for the algorithm X25519/X448 static HMAC (XiPKI own
 * algorithm, based on RFC 6955).
 *
 * @author Lijun Liao
 */

// CHECKSTYLE:SKIP
public class XiXDHContentVerifierProvider implements ContentVerifierProvider {

  //CHECKSTYLE:SKIP
  private static class XDHContentVerifier implements ContentVerifier {

    private class HmacOutputStream extends OutputStream {

      @Override
      public void write(int bb) throws IOException {
        hmac.update((byte) bb);
      }

      @Override
      public void write(byte[] bytes) throws IOException {
        hmac.update(bytes, 0, bytes.length);
      }

      @Override
      public void write(byte[] bytes, int off, int len) throws IOException {
        hmac.update(bytes, off, len);
      }

    } // class HmacOutputStream

    private final AlgorithmIdentifier algId;

    private final HmacOutputStream outputStream;

    private final Mac hmac;

    private final SecretKey macKey;

    private XDHContentVerifier(AlgorithmIdentifier algId, Mac hmac, SecretKey macKey) {
      this.algId = algId;
      this.hmac = hmac;
      this.macKey = macKey;
      this.outputStream = new HmacOutputStream();
    }

    @Override
    public AlgorithmIdentifier getAlgorithmIdentifier() {
      return algId;
    }

    @Override
    public OutputStream getOutputStream() {
      try {
        hmac.init(macKey);
      } catch (InvalidKeyException ex) {
        throw new RuntimeCryptoException("could not init MAC: " + ex.getMessage());
      }
      return outputStream;
    }

    @Override
    public boolean verify(byte[] expected) {
      DhSigStatic dhsig = DhSigStatic.getInstance(expected);
      byte[] expectedHashValue = dhsig.getHashValue();

      // compute the hashvalue
      byte[] hashValue = hmac.doFinal();
      return Arrays.equals(expectedHashValue, hashValue);
    }

  } // class XDHContentVerifier

  private final SecretKey hmacKey;

  private final String hmacAlgoithm;

  private final ASN1ObjectIdentifier sigAlgOid;

  public XiXDHContentVerifierProvider(PublicKey verifyKey, DHSigStaticKeyCertPair ownerKeyAndCert)
      throws InvalidKeyException {
    Args.notNull(verifyKey, "verifyKey");
    Args.notNull(ownerKeyAndCert, "ownerKeyAndCert");

    String keyAlgName = verifyKey.getAlgorithm();

    HashAlgo hash;
    if (EdECConstants.X25519.equalsIgnoreCase(keyAlgName)) {
      this.sigAlgOid = Xipki.id_alg_dhPop_x25519_sha256;
      this.hmacAlgoithm = "HMAC-SHA256";
      hash = HashAlgo.SHA256;
    } else if (EdECConstants.X448.equalsIgnoreCase(keyAlgName)) {
      this.sigAlgOid = Xipki.id_alg_dhPop_x448_sha512;
      this.hmacAlgoithm = "HMAC-SHA512";
      hash = HashAlgo.SHA512;
    }  else {
      throw new InvalidKeyException("unsupported verifyKey.getAlgorithm(): " + keyAlgName);
    }

    if (!keyAlgName.equals(ownerKeyAndCert.getPrivateKey().getAlgorithm())) {
      throw new InvalidKeyException("verifyKey and ownerKeyAndCert does not match");
    }

    // compute the secret key
    byte[] zz;
    try {
      KeyAgreement keyAgreement = KeyAgreement.getInstance(keyAlgName, "BC");
      keyAgreement.init(ownerKeyAndCert.getPrivateKey());
      keyAgreement.doPhase(verifyKey, true);
      zz = keyAgreement.generateSecret();
    } catch (NoSuchAlgorithmException | NoSuchProviderException
        | InvalidKeyException | IllegalStateException ex) {
      throw new InvalidKeyException("KeyChange error", ex);
    }

    // as defined in RFC 6955, raw hash algorithm is used as KDF

    // LeadingInfo := Subject Distinguished Name from certificate
    byte[] leadingInfo = ownerKeyAndCert.getEncodedSubject();
    // TrailingInfo ::= Issuer Distinguished Name from certificate
    byte[] trailingInfo = ownerKeyAndCert.getEncodedIssuer();
    byte[] k = hash.hash(leadingInfo, zz, trailingInfo);
    this.hmacKey = new SecretKeySpec(k, hmacAlgoithm);
  } // constructor

  @Override
  public boolean hasAssociatedCertificate() {
    return false;
  }

  @Override
  public X509CertificateHolder getAssociatedCertificate() {
    return null;
  }

  @Override
  public ContentVerifier get(AlgorithmIdentifier verifierAlgorithmIdentifier)
      throws OperatorCreationException {
    ASN1ObjectIdentifier oid = verifierAlgorithmIdentifier.getAlgorithm();
    if (!this.sigAlgOid.equals(oid)) {
      throw new OperatorCreationException(
          "given public key is not suitable for the alogithm " + oid.getId());
    }

    Mac hmac;
    try {
      hmac = Mac.getInstance(hmacAlgoithm);
    } catch (NoSuchAlgorithmException ex) {
      throw new OperatorCreationException(ex.getMessage());
    }

    return new XDHContentVerifier(verifierAlgorithmIdentifier, hmac, hmacKey);
  } // method get

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy