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

org.xipki.p11proxy.client.ProxyP11Module Maven / Gradle / Ivy

The newest version!
/*
 *
 * Copyright (c) 2013 - 2018 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.p11proxy.client;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;

import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1Object;
import org.bouncycastle.asn1.ASN1Sequence;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xipki.p11proxy.P11ProxyConstants;
import org.xipki.p11proxy.msg.Asn1P11SlotIdentifier;
import org.xipki.p11proxy.msg.Asn1ServerCaps;
import org.xipki.security.exception.BadAsn1ObjectException;
import org.xipki.security.pkcs11.AbstractP11Module;
import org.xipki.security.pkcs11.P11Module;
import org.xipki.security.pkcs11.P11ModuleConf;
import org.xipki.security.pkcs11.P11Slot;
import org.xipki.security.pkcs11.P11SlotIdentifier;
import org.xipki.security.pkcs11.exception.P11TokenException;
import org.xipki.util.ConfPairs;
import org.xipki.util.IoUtil;
import org.xipki.util.LogUtil;
import org.xipki.util.ParamUtil;
import org.xipki.util.StringUtil;

/**
 * TODO.
 * @author Lijun Liao
 * @since 2.0.0
 */

public class ProxyP11Module extends AbstractP11Module {

  public static final String TYPE = "proxy";

  private static final Logger LOG = LoggerFactory.getLogger(ProxyP11Module.class);

  private static final String REQUEST_MIMETYPE = "application/x-xipki-pkcs11";

  private static final String RESPONSE_MIMETYPE = "application/x-xipki-pkcs11";

  private final Random random = new Random();

  private final short version = P11ProxyConstants.VERSION_V1_0;

  private final String description;

  private URL serverUrl;

  private short moduleId;

  private boolean readOnly;

  private ProxyP11Module(P11ModuleConf moduleConf) throws P11TokenException {
    super(moduleConf);

    final String modulePath = moduleConf.getNativeLibrary();

    this.description = StringUtil.concat("PKCS#11 proxy", "\nPath: ", modulePath);

    ConfPairs confPairs = new ConfPairs(modulePath);
    String urlStr = confPairs.value("url");
    try {
      serverUrl = new URL(urlStr);
    } catch (MalformedURLException ex) {
      throw new IllegalArgumentException("invalid url: " + urlStr);
    }

    String moduleStr = confPairs.value("module");
    if (moduleStr == null) {
      throw new IllegalArgumentException("module not specified");
    }

    try {
      moduleStr = moduleStr.trim();
      if (moduleStr.startsWith("0x") || moduleStr.startsWith("0X")) {
        moduleId = Short.parseShort(moduleStr.substring(2), 16);
      } else {
        moduleId = Short.parseShort(moduleStr.trim());
      }
    } catch (NumberFormatException ex) {
      throw new IllegalArgumentException("invalid module: " + moduleStr);
    }

    refresh();
  }

  public static P11Module getInstance(P11ModuleConf moduleConf) throws P11TokenException {
    ParamUtil.requireNonNull("moduleConf", moduleConf);
    return new ProxyP11Module(moduleConf);
  }

  @Override
  public boolean isReadOnly() {
    return readOnly || super.isReadOnly();
  }

  public void refresh() throws P11TokenException {
    byte[] resp = send(P11ProxyConstants.ACTION_GET_SERVER_CAPS, null);

    Asn1ServerCaps caps;
    try {
      caps = Asn1ServerCaps.getInstance(resp);
    } catch (BadAsn1ObjectException ex) {
      throw new P11TokenException("response is a valid Asn1ServerCaps", ex);
    }

    if (!caps.getVersions().contains(version)) {
      throw new P11TokenException("Server does not support any version supported by the client");
    }
    this.readOnly = caps.isReadOnly();

    resp = send(P11ProxyConstants.ACTION_GET_SLOT_IDS, null);

    ASN1Sequence seq;
    try {
      seq = ASN1Sequence.getInstance(resp);
    } catch (IllegalArgumentException ex) {
      throw new P11TokenException("response is not ASN1Sequence", ex);
    }

    final int n = seq.size();

    Set slots = new HashSet<>();
    for (int i = 0; i < n; i++) {
      Asn1P11SlotIdentifier asn1SlotId;
      try {
        ASN1Encodable obj = seq.getObjectAt(i);
        asn1SlotId = Asn1P11SlotIdentifier.getInstance(obj);
      } catch (Exception ex) {
        throw new P11TokenException(ex.getMessage(), ex);
      }

      P11SlotIdentifier slotId = asn1SlotId.getValue();
      if (!conf.isSlotIncluded(slotId)) {
        continue;
      }

      if (!conf.isSlotIncluded(slotId)) {
        LOG.info("skipped slot {}", slotId);
        continue;
      }

      P11Slot slot = new ProxyP11Slot(this, slotId, conf.isReadOnly(),
          conf.getP11MechanismFilter());
      slots.add(slot);
    }
    setSlots(slots);
  }

  @Override
  public String getDescription() {
    return description;
  }

  @Override
  public void close() {
    for (P11SlotIdentifier slotId : getSlotIds()) {
      try {
        getSlot(slotId).close();
      } catch (Throwable th) {
        LogUtil.error(LOG, th, "could not close PKCS#11 slot " + slotId);
      }
    }
  }

