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

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