#
查看密鑰
密匙生成后,可以隨時(shí)用以下命令查看。
查看所有密鑰:
查看所有公鑰:
查看所有私鑰:
列出所有簽名:
導(dǎo)出公鑰
在非對(duì)稱加密體系中,私鑰是由用戶保管,而公鑰是對(duì)外公開的。用戶在生成密鑰對(duì)后,需要把其中的公鑰導(dǎo)出到一個(gè)文件中,然后將其分發(fā)給其它用戶。
導(dǎo)出公鑰的方法很簡(jiǎn)單,通過(guò)gpg命令的“-export”參數(shù)就可完成。為了使導(dǎo)出文件是ASCⅡ編碼的,還需要加上參數(shù)“-a”。比如,導(dǎo)出Terry Yu ASCⅡ編碼的公鑰文件,可以使用以下命令:
分發(fā)公鑰
這個(gè)包含公鑰信息的文件需要對(duì)外分發(fā),可以通過(guò)各種方式將terry.acs文件分發(fā)給所有與用戶有信息通信需求的人。
最簡(jiǎn)單的分發(fā)方式是,將該文件放到互聯(lián)網(wǎng)上供人下載。這種方式需要注意的問(wèn)題是,所發(fā)布公鑰文件的網(wǎng)站一定要是一個(gè)可以信賴的站點(diǎn)。實(shí)際應(yīng)用中,類似的做法很普遍。
比如,Red Hat的公鑰就是在它的官方網(wǎng)站上發(fā)布的,任何人都可以下載獲得,并用來(lái)驗(yàn)證Red Hat所發(fā)布軟件的簽名的正確性。
命令開始運(yùn)行后,首先,會(huì)看到版本和路徑信息如下
隨后需要回答一系列問(wèn)題,以幫助產(chǎn)生一對(duì)密鑰。首先遇到的問(wèn)題是要求選擇密鑰使用的算法:
其中,DSA是數(shù)字簽名算法,RSA和ElGamal是兩種不同原理的非對(duì)稱密鑰算法。通常可以選擇“1”,這樣生成的密鑰可以同時(shí)用作簽名和加密兩種用途。
接著,會(huì)要求選擇密鑰的長(zhǎng)度:
這里的密鑰長(zhǎng)度有768、1024和2048位三種。顯然,密鑰越長(zhǎng)越安全,但太長(zhǎng)又會(huì)影響使用的速度。所以,可以根據(jù)不同的需要選擇適合的長(zhǎng)度。
另外,還需要設(shè)定密鑰過(guò)期的時(shí)間:
原則上,密鑰使用的頻率越高,密鑰有效的時(shí)間越長(zhǎng),被攻擊的可能性就越大。所以,要根據(jù)應(yīng)用的實(shí)際情況綜合考慮,確定一個(gè)適當(dāng)?shù)臅r(shí)間長(zhǎng)度。需要注意的是,密鑰要定期更換,建議絕對(duì)不要永遠(yuǎn)使用同一對(duì)密鑰。
最后,需要輸入一些個(gè)人信息,包括真實(shí)姓名、電子郵件地址等,用來(lái)識(shí)別密鑰,最好是如實(shí)填寫。比如:
然后,必須輸入一個(gè)密碼。密碼用來(lái)保護(hù)密鑰,沒有這個(gè)密碼,任何人都不能看到密鑰本身的內(nèi)容。密碼是在密鑰文件泄露后惟一的保密措施,它的最大敵人是暴力破解和字典攻擊。所以,一定要選擇一個(gè)強(qiáng)壯的密碼,來(lái)有效地對(duì)抗這些攻擊。
密碼確定以后,系統(tǒng)開始運(yùn)算:

這時(shí)需要隨便地敲擊鍵盤或是移動(dòng)鼠標(biāo),以產(chǎn)生一些隨機(jī)數(shù),協(xié)助密鑰的順利生成。注意,如果沒有以上動(dòng)作,很可能最終不能產(chǎn)生密鑰。
以上信息表示已經(jīng)成功地為“ivan wan”生成并簽名了一對(duì)密鑰,密鑰過(guò)期時(shí)間為“2005-09-22”。在生成密鑰的同時(shí),默認(rèn)用戶目錄的.gnupg目錄中也存放了與該用戶相關(guān)的
GPG配置及密鑰存儲(chǔ)文件。這些文件控制了用戶的GPG環(huán)境,用戶不能直接修改這些文件,所有改動(dòng)都將通過(guò)“gpg”命令實(shí)現(xiàn)。
建立GPG環(huán)境
GPG軟件作為用于加密和數(shù)字簽名的開放源碼工具,許多Linux發(fā)行版本都自帶了該軟件。在默認(rèn)安裝的情況下,gpg會(huì)作為一個(gè)基本命令事先安裝好。
如果選用的Linux發(fā)行版默認(rèn)沒有安裝GPG,可以通過(guò)tar包或RPM包進(jìn)行安裝,可從http://www.gnupg.org/download/下載安裝包。安裝過(guò)程比較簡(jiǎn)單,這里省略了。
判斷是否安裝有GPG的方法也很簡(jiǎn)單。直接在命令行下輸入“gpg -h”命令,如果系統(tǒng)已經(jīng)安裝有GPG,就會(huì)顯示關(guān)于GPG用法的信息。
確定Linux系統(tǒng)中已經(jīng)安裝了GPG后,就可以開始下面加密和簽名的工作了。 建立GPG環(huán)境
GPG軟件作為用于加密和數(shù)字簽名的開放源碼工具,許多Linux發(fā)行版本都自帶了該軟件。在默認(rèn)安裝的情況下,gpg會(huì)作為一個(gè)基本命令事先安裝好。
如果選用的Linux發(fā)行版默認(rèn)沒有安裝GPG,可以通過(guò)tar包或RPM包進(jìn)行安裝,可從http://www.gnupg.org/download/下載安裝包。安裝過(guò)程比較簡(jiǎn)單,這里省略了。
判斷是否安裝有GPG的方法也很簡(jiǎn)單。直接在命令行下輸入“gpg -h”命令,如果系統(tǒng)已經(jīng)安裝有GPG,就會(huì)顯示關(guān)于GPG用法的信息。
確定Linux系統(tǒng)中已經(jīng)安裝了GPG后,就可以開始下面加密和簽名的工作了。
生成密鑰
用戶應(yīng)用GPG,首先要有一對(duì)自己的密鑰。所以,第一步就是產(chǎn)生一對(duì)密鑰。gpg命令通過(guò)大量參數(shù)提供所需要的幾乎所有操作。其中,參數(shù)“-gen-key”就是用來(lái)產(chǎn)生一對(duì)密鑰的。在安裝了GPG的Linux系統(tǒng)上可以運(yùn)行以下命令:
如果想對(duì)產(chǎn)生密鑰的操作進(jìn)行一些個(gè)性化設(shè)置,還可以加上其它參數(shù)。比如,要指定生成密鑰存放的位置,可以運(yùn)行以下命令:
范例(Examples) Java 2擁有一組全新群集(collections)--并非僅僅加入一些新classes,而是完全改變了群集的風(fēng)格.所以在Java 1.1和Java 2中,封裝群集的方式也完全不同.我首先討論Java 2的方式,因?yàn)槲艺J(rèn)為功能更強(qiáng)大的Java 2 collections會(huì)取代Java 1.1 collections的地位.
范例(Examples): Java 2
假設(shè)有個(gè)人要去上課.我們用一個(gè)簡(jiǎn)單的Course來(lái)表示[課程]: class Course... public Course(String name, boolean isAdvanced) {...}; public boolean isAdvanced() {...}; 我不關(guān)心課程其他細(xì)節(jié).我感興趣的是表示[人]的Person: class Person... public Set getCourse() { return _courses; } public void setCourse(Set arg) { _courses = arg; } private Set _courses; 有了這個(gè)接口,我們就可以這樣為某人添加課程: Person kent = new Person(); Set s = new HashSet(); s.add(new Course("Smalltalk Programming", false)); s.add(new Course("Appreciating Single Malts", true)); kent.setCourses(s); Assert.equals(2, Kent.getCourses().size());
Course refact = new Course("Refactoring", true); kent.getCourses().add(refact); kent.getCourses().add(new Course("Brutal Sarcasm", false)); Assert.equals(4, kent.getCourses().size());
kent.getCourses().remove(refact); Assert.equals(3, kent.getCourses().size()); 如果想了解高級(jí)課程,可以這么做: Iterator iter = person.getCourses().iterator(); int count = 0; while(iter.hasNext()) { Course each = (Course)iter.next(); if(each.isAdvanced()) count++; } 我要做的第一件事就是為Person中的群集(collections)建立合適的修改函數(shù)(modifiers, 亦即add/remove函數(shù)),如下所示,然后編譯: class Person... public void addCourse(Course arg) { _courses.add(arg); } public void removeCourse(Course arg) { _courses.remove(arg); } 如果我想下面這樣初始化_courses值域,我的人生會(huì)輕松得多: private Set _courses = new HashSet();
接下來(lái)我需要觀察設(shè)值函數(shù)(setter)的調(diào)用者.如果有許多地點(diǎn)大量運(yùn)用了設(shè)值函數(shù),我就需要修改設(shè)值函數(shù),令它調(diào)用添加/移除(add/remove)函數(shù).這個(gè)過(guò)程的復(fù)雜度取決于設(shè)值函數(shù)的被使用方式.設(shè)值函數(shù)的用法有兩種,最簡(jiǎn)單的情況就是:它被用來(lái)[對(duì)集群進(jìn)行初始化動(dòng)作].換句話說(shuō),設(shè)值函數(shù)被調(diào)用之前,_courses是個(gè)空群集.這種情況下我需要修改設(shè)值函數(shù),令它調(diào)用添加函數(shù)(add)就行了: class Person... public void setCourses(Set arg) { Assert.isTrue(_courses.isEmpty()); Iterator iter = arg.iterator(); while(iter.hasNext()) { addCourse((Course)iter.next()); } } 修改完畢后,最后以Rename Method(273)更明確地展示這個(gè)函數(shù)的意圖. public void initializeCourses(Set arg) { Assert.isTrue(_courses.isEmpty()); Iterator iter = arg.iterator(); while(iter.hasNext()) { addCourse((Course)iter.next()); } } 更普通的情況下,我必須首先以移除函數(shù)(remove)將群集中的所有元素全部移除,然后再調(diào)用添加函數(shù)(add)將元素一一添加進(jìn)去.不過(guò)我發(fā)現(xiàn)這種情況很少出現(xiàn)(唔,愈是普通的情況,愈少出現(xiàn)).
如果我知道初始化時(shí),除了添加元素,不會(huì)再有其他行為,那么我可以不使用循環(huán),直接調(diào)用addAll()函數(shù): public void initializeCourses(Set arg) { Assert.isTrue(_courses.isEmpty()); _courses.addAll(arg); } 我不能僅僅對(duì)這個(gè)set賦值,就算原本這個(gè)set是空的也不行.因?yàn)槿f(wàn)一用戶在[把set傳遞給Person對(duì)象]之后又去修改它,會(huì)破壞封裝.我必須像上面那樣創(chuàng)建set的一個(gè)拷貝.
如果用戶僅僅只是創(chuàng)建一個(gè)set,然后使用設(shè)值函數(shù)(setter.譯注:目前已改名為initializeCourses()),我可以讓它們直接使用添加/移除(add/remove)函數(shù),并將設(shè)值函數(shù)完全移除.于是,以下代碼: Person kent = new Person(); Set s = new HashSet(); s.add(new Course("Smalltalk Programming", false)); s.add(new Course("Appreciating Single Malts", true)); kent.initializeCourses(s); 就變成了: Person kent = new Person(); kent.addCourse(new Course("Smalltalk Programming", false)); kent.addCourse(new Course("Appreciating Single Malts", true));
接下來(lái)我開始觀察取值函數(shù)(getter)的使用情況.首先處理[有人以取值函數(shù)修改底部群集(underlying collection)]的情況,例如: kent.getCourses().add(new Course("Brutal Sarcasm", false)); 這種情況下我必須加以改變,使它調(diào)用新的修改函數(shù)(modifier): kent.addCourse(new Course("Brutal Sarcasm", false)); 修改完所有此類情況之后,我可以讓取值函數(shù)(getter)返回一個(gè)只讀映件(read-only view),用以確保沒有任何一個(gè)用戶能夠通過(guò)取值函數(shù)(getter)修改群集: public Set getCourses() { return Collections.unmodifiableSet(_courses); } 這樣我就完成了對(duì)群集的封裝.此后,不通過(guò)Person提供的add/remove函數(shù),誰(shuí)也不能修改群集內(nèi)的元素.
將行為移到這個(gè)class中
我擁有了合理的接口.現(xiàn)在開始觀察取值函數(shù)(getter)的用戶,從中找出應(yīng)該屬于Person的代碼.下面這樣的代碼就應(yīng)該搬移到Person去: Iterator iter = person.getCourses().iterator(); int counter = 0; while(iter.hasNext()){ Course each = (Course)iter.next(); if(each.isAdvanced()) cout++; }
因?yàn)橐陨现皇褂昧藢儆?STRONG>Person的數(shù)據(jù).首先我使用Extract Method(110)將這段代碼提煉為一個(gè)獨(dú)立函數(shù): int numberOfAdvancedCourses(Person person) { Iterator iter = person.getCourses().iterator(); int count = 0; while(iter.hasNext()) { Course each = (Course)iter.next(); if(each.isAdvanced()) count++; } return count; } 然后使用Move Method(142)將這個(gè)函數(shù)搬移到Person中: class Person... int numberOfAdvancedCourses(Person person) { Iterator iter = person.getCourses().iterator(); int count = 0; while(iter.hasNext()) { Course each = (Course)iter.next(); if(each.isAdvanced()) count++; } return count; } 舉個(gè)常見例子,下列代碼: kent.getCourses().size(); 可以修改更具可讀性的樣子,像這樣:
kent.numberOfCourses();
class Person... public int numberOfCourses() { return _courses.size(); } 數(shù)年以前,我曾經(jīng)擔(dān)心將這樣的行為搬移到Person中會(huì)導(dǎo)致Person變得臃腫.但是在實(shí)際工作經(jīng)驗(yàn)中,我發(fā)現(xiàn)這通常并不成為問(wèn)題.
- 作法(Mechanics)
- 加入[為群集添加(add),移除(remove)元素]的函數(shù).
- 將[用以保存群集]的值域初始化為一個(gè)空群集.
- 編譯.
- 找出[群集設(shè)值函數(shù)]的所有調(diào)用者.你可以修改那個(gè)設(shè)值函數(shù),讓它使用上述新建立的[添加/移除元素]函數(shù);也可以直接修改調(diào)用端,改讓它們調(diào)用上述新建立的[添加/移除元素]函數(shù).
- ==>兩種情況下需要用到[群集設(shè)值函數(shù)];(1)群集為空時(shí);(2)準(zhǔn)備將原有群集替換為另一個(gè)群集時(shí).
- ==>你或許會(huì)想運(yùn)用Rename Method(273)為[群集設(shè)值函數(shù)]改名,從setXxx()改為initializeXxx()或replaceXxx().
- 編譯,測(cè)試.
- 找出所有[通過(guò)取值函數(shù)(getter)獲得群集并修改其內(nèi)容]的函數(shù).逐一修改這些函數(shù),讓它們改用[添加/移除](add/remove)函數(shù).每次修改后,編譯并測(cè)試.
- 修改完上述所有[通過(guò)取值函數(shù)(getter)獲得群集并修改群集內(nèi)容]的函數(shù)后,修改取值函數(shù)自身,使它返回該群集的一個(gè)只讀映件(read-only view).
- ==>在Java 2中,你可以使用Collection.unmodifiableXxx()得到該集群的只讀映件.
- ==>在Java 1.1中,你應(yīng)該返回群集的一份拷貝.
- 編譯,測(cè)試.
- 找出取值函數(shù)(getter)的所有用戶,從中找出應(yīng)該存在于[群集之宿主對(duì)象(host object)]內(nèi)的代碼.運(yùn)用Extract Method(110)和Move Method(142)將這些代碼移到宿主對(duì)象去.
- 如果你使用Java 2,那么本項(xiàng)重構(gòu)到此為止.如果你使用Java 1.1,那么用戶也許會(huì)喜歡使用枚舉(enumeration).為了提供這個(gè)枚舉,你應(yīng)該這樣做.
- 修改現(xiàn)有取值函數(shù)(getter)的名字,然后添加一個(gè)新取值函數(shù),使其返回一個(gè)枚舉.找出舊取值函數(shù)的所有被使用點(diǎn),將它們都改為使用新取值函數(shù).
- ==>如果這一步跨度太大,你可以先使用Rename Method(273)修改原取值函數(shù)的名稱;再建立一個(gè)新取值函數(shù)用以返回枚舉;最后再修改所有調(diào)用者,使其調(diào)用新取值函數(shù).
- 編譯,測(cè)試.
動(dòng)機(jī)(Motivation) class常常會(huì)使用群集(collection,可能是array,list,set或vector)來(lái)保存一組實(shí)體.這樣的class通常也會(huì)提供針對(duì)該群集[取值/設(shè)值函數(shù)](getter/setter).
但是,集群的處理方式應(yīng)該和其他種類的數(shù)據(jù)略有不同.取值函數(shù)(getter)不該返回群集自身,因?yàn)檫@將讓用戶得以修改群集內(nèi)容而群集擁有者卻一無(wú)所悉.這也會(huì)對(duì)用戶暴露過(guò)多[對(duì)象內(nèi)部數(shù)據(jù)結(jié)構(gòu)]的信息.如果一個(gè)取值函數(shù)(getter)確實(shí)需要返回多個(gè)值,它應(yīng)該避免用戶直接操作對(duì)象內(nèi)所保存的群集,并隱藏對(duì)象內(nèi)[與用戶無(wú)關(guān)]的數(shù)據(jù)結(jié)構(gòu).至于如何做到這一點(diǎn),視你使用的Java版本不同而有所不同.
另外,不應(yīng)該為這整個(gè)群集提供一個(gè)設(shè)值函數(shù)(setter),但應(yīng)該提供用以為群集添加/移除(add/remove)元素的函數(shù).這樣,群集擁有者(對(duì)象)就可以控制群集元素的添加和移除.
如果你做到以上數(shù)點(diǎn),群集(collection)就被很好地封裝起來(lái)了,這便可以降低群集擁有者(class)和用戶之間的耦合度.
有個(gè)函數(shù)( method)返回一個(gè)群集( collection). 讓這個(gè)函數(shù)返回該群集的一個(gè)只讀映件(read-only view),并在這個(gè)class中提供[添加/移除](add/remove)群集元素的函數(shù).
- 作法(Mechanics)
- 為public值域提供取值/設(shè)值函數(shù)(getter/setter).
- 找到這個(gè)class以外使用該值域的所有地點(diǎn).如果客戶只是使用該值域,就把引用動(dòng)作(reference)替換為對(duì)取值函數(shù)(getter)的調(diào)用];如果客戶修改了該值值域,就將此一引用點(diǎn)替換為[對(duì)設(shè)值函數(shù)(setter)的調(diào)用].
- ==>如果這個(gè)值域是個(gè)對(duì)象,而客戶只不過(guò)是調(diào)用該對(duì)象的某個(gè)函數(shù),那么不論該函數(shù)是否為修改函數(shù)(modifier,會(huì)改變對(duì)象狀態(tài)),都只能算是使用該值域.只有當(dāng)客戶為該值域賦值時(shí),才能將其替換為設(shè)值函數(shù)(setter).
- 每次修改之后,編譯并測(cè)試.
- 將值域的所有用戶修改完畢后,把值域聲明為private.
- 編譯,測(cè)試.
動(dòng)機(jī)(Motivation) 面向?qū)ο蟮氖滓瓌t之一就是封裝(encapsulation),或者稱為[數(shù)據(jù)隱藏](data hidding).
public數(shù)據(jù)被看做是一種不好的作法,因?yàn)檫@樣會(huì)降低程序的模塊化程度(modularity).如果數(shù)據(jù)和使用該數(shù)據(jù)的行為被集中在一起,一旦情況發(fā)生變化,代碼的修改就會(huì)比較簡(jiǎn)單,因?yàn)樾枰薷牡拇a都集中于同一塊地方,而不是星羅棋布地散落在整個(gè)程序中.
Encapsulate Field(206)是封裝過(guò)程的第一步.通過(guò)這項(xiàng)重構(gòu)手法,你可以將數(shù)據(jù)隱藏起來(lái),并提供相應(yīng)的訪問(wèn)函數(shù)(accessors).但它畢竟只是第一步.如果一個(gè)class除了訪問(wèn)函數(shù)(accessors)外不能提供其他行為,它終究只是一個(gè)dumb class(啞類).這樣的class并不能獲得對(duì)象技術(shù)的優(yōu)勢(shì),而你知道,浪費(fèi)如何一個(gè)對(duì)象都是很不好的.實(shí)施Encapsulate Field(206)之后,我會(huì)嘗試尋找那些使用[新建訪問(wèn)函數(shù)]的函數(shù),看看是否可以通過(guò)簡(jiǎn)單的Move Method(142)輕快地將它們移到新對(duì)象去.
你的class中存在一個(gè)public值域. 將它聲明為private,并提供相應(yīng)的訪問(wèn)函數(shù)(accessors). public String _name private String _name; public String getName() {return _name;} public void setName(String arg) {_name = arg;}
|