  protected byte[] send(byte[] request) throws IOException {
    ParamUtil.requireNonNull("request", request);
    HttpURLConnection httpUrlConnection = IoUtil.openHttpConn(serverUrl);
    httpUrlConnection.setDoOutput(true);
    httpUrlConnection.setUseCaches(false);

    int size = request.length;

    httpUrlConnection.setRequestMethod("POST");
    httpUrlConnection.setRequestProperty("Content-Type", REQUEST_MIMETYPE);
    httpUrlConnection.setRequestProperty("Content-Length", java.lang.Integer.toString(size));
    OutputStream outputstream = httpUrlConnection.getOutputStream();
    outputstream.write(request);
    outputstream.flush();

    if (httpUrlConnection.getResponseCode() != HttpURLConnection.HTTP_OK) {
      try {
        try {
          InputStream is = httpUrlConnection.getInputStream();
          if (is != null) {
            is.close();
          }
        } catch (IOException ex) {
          InputStream errStream = httpUrlConnection.getErrorStream();
          if (errStream != null) {
            errStream.close();
          }
        }
      } catch (Throwable th) {
        // ignore it
      }

      throw new IOException("bad response: code=" + httpUrlConnection.getResponseCode()
          + ", message=" + httpUrlConnection.getResponseMessage());
    }

    InputStream inputstream = null;
    try {
      inputstream = httpUrlConnection.getInputStream();
    } catch (IOException ex) {
      InputStream errStream = httpUrlConnection.getErrorStream();
      if (errStream != null) {
        errStream.close();
      }
      throw ex;
    }

    try {
      String responseContentType = httpUrlConnection.getContentType();
      boolean isValidContentType = false;
      if (responseContentType != null) {
        if (responseContentType.equalsIgnoreCase(RESPONSE_MIMETYPE)) {
          isValidContentType = true;
        }
      }
      if (!isValidContentType) {
        throw new IOException("bad response: mime type " + responseContentType
            + " is not supported!");
      }

      byte[] buf = new byte[4096];
      ByteArrayOutputStream bytearrayoutputstream = new ByteArrayOutputStream();
      do {
        int readedByte = inputstream.read(buf);
        if (readedByte == -1) {
          break;
        }
        bytearrayoutputstream.write(buf, 0, readedByte);
      } while (true);

      return bytearrayoutputstream.toByteArray();
    } finally {
      inputstream.close();
    }
  } // method send

  /**
   * The request is constructed as follows.
   * 
   * 0 - - - 1 - - - 2 - - - 3 - - - 4 - - - 5 - - - 6 - - - 7 - - - 8
   * |    Version    |        Transaction ID         |   Body ...    |
   * |   ... Length  |     Action    |   Module ID   |   Content...  |
   * |   .Content               | <-- 10 + Length (offset).
   *
   * 
* @param action action * @param content content * @return result. * @throws P11TokenException If error occurred. */ public byte[] send(short action, ASN1Object content) throws P11TokenException { byte[] encodedContent; if (content == null) { encodedContent = null; } else { try { encodedContent = content.getEncoded(); } catch (IOException ex) { throw new P11TokenException("could encode the content", ex); } } int bodyLen = 4; if (encodedContent != null) { bodyLen += encodedContent.length; } byte[] request = new byte[10 + bodyLen]; // version IoUtil.writeShort(version, request, 0); // transaction id byte[] transactionId = randomTransactionId(); System.arraycopy(transactionId, 0, request, 2, 4); // length IoUtil.writeInt(bodyLen, request, 6); // action IoUtil.writeShort(action, request, 10); // module ID IoUtil.writeShort(moduleId, request, 12); //content if (encodedContent != null) { System.arraycopy(encodedContent, 0, request, 14, encodedContent.length); } byte[] response; try { response = send(request); } catch (IOException ex) { final String msg = "could not send the request"; LOG.error(msg + " {}", request); throw new P11TokenException(msg + ": " + ex.getMessage(), ex); } int respLen = response.length; if (respLen < 12) { throw new P11TokenException("response too short"); } // Length int respBodyLen = IoUtil.parseInt(response, 6); if (respBodyLen + 10 != respLen) { throw new P11TokenException("message lengt unmatch"); } // RC short rc = IoUtil.parseShort(response, 10); if (rc != 0) { throw new P11TokenException("server returned RC " + P11ProxyConstants.getReturnCodeName(rc)); } // Version short respVersion = IoUtil.parseShort(response, 0); if (version != respVersion) { throw new P11TokenException("version of response and request unmatch"); } // TransactionID if (!equals(transactionId, response, 2)) { throw new P11TokenException("version of response and request unmatch"); } if (respLen < 14) { throw new P11TokenException("too short successful response"); } short respAction = IoUtil.parseShort(response, 12); if (action != respAction) { throw new P11TokenException("action of response and request unmatch"); } int respContentLen = respLen - 14; if (respContentLen == 0) { return null; } byte[] respContent = new byte[respContentLen]; System.arraycopy(response, 14, respContent, 0, respContentLen); return respContent; } // method send private byte[] randomTransactionId() { byte[] tid = new byte[4]; random.nextBytes(tid); return tid; } private static boolean equals(byte[] bytes, byte[] bytesB, int offsetB) { if (bytesB.length - offsetB < bytes.length) { return false; } for (int i = 0; i < bytes.length; i++) { if (bytes[i] != bytesB[offsetB + i]) { return false; } } return true; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy