爲什麼你需要簽署一個jar文件?
我無法回答這部分的問題。但是,對於任何非平凡的應用程序,您至少需要通過過程自行簽署您的jar文件,即使此自簽名也不提供實際的額外安全性。任何人都可以使用Java Development Kit中提供的工具自簽名應用程序。自簽名證書適用於開發工作,但每次運行應用程序時都必須單擊風險接受複選框。
好的,我的應用程序並不重要,我需要簽署我的jar文件。什麼是程序?
下面是快速回答:這是一個兩步過程。您首先使用keytool程序創建必要的證書,然後使用jarsigner工具簽署您的jar文件。您只需稍後創建憑證,但需要簽署每個部署的jar文件。
要創建這些證書(自簽名證書)使用方法:
$JAVA_HOME/bin/keytool -genkeypair -keyalg RSA -keysize 2048 -alias mydomain -validity 1825
這將創建一個在你的主目錄這是很好的五年評爲.keystore
證書。你必須迴應它的提示,並使用「密碼」作爲密碼。由於我只使用此證書進行自簽名jar文件,因此安全性不是一個大問題。有效性參數指定證書有效的時間(以天計)。
每次更新jar文件時都需要對它進行簽名。假設你在你的分發目錄,並需要簽署applet.jar
,用途:
$JAVA_HOME/bin/jarsigner -tsa http://timestamp.digicert.com -storepass password applet.jar mydomain
「密碼」 -storepass
你以後再密鑰工具使用的密碼相匹配,而「MYDOMAIN」密鑰工具-alias
參數相匹配。您需要指定-tsa(時間戳權限)參數,並且http://timestamp.digicert.com(或至少是)一個公開可用的參數。我不知道TSA究竟做了什麼或者爲什麼需要它,但是如果沒有它,jarsigner會不高興,不會默認它,也不會直接記錄如何找到它。
您現在可以使用或忽略以下批處理文件。我創建它是因爲當我需要創建一個新證書時(我的原始證書已過期),我已經忘記了如何創建它。希望我們能夠在下次需要它時找到這個批處理文件,大概是五年後。
#!/bin/bash
#
# Title-
# mkJavaKey
#
# Function-
# Create a new key using $JAVA_HOME/bin/keytool
#
# Usage-
# mkJavaKey ## CYGWIN ONLY ##
# (This is required when jarsigner complains about an expired key.)
# NOTE: This *REMOVES* and *REPLACES* your existing .keystore file!
#
#######
##########################################################################
# Environment check
if [ -z "$JAVA_HOME" ] ; then
. setupJAVA ## (This personal script sets JAVA_HOME)
if [ -z "$JAVA_HOME" ] ; then
echo "JAVA_HOME environment variable missing"
exit 1
fi
fi
if [ -z "$HOMEPATH" ] ; then
echo "HOMEPATH environment variable missing"
echo "Try export HOMEPATH=\Users\myname"
exit 1
fi
home_path=`cygpath --path --unix C:$HOMEPATH`
PGM=$JAVA_HOME/bin/keytool
if [ ! -x "$PGM" ] ; then
echo "$PGM not executable"
exit 1
fi
##########################################################################
# Create a new .keystore
set -x
rm -Rf $home_path/.keystore
$PGM -genkeypair -keyalg RSA -keysize 2048 -alias mydomain -validity 1825
exit $?
說明:我的setupJAVA腳本設置了JAVA_HOME
環境變量。對於Linux,請使用$HOME
而不是$HOMEPATH
,並跳過cygpath
部分。這些在Cygwin環境中的Linux和Windows文件名格式之間進行轉換。
您需要登錄每次安裝它們的時間你的jar文件。爲了自動執行此操作,我修改了我的Makefile來執行此操作。下面是我用的化妝代碼片段:
.PHONY: golfer.install
golfer.install: test golfer
: (Not relevant to discussion)
cp -p $(OBJDIR)/usr/fne/golfer/Applet/applet.jar $(DEPLOYDIR)/webapps/golfer/.
jarsigner -tsa http://timestamp.digicert.com -storepass password "$(shell cygpath --path --windows "$(DEPLOYDIR)/webapps/golfer/applet.jar")" mydomain
: (Not relevant to discussion)
的$(OBDIR)
和$(DEPLOYDIR)
變量是不相關的討論。它們是在基於Makefile的構建環境中設置的目錄路徑。
如何將Applets遷移到新的JNLP環境?
既然我們有自簽名的jar文件,我們可以開始計算如何運行它們。許多瀏覽器不再支持NPAPI,因此<applet>
標記將不起作用。也不會deployJava.runApplet()。我不會理解爲什麼NPAPI支持被丟棄,只是需要做些什麼來使現有的應用程序運行。
我發現遷移我的代碼最大的問題是,最終我不得不創建.jnlp文件而不是.html文件。我會告訴你如何做到這一點,描述我修改和添加的代碼。
這是(現在已廢棄)的JavaScript代碼我用來生成html:
//------------------------------------------------------------------------
//
// Title-
// applet.js
//
// Purpose-
// Common applet javascript.
//
// Last change date-
// 2010/10/19
//
//------------------------------------------------------------------------
var out; // Output document
//------------------------------------------------------------------------
// appHead
//
// Generate html header for application.
//------------------------------------------------------------------------
function appHead(title,cname,height,width)
{
var todoWindow= window.open('','','');
out= todoWindow.document;
out.write('<html>');
out.write('<head><title>' + title + '</title></head>');
out.write('<body>\n');
out.write('<applet code="' + cname + '.class"');
out.write(' codebase="./"')
out.write(' archive="applet.jar,jars/common.jar"');
out.write(' width="' + width + '" height="' + height + '">\n');
}
//------------------------------------------------------------------------
// appParm
//
// Add parameter information
//------------------------------------------------------------------------
function appParm(name, value)
{
out.write(' <param-name="' + name + '" value="' + value + '"/>\n');
}
//------------------------------------------------------------------------
// appTail
//
// Generate html trailer information.
//------------------------------------------------------------------------
function appTail()
{
out.write('Your browser is completely ignoring the <APPLET> tag!\n');
out.write('</applet>');
out.write('<form>');
out.write('<input type="button" value="Done" onclick="window.close()">');
out.write('</form>');
out.write('</body>');
out.write('</html>');
out.close();
out= null;
}
//------------------------------------------------------------------------
// cardEvents
//
// Display scorecard for selected date.
//------------------------------------------------------------------------
function cardEvents(eventsID, obj)
{
if(obj.selectedIndex == 0)
{
alert("No date selected");
return;
}
appHead('Score card', 'EventsCard', '100%', '100%');
appParm('events-nick', eventsID);
appParm('events-date', obj[obj.selectedIndex].value);
appTail();
reset();
}
我們不需要看到我的servlet,包括用於調用cardEvents功能表單按鈕生成的html。它與「完成」按鈕生成類似,不需要更改。
它應該是非常簡單的轉換這個JavaScript來生成一個jnlp文件。這是不可能的,或者至少我找不到如何做到這一點的任何工作示例,也無法找到修改任何破壞示例的方法。 window.open()
聲明總是會添加<html>
和<body>
部分,即使我只想生成jnlp xml。我也試過document.open("application/x-java-jnlp-file")
。即使指定了mime類型,不需要的html和正文部分仍然存在。
無我發現的文件展示瞭如何動態生成我需要.jnlp文件,其中包括用戶選擇的小應用程序的參數。這是我用來替代的解決方法。
我取代的HTML代applet.js與此:
//------------------------------------------------------------------------
//
// Title-
// applet.js
//
// Purpose-
// Common applet javascript.
//
// Last change date-
// 2017/03/15
//
//------------------------------------------------------------------------
var out; // Output URL
//------------------------------------------------------------------------
// appHead
//
// Generate application URL header.
//------------------------------------------------------------------------
function appHead(title,cname,height,width)
{
out= cname + ',' + title;
}
//------------------------------------------------------------------------
// appParm
//
// Generate html parameter information.
//------------------------------------------------------------------------
function appParm(name, value)
{
out= out + ',' + name + '=' + value;
}
//------------------------------------------------------------------------
// appTail
//
// Generate html trailer information.
//------------------------------------------------------------------------
function appTail()
{
var specs= 'menubar=yes,toolbar=yes';
window.open('Applet.jnlp?' + out, '_self', specs);
}
//------------------------------------------------------------------------
// cardEvents
//
// Display scorecard for selected date.
//------------------------------------------------------------------------
function cardEvents(eventsID, obj)
{
// (UNCHANGED!)
}
這產生在Applet.jnlp,className,description,parm=value,parm=value,...
形式的URL。
然後我創建了一個名爲AppletServlet.java新的Servlet。傳遞給它的URL提供了生成.jnlp文件所需的全部信息。此代碼遵循標準示例Servlet結構,其中doGet被調用來處理請求。下面的代碼:
//------------------------------------------------------------------------
//
// Method-
// AppletServlet.doGet
//
// Purpose-
// Called for each HTTP GET request.
//
//------------------------------------------------------------------------
public void
doGet( // Handle HTTP "GET" request
HttpServletRequest req, // Request information
HttpServletResponse res) // Response information
throws ServletException, IOException
{
String q= req.getQueryString();
if(debug) log("doGet("+q+")");
res.setContentType("text/html");
query(req, res);
}
//------------------------------------------------------------------------
//
// Method-
// AppletServlet.putError
//
// Purpose-
// Generate error response.
//
//------------------------------------------------------------------------
public void
putError( // Generate error response
PrintWriter out, // The response writer
String msg) // The error message
{ out.println("<HTML>");
out.println("<HEAD><TITLE>" + msg + "</TITLE></HEAD>");
out.println("<BODY>");
out.println("<H1 align=\"center\">" + msg + "</H1>");
out.println("</BODY>");
out.println("</HTML>");
}
//------------------------------------------------------------------------
//
// Method-
// AppletServlet.query
//
// Purpose-
// Handle a query.
//
//------------------------------------------------------------------------
protected void
query( // Handle a query
HttpServletRequest req, // Request information
HttpServletResponse res) // Response information
throws ServletException, IOException
{
String q= req.getQueryString();
if(debug) log("query("+q+")");
PrintWriter out = res.getWriter();
String BOGUS= "<br> Malformed request: query: '" + q + "'";
//=====================================================================
// Applet.jnlp?classname,title,parm=value,parm=value,...
int index= q.indexOf(',');
if(index < 0 || index == (q.length() - 1))
{
putError(out, BOGUS);
return;
}
String invoke= q.substring(0, index);
q= q.substring(index+1);
index= q.indexOf(',');
if(index < 0)
index= q.length();
String title= q.substring(0, index);
title= java.net.URLDecoder.decode(title, "UTF-8");
// Parameter extraction
Vector<String> param= new Vector<String>();
if(index < q.length())
{
q= q.substring(index+1);
for(;;)
{
index= q.indexOf(',');
if(index < 0)
index= q.length();
String s= q.substring(0, index);
int x= s.indexOf('=');
if(x < 0)
{
putError(out, BOGUS);
return;
}
param.add(s);
if(index >= q.length())
break;
q= q.substring(index+1);
}
}
//---------------------------------------------------------------------
// We now have enough information to generate the response
//---------------------------------------------------------------------
res.setContentType("application/x-java-jnlp-file");
out.println("<?xml version='1.0' encoding='utf-8'?>");
out.println("<jnlp spec='1.0+' codebase='http://localhost:8080/golfer'>");
out.println(" <information>");
out.println(" <title>" + title + "</title>");
out.println(" <vendor>My Name</vendor>");
out.println(" <description>" + title + "</description>");
out.println(" </information>");
out.println(" <security><all-permissions/></security>");
out.println(" <resources>");
out.println(" <j2se version='1.7+'/>");
out.println(" <jar href='applet.jar'/>");
out.println(" <jar href='jars/common.jar'/>");
out.println(" </resources>");
out.println(" <applet-desc main-class='" + invoke + "' name='" + title + "'" +
" height='90%' width='98%'>");
// Insert applet parameters
for(int i= 0; i<param.size(); i++)
{
String s= param.elementAt(i);
int x= s.indexOf('=');
String n= s.substring(0,x);
String v= s.substring(x+1);
out.println(" <param name='" + n+ "' value='" + v + "'/>");
}
out.println(" </applet-desc>");
out.println("</jnlp>");
}
注:debug
是我的「調試啓用」標誌,並log()
到標準輸出寫入調試信息。在這個新的代碼版本中,高度和寬度不作爲參數傳遞,而是用硬編碼代替。事實證明,在HTML版本中,「100%」總是用作高度和寬度,並且運行良好。對於一些(我不知道)原因,當使用100%高度和寬度的.jnlp代碼調用時,我的applet窗口在底部被截斷,可能在右側被截斷。我使用這些新的高度和寬度參數來解決這個格式問題。
爲了調用我的新AppletServlet,我修改web.xml文件:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<servlet>
<servlet-name>Applet</servlet-name>
<servlet-class>usr.fne.golfer.AppletServlet</servlet-class>
<init-param>
<param-name>property-path</param-name>
<param-value>profile</param-value>
</init-param>
<init-param>
<param-name>property-file</param-name>
<param-value>golfer.pro</param-value>
</init-param>
<load-on-startup>30</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Applet</servlet-name>
<url-pattern>/Applet.jnlp</url-pattern>
</servlet-mapping>
: (Other Servlets unchanged)
</web-app>
這將導致AppletServlet被調用任何Applet.jnlp URL。瀏覽器忽略查詢字符串,並將結果視爲文件名稱爲Applet.jnlp。
爲了更流暢的操作,你需要設置你的Windows文件關聯,以便.jnlp
文件調用Java(TM)網絡開始啓動。在Windows中,您的JWS啓動器是C:\Program Files\java\jre*\bin\javaws.exe
(使用最新的jre文件夾。)另外,如果您使用Chrome,則您的下載目錄將包含生成的Applet.jnlp文件。你需要立即清理它們。
這完成遷移過程。在這次遷移中沒有小應用程序受到傷害(或更改),因此30,000個源代碼行中的大部分保持不變。
雖然我用剪切和操作代碼粘貼到創建的例子,它可能是拼寫錯誤可能在已悄悄請評論,如果您發現任何不正確,缺失或不清晰。