/*
* Copyright (c) 2008-2016 Computer Network Information Center (CNIC), Chinese Academy of Sciences.
*
* This file is part of Duckling project.
*
* 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 cn.vlabs.umt.services.ca;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.apache.commons.lang.StringUtils;
import org.apache.http.client.HttpClient;
import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipOutputStream;
import cn.vlabs.duckling.ca.HttpClientFactory;
import cn.vlabs.duckling.ca.XPathParser;
import cn.vlabs.duckling.ca.action.ActionResult;
import cn.vlabs.duckling.ca.action.CsrRequestAction;
import cn.vlabs.duckling.ca.action.DownloadCertificateAndKeypairByKeyAction;
import cn.vlabs.duckling.ca.action.GetCertificateByKeyAction;
import cn.vlabs.duckling.ca.action.GetKeyByDnAction;
import cn.vlabs.duckling.ca.action.HttpAction;
import cn.vlabs.duckling.ca.action.XsrfTokenAction;
import cn.vlabs.umt.common.util.UMTStringUtils;
public class CaService {
public static String CERT_FORMAT_CRT="crt";
public static String CERT_FORMAT_TXT="txt";
public static String CERT_FORMAT_PEM="pem";
public static String CERT_FORMAT_DER="der";
public static String CERT_FORMAT_CER="cer";
public static String CERT_FORRMAT_CERT_KEY_OPENSSL="openssl";
public static String CERT_FORRMAT_CERT_KEY_PKCS8="pkcs8";
public static String CERT_FORRMAT_CERT_KEY_PKCS12="pkcs12";
private static final String REQUEST_KEY_XPATH = "/HTML/BODY/FORM/CENTER/TABLE/TR/TD/CENTER/TABLE/TR[2]/TD[1]/A";
private String endDN;
private HttpClient httpClient;
private String serverUrl;
public void setServerUrl(String serverUrl) {
this.serverUrl = serverUrl;
}
public void setEndDN(String endDN) {
this.endDN = endDN;
}
public CaService() {
this.httpClient = HttpClientFactory.getHttpClient();
}
public String getToken() throws IOException{
Map<String, String> context = new HashMap<String, String>();
context.put("baseUrl", serverUrl);
XsrfTokenAction xsrf = new XsrfTokenAction();
xsrf.execute(httpClient, context);
ActionResult actionResult=xsrf.execute(httpClient, context);
String xsrfToken = parseToken(actionResult);
return xsrfToken;
}
public boolean submitCsrRequest(String cn, String password, String email,String dn) throws IOException{
String token=getToken();
return submitCsrRequest(cn,password,email,dn,token);
}
public boolean submitCsrRequest(String cn, String password, String email,String dn,String token)
throws IOException {
Map<String, String> context = new HashMap<String, String>();
context.put(HttpAction.BASE_URL, serverUrl);
context.put(HttpAction.XSRF_TOKEN, token);
context.put(HttpAction.DN, dn);
context.put(HttpAction.CN, cn);
context.put(HttpAction.PASSWORD, password);
context.put(HttpAction.EMAIL, email);
CsrRequestAction request = new CsrRequestAction();
ActionResult actionResult=request.execute(httpClient, context);
return (actionResult.getResponseCode()== 200);
}
public InputStream getCert(String dn,String certFormat) throws IOException, DownloadCaException, KeyEmptyException{
String token=getToken();
String key=getKey(dn,token);
return getCertByKey(key,token,certFormat);
}
public InputStream getCertByKey(String requestKey,String certFormat) throws IOException, DownloadCaException{
String token=getToken();
return getCertByKey(requestKey,token,certFormat);
}
public InputStream getCertByKey(String key,String token,String certFormat) throws IOException, DownloadCaException{
Map<String, String> context = new HashMap<String, String>();
context.put(HttpAction.BASE_URL, serverUrl);
context.put(HttpAction.REQUEST_KEY, key);
context.put(HttpAction.XSRF_TOKEN, token);
context.put(HttpAction.CERT_FORMAT, certFormat);
GetCertificateByKeyAction download=new GetCertificateByKeyAction();
ActionResult actionResult=download.execute(httpClient, context);
if (actionResult.getResponseCode()== 200&&!actionResult.isHtml()) {
return actionResult.getIn();
}
throw new DownloadCaException(key);
}
public InputStream downloadCertAndKeypairByKey(String requestKey,String password,String dn,String certKeyFormat) throws IOException, DownloadCaException{
String token=getToken();
return downloadCertAndKeypairByKey(requestKey,password,dn,token,certKeyFormat);
}
public InputStream downloadCertAndKeypairByKey(String password,String dn,String certKeyFormat) throws IOException, DownloadCaException, KeyEmptyException{
String token=getToken();
String key=getKey(dn,token);
return downloadCertAndKeypairByKey(key,password,dn,token,certKeyFormat);
}
public String downloadKeypair(String password,String dn) throws IOException, DownloadCaException, KeyEmptyException{
String token=getToken();
String key=getKey(dn,token);
InputStream in= downloadCertAndKeypairByKey(key,password,dn,token,CERT_FORRMAT_CERT_KEY_OPENSSL);
String encode = "UTF-8";
InputStreamReader reader = new InputStreamReader(in, encode);
try {
String certAndKey=UMTStringUtils.readToString(reader);
String keypair=StringUtils.substring(certAndKey, StringUtils.indexOf(certAndKey, "-----BEGIN ENCRYPTED PRIVATE KEY-----"), certAndKey.length());
return keypair;
} finally {
reader.close();
}
}
public InputStream downloadCertAndKeypairByKey(String key,String password,String dn,String token,String certKeyFormat) throws IOException, DownloadCaException{
Map<String, String> context = new HashMap<String, String>();
context.put(HttpAction.BASE_URL, serverUrl);
context.put(HttpAction.REQUEST_KEY, key);
context.put(HttpAction.PASSWORD, password);
context.put(HttpAction.DN, dn);
context.put(HttpAction.XSRF_TOKEN, token);
context.put(HttpAction.CERT_KEY_Format, certKeyFormat);
DownloadCertificateAndKeypairByKeyAction download=new DownloadCertificateAndKeypairByKeyAction();
ActionResult actionResult=download.execute(httpClient, context);
if (actionResult.getResponseCode()== 200&&!actionResult.isHtml()) {
return actionResult.getIn();
}
throw new DownloadCaException(key);
}
public String getKey(String dn) throws IOException, KeyEmptyException{
String token=getToken();
return getKey(dn,token);
}
public String getKey(String dn,String token) throws IOException, KeyEmptyException{
Map<String, String> context = new HashMap<String, String>();
context.put(HttpAction.BASE_URL, serverUrl);
context.put(HttpAction.DN, dn);
context.put(HttpAction.XSRF_TOKEN, token);
GetKeyByDnAction download=new GetKeyByDnAction();
ActionResult actionResult=download.execute(httpClient, context);
if (actionResult.getResponseCode()== 200) {
BufferedReader reader = new BufferedReader(new InputStreamReader(actionResult.getIn(),"UTF-8"));
String href=XPathParser.getNodeValue(reader, REQUEST_KEY_XPATH,"href");
String result=getKeyFormHrefUrl(href);
if(StringUtils.isBlank(result)){
throw new KeyEmptyException(dn);
}
}
return "";
}
public InputStream getCertAll(String password,String dn,String cn) throws IOException, DownloadCaException, KeyEmptyException{
String token=this.getToken();
String key=getKey(dn);
InputStream cerIn=getCertByKey(key,token, CERT_FORMAT_CER);
InputStream p12In=downloadCertAndKeypairByKey(key,password, dn,token, CERT_FORRMAT_CERT_KEY_PKCS12);
InputStream keypair=new ByteArrayInputStream(downloadKeypair(password, dn).getBytes());
ByteArrayOutputStream zipBaseOut = new ByteArrayOutputStream();
ZipOutputStream zipOut=new ZipOutputStream(zipBaseOut);
zipCert(cerIn,zipOut,cn+".cer");
zipCert(p12In,zipOut,cn+".p12");
zipCert(keypair,zipOut,cn+".pem");
zipOut.close();
return new ByteArrayInputStream(zipBaseOut.toByteArray());
}
private void zipCert(InputStream in,ZipOutputStream zipOut,String fileName) throws IOException{
zipOut.putNextEntry(new ZipEntry(fileName));
int count;
byte data[] = new byte[100];
while ((count = in.read(data, 0, 100)) != -1) {
zipOut.write(data, 0, count);
}
in.close();
}
private String getKeyFormHrefUrl(String href){
if(href==null||href.equals("")){
return "";
}
int keyStart=StringUtils.indexOf(href, "key=");
int keyEnd=StringUtils.indexOf(StringUtils.substring(href, keyStart),";");
return StringUtils.substring(href, keyStart+4, keyStart+keyEnd);
}
private String parseToken(ActionResult result)
throws UnsupportedOperationException, IOException {
try {
List<String> lines = IOUtils.readLines(result.getIn());
Pattern pattern = Pattern
.compile(".*\";xsrf_protection_token=(.*)\".*");
for (String line : lines) {
Matcher matcher = pattern.matcher(line);
if (matcher.matches()) {
return matcher.group(1);
}
}
return "";
} finally {
result.getIn().close();
}
}
public String buildDN(String cn){
return "CN="+cn+endDN;
}
}