證書(Certificate,也稱public-key certificate)是用某種簽名算法對某些內(nèi)容(比如公鑰)進行數(shù)字簽名后得到的、可以用來當(dāng)成信任關(guān)系中介的數(shù)字憑證。證書發(fā)行機構(gòu)通過發(fā)行證書告知證書使用者或?qū)嶓w其公鑰(public-key)以及其它一些輔助信息。證書在電子商務(wù)安全交易中有著廣泛的應(yīng)用,證書發(fā)行機構(gòu)也稱CA(Certificate Authority)。
創(chuàng)新互聯(lián)公司是專業(yè)的湟中網(wǎng)站建設(shè)公司,湟中接單;提供成都做網(wǎng)站、成都網(wǎng)站設(shè)計,網(wǎng)頁設(shè)計,網(wǎng)站設(shè)計,建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進行湟中網(wǎng)站開發(fā)網(wǎng)頁制作和功能擴展;專業(yè)做搜索引擎喜愛的網(wǎng)站,專業(yè)的做網(wǎng)站團隊,希望更多企業(yè)前來合作!
應(yīng)用證書
證書在公鑰加密應(yīng)用中的作用是保證公鑰在某些可信的機構(gòu)發(fā)布,其在協(xié)議SSL、電子交易協(xié)議SET等方面有重要的應(yīng)用。圖1顯示了一個最簡單的證書應(yīng)用方法:
圖1 證書應(yīng)用方法
證書的應(yīng)用步驟是:
(1) A把自己的公鑰PKA送到CA(Certificate Authority);
(2) CA用自己的私鑰和A的公鑰生成A的證書,證書內(nèi)包括CA的數(shù)字簽名。簽名對象包括需要在證書中說明的內(nèi)容,比如A的公鑰、時間戳、序列號等,為了簡化這里不妨假設(shè)證書中只有三項內(nèi)容:A的公鑰PKA、時間戳TIME1、序列號IDA。那么CA發(fā)送給A的簡單證書憑證可表達(dá)為:CertA=Eca[TIME1,IDA,PKA];
(3) B同樣把自己的公鑰PKB送到CA;
(4) B得到CA發(fā)布的證書CertB;
(5) A告知B證書CertA;
(6) B告知A證書CertB。
A、B各自得到對方證書后,利用從CA得到的公鑰(在CA的自簽證書中)驗證彼此對方的證書是否有效,如果有效,那么就得到了彼此的公鑰。利用對方的公鑰,可以加密數(shù)據(jù),也可以用來驗證對方的數(shù)字簽名。
本文為了方便說明,并沒有使用從CA獲得的證書,而是通信雙方各自產(chǎn)生自簽證書,也就是說圖1的A和B并沒有經(jīng)過CA,不過前提是A和B之間是互相擁有對方的證書。
肯定可以,這個跟語言是無關(guān)的
Python上RSA加密的庫挺多的,最開始使用的是rsa,因為比較簡單嘛!測試的時候也是用 python模擬App的訪問,順利通過!
然而App開發(fā)者反饋,python測試腳本沒法移植到j(luò)ava上,因為java的加密解密模塊需要更加精細(xì)的算法細(xì)節(jié)指定,否則java加密過的數(shù)據(jù)python是解不出來的。
當(dāng)初就是因為rsa模塊簡單,不需要注重細(xì)節(jié)才選的,自己又不是專業(yè)搞加密解密的。沒辦法了,只能硬著頭皮,捋了一遍RSA的加密原理。網(wǎng)上還是有比較多的講述比較好的文章,比如RSA算法原理
原理是懂了,但具體到python和java的區(qū)別上,還是一頭霧水。最終python的RSA模塊換成Crypto,因為支持的參數(shù)比較多。搜了很多網(wǎng)站講的都不是很詳細(xì),stackflow上有幾篇還可以,借鑒了一下,最后測試通過了。還是直接上代碼吧。
Java代碼
//下面這行指定了RSA算法的細(xì)節(jié),必須更python對應(yīng)
private static String RSA_CONFIGURATION = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding";
//這個貌似需要安裝指定的provider模塊,這里沒有使用
private static String RSA_PROVIDER = "BC";
//解密 Key:私鑰
public static String decrypt(Key key, String encryptedString){
try {
Cipher c = Cipher.getInstance(RSA_CONFIGURATION);
c.init(Cipher.DECRYPT_MODE, key, new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256,
PSource.PSpecified.DEFAULT));
byte[] decodedBytes;
decodedBytes = c.doFinal(Base64.decode(encryptedString.getBytes("UTF-8")));
return new String(decodedBytes, "UTF-8");
} catch (IllegalBlockSizeException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (BadPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Base64DecodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvalidKeyException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
//加密 Key一般是公鑰
public static String encrypt(Key key, String toBeEncryptedString){
try {
Cipher c = Cipher.getInstance(RSA_CONFIGURATION);
c.init(Cipher.ENCRYPT_MODE, key, new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256,
PSource.PSpecified.DEFAULT));
byte[] encodedBytes;
encodedBytes = c.doFinal(toBeEncryptedString.getBytes("UTF-8"));
return Base64.encode(encodedBytes);
} catch (IllegalBlockSizeException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (BadPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvalidKeyException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
//通過Pem格式的字符串(PKCS8)生成私鑰,base64是去掉頭和尾的b64編碼的字符串
//Pem格式私鑰一般有2種規(guī)范:PKCS8和PKCS1.注意java在生成私鑰時的不同
static PrivateKey generatePrivateKeyFromPKCS8(String base64)
{
byte[] privateKeyBytes;
try {
privateKeyBytes = Base64.decode(base64.getBytes("UTF-8"));
KeyFactory kf = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec ks = new PKCS8EncodedKeySpec(privateKeyBytes);
PrivateKey privateKey = kf.generatePrivate(ks);
return privateKey;
} catch (Base64DecodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvalidKeySpecException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
//通過Pem格式的字符串(PKCS1)生成私鑰,base64是去掉頭和尾的b64編碼的字符串
static PrivateKey generatePrivateKeyFromPKCS1(String base64)
{
byte[] privateKeyBytes;
try {
privateKeyBytes = Base64.decode(base64.getBytes("UTF-8"));
KeyFactory kf = KeyFactory.getInstance("RSA");
X509EncodedKeySpec ks = new X509EncodedKeySpec(privateKeyBytes);
PrivateKey privateKey = kf.generatePrivate(ks);
return privateKey;
} catch (Base64DecodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvalidKeySpecException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
//通過Pem格式的字符串(PKCS1)生成公鑰,base64是去掉頭和尾的b64編碼的字符串
//Pem格式公鑰一般采用PKCS1格式
static PublicKey generatePublicKeyFromPKCS1(String base64)
{
byte[] publicKeyBytes;
try {
publicKeyBytes = Base64.decode(base64.getBytes("UTF-8"));
KeyFactory kf = KeyFactory.getInstance("RSA");
X509EncodedKeySpec ks = new X509EncodedKeySpec(publicKeyBytes);
PublicKey publicKey = kf.generatePublic(ks);
return publicKey;
} catch (Base64DecodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvalidKeySpecException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
//通過modulus和exponent生成公鑰
//參數(shù)含義就是RSA算法里的意思
public static RSAPublicKey getPublicKey(String modulus, String exponent) {
try {
BigInteger b1 = new BigInteger(modulus);
BigInteger b2 = new BigInteger(exponent);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(b1, b2);
return (RSAPublicKey) keyFactory.generatePublic(keySpec);
} catch (Exception e) {
e.printStackTrace();
return null;
}
} 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
Python 代碼
from Config import config
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
key = RSA.generate(1024)
pubkey = key.publickey().key
def Decrypt(prikey,data):
try:
cipher = PKCS1_OAEP.new(prikey, hashAlgo=SHA256)
return cipher.decrypt(data)
except:
traceback.print_exc()
return None
def Encrypt(pubkey,data):
try:
cipher = PKCS1_OAEP.new(pubkey, hashAlgo=SHA256)
return cipher.encrypt(data)
except:
traceback.print_exc()
return None
1234567891011121314151617181920212223
總結(jié)
主要是對RSA算法不是很熟悉,其中很多術(shù)語不懂,導(dǎo)致跟java里的加密模塊的函數(shù)和類對應(yīng)不上。
RSA算法的細(xì)節(jié)到現(xiàn)在也是一知半解,但真的沒時間去深入學(xué)習(xí)了。
一:需要包含的包
import java.security.*;
import java.io.*;
import java.util.*;
import java.security.*;
import java.security.cert.*;
import sun.security.x509.*
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
二:從文件中讀取證書
用keytool將.keystore中的證書寫入文件中,然后從該文件中讀取證書信息
CertificateFactory cf=CertificateFactory.getInstance("X.509");
FileInputStream in=new FileInputStream("out.csr");
Certificate c=cf.generateCertificate(in);
String s=c.toString();
三:從密鑰庫中直接讀取證書
String pass="123456";
FileInputStream in=new FileInputStream(".keystore");
KeyStore ks=KeyStore.getInstance("JKS");
ks.load(in,pass.toCharArray());
java.security.cert.Certificate c=ks.getCertificate(alias);//alias為條目的別名
四:JAVA程序中顯示證書指定信息
System.out.println("輸出證書信息:\n"+c.toString());
System.out.println("版本號:"+t.getVersion());
System.out.println("序列號:"+t.getSerialNumber().toString(16));
System.out.println("主體名:"+t.getSubjectDN());
System.out.println("簽發(fā)者:"+t.getIssuerDN());
System.out.println("有效期:"+t.getNotBefore());
System.out.println("簽名算法:"+t.getSigAlgName());
byte [] sig=t.getSignature();//簽名值
PublicKey pk=t.getPublicKey();
byte [] pkenc=pk.getEncoded();
System.out.println("公鑰");
for(int i=0;ipkenc.length;i++)System.out.print(pkenc[i]+",");
五:JAVA程序列出密鑰庫所有條目
String pass="123456";
FileInputStream in=new FileInputStream(".keystore");
KeyStore ks=KeyStore.getInstance("JKS");
ks.load(in,pass.toCharArray());
Enumeration e=ks.aliases();
while(e.hasMoreElements())
java.security.cert.Certificate c=ks.getCertificate((String)e.nextElement());
六:JAVA程序修改密鑰庫口令
String oldpass="123456";
String newpass="654321";
FileInputStream in=new FileInputStream(".keystore");
KeyStore ks=KeyStore.getInstance("JKS");
ks.load(in,oldpass.toCharArray());
in.close();
FileOutputStream output=new FileOutputStream(".keystore");
ks.store(output,newpass.toCharArray());
output.close();
七:JAVA程序修改密鑰庫條目的口令及添加條目
FileInputStream in=new FileInputStream(".keystore");
KeyStore ks=KeyStore.getInstance("JKS");
ks.load(in,storepass.toCharArray());
Certificate [] cchain=ks.getCertificate(alias);獲取別名對應(yīng)條目的證書鏈
PrivateKey pk=(PrivateKey)ks.getKey(alias,oldkeypass.toCharArray());獲取別名對應(yīng)條目的私鑰
ks.setKeyEntry(alias,pk,newkeypass.toCharArray(),cchain);向密鑰庫中添加條目
第一個參數(shù)指定所添加條目的別名,假如使用已存在別名將覆蓋已存在條目,使用新別名將增加一個新條目,第二個參數(shù)為條目的私鑰,第三個為設(shè)置的新口令,第四個為該私鑰的公鑰的證書鏈
FileOutputStream output=new FileOutputStream("another");
ks.store(output,storepass.toCharArray())將keystore對象內(nèi)容寫入新文件
八:JAVA程序檢驗別名和刪除條目
FileInputStream in=new FileInputStream(".keystore");
KeyStore ks=KeyStore.getInstance("JKS");
ks.load(in,storepass.toCharArray());
ks.containsAlias("sage");檢驗條目是否在密鑰庫中,存在返回true
ks.deleteEntry("sage");刪除別名對應(yīng)的條目
FileOutputStream output=new FileOutputStream(".keystore");
ks.store(output,storepass.toCharArray())將keystore對象內(nèi)容寫入文件,條目刪除成功
九:JAVA程序簽發(fā)數(shù)字證書
(1)從密鑰庫中讀取CA的證書
FileInputStream in=new FileInputStream(".keystore");
KeyStore ks=KeyStore.getInstance("JKS");
ks.load(in,storepass.toCharArray());
java.security.cert.Certificate c1=ks.getCertificate("caroot");
(2)從密鑰庫中讀取CA的私鑰
PrivateKey caprk=(PrivateKey)ks.getKey(alias,cakeypass.toCharArray());
(3)從CA的證書中提取簽發(fā)者的信息
byte[] encod1=c1.getEncoded(); 提取CA證書的編碼
X509CertImpl cimp1=new X509CertImpl(encod1); 用該編碼創(chuàng)建X509CertImpl類型對象
X509CertInfo cinfo1=(X509CertInfo)cimp1.get(X509CertImpl.NAME+"."+X509CertImpl.INFO); 獲取X509CertInfo對象
X500Name issuer=(X500Name)cinfo1.get(X509CertInfo.SUBJECT+"."+CertificateIssuerName.DN_NAME); 獲取X509Name類型的簽發(fā)者信息
(4)獲取待簽發(fā)的證書
CertificateFactory cf=CertificateFactory.getInstance("X.509");
FileInputStream in2=new FileInputStream("user.csr");
java.security.cert.Certificate c2=cf.generateCertificate(in);
(5)從待簽發(fā)的證書中提取證書信息
byte [] encod2=c2.getEncoded();
X509CertImpl cimp2=new X509CertImpl(encod2); 用該編碼創(chuàng)建X509CertImpl類型對象
X509CertInfo cinfo2=(X509CertInfo)cimp2.get(X509CertImpl.NAME+"."+X509CertImpl.INFO); 獲取X509CertInfo對象
(6)設(shè)置新證書有效期
Date begindate=new Date(); 獲取當(dāng)前時間
Date enddate=new Date(begindate.getTime()+3000*24*60*60*1000L); 有效期為3000天
CertificateValidity cv=new CertificateValidity(begindate,enddate); 創(chuàng)建對象
cinfo2.set(X509CertInfo.VALIDITY,cv); 設(shè)置有效期
(7)設(shè)置新證書序列號
int sn=(int)(begindate.getTime()/1000); 以當(dāng)前時間為序列號
CertificateSerialNumber csn=new CertificateSerialNumber(sn);
cinfo2.set(X509CertInfo.SERIAL_NUMBER,csn);
(8)設(shè)置新證書簽發(fā)者
cinfo2.set(X509CertInfo.ISSUER+"."+CertificateIssuerName.DN_NAME,issuer);應(yīng)用第三步的結(jié)果
(9)設(shè)置新證書簽名算法信息
AlgorithmId algorithm=new AlgorithmId(AlgorithmId.md5WithRSAEncryption_oid);
cinfo2.set(CertificateAlgorithmId.NAME+"."+CertificateAlgorithmId.ALGORITHM,algorithm);
(10)創(chuàng)建證書并使用CA的私鑰對其簽名
X509CertImpl newcert=new X509CertImpl(cinfo2);
newcert.sign(caprk,"MD5WithRSA"); 使用CA私鑰對其簽名
(11)將新證書寫入密鑰庫
ks.setCertificateEntry("lf_signed",newcert);
FileOutputStream out=new FileOutputStream("newstore");
ks.store(out,"newpass".toCharArray()); 這里是寫入了新的密鑰庫,也可以使用第七條來增加條目
十:數(shù)字證書的檢驗
(1)驗證證書的有效期
(a)獲取X509Certificate類型對象
CertificateFactory cf=CertificateFactory.getInstance("X.509");
FileInputStream in1=new FileInputStream("aa.crt");
java.security.cert.Certificate c1=cf.generateCertificate(in1);
X509Certificate t=(X509Certificate)c1;
in2.close();
(b)獲取日期
Date TimeNow=new Date();
(c)檢驗有效性
try{
t.checkValidity(TimeNow);
System.out.println("OK");
}catch(CertificateExpiredException e){ //過期
System.out.println("Expired");
System.out.println(e.getMessage());
}catch((CertificateNotYetValidException e){ //尚未生效
System.out.println("Too early");
System.out.println(e.getMessage());}
(2)驗證證書簽名的有效性
(a)獲取CA證書
CertificateFactory cf=CertificateFactory.getInstance("X.509");
FileInputStream in2=new FileInputStream("caroot.crt");
java.security.cert.Certificate cac=cf.generateCertificate(in2);
in2.close();
(c)獲取CA的公鑰
PublicKey pbk=cac.getPublicKey();
(b)獲取待檢驗的證書(上步已經(jīng)獲取了,就是C1)
(c)檢驗證書
boolean pass=false;
try{
c1.verify(pbk);
pass=true;
}catch(Exception e){
pass=false;
System.out.println(e);
}
下面產(chǎn)生一個自簽證書。安裝完J2SDK(這里用的是J2SDK1.4)后,在J2SDK安裝目錄的bin目錄下,有一個keytool的可執(zhí)行程序。利用keytool產(chǎn)生自簽證書的步驟如下:
第一步,用-genkey命令選項,產(chǎn)生公私密鑰對。在控制臺界面輸入:keytool -genkey -alias testkeypair -keyalg RSA -keysize 1024 -sigalg MD5withRSA。這里的-alias表示使用這對公私密鑰產(chǎn)生新的keystore入口的別名(keystore是用來存放管理密鑰對和證書鏈的,缺省位置是在使用者主目錄下,以.keystore為名的隱藏文件,當(dāng)然也可指定某個路徑存放.keystore文件);-keyalg是產(chǎn)生公私鑰對所用的算法,這里是RSA;-keysize定義密鑰的長度;-sigalg是簽名算法,選擇MD5withRSA,即用RSA簽名,然后用MD5哈希算法摘要。接下來,系統(tǒng)會提示進行一些輸入:
輸入keystore密碼: abc123
您的名字與姓氏是什么?
[Unknown]: Li
您的組織單位名稱是什么?
nbs
問題的其他解決辦法參考 :
;t=JAVA+
Java2下Applet數(shù)字簽名實現(xiàn)過程如下:
在代碼的分發(fā)端:
(1)開發(fā)Java源程序并對其進行編譯。
(2)用JAR工具對類文件和資源文件進行封裝。
(3)用keytool創(chuàng)建公鑰和密鑰,生成X。509V1簽名證書,輸出證書。
(4)通過jarsigner工具用生成的密鑰對JAR文件進行數(shù)字簽名。
在代碼的接收端:
(1)用keytool輸入證書視其為可信任。
(2)用policytool創(chuàng)建和修改安全性策略配置文件,授權(quán)請求的訪問權(quán)限。
(3)從網(wǎng)絡(luò)取得字節(jié)碼,用公鑰驗證數(shù)字簽名證書和文檔代碼的完整性。
(4)驗證字節(jié)碼的合法性,根據(jù)策略文件分配相應(yīng)權(quán)限。
(5)執(zhí)行代碼,完成后被垃圾回收器回收內(nèi)存。
在用公鑰驗證數(shù)字簽名證書之前,接收方需要確認(rèn)公鑰自身的可靠性,因此通常情況是提供一個包含公鑰的證書而不是公鑰自身。1個證書包括:
(1)1個公鑰。
(2)1個唯一的名字實體(個人或公司),它是證書的所有者,包含用戶名字、公司、組織、城市、地址、國家代碼、省份等信息。
(3)數(shù)字簽名:1個證書被1個分發(fā)者的實體簽名,保證證書確實包含另1個實體(所有者)的公鑰。
客戶端和服務(wù)端都指定編碼格式了嗎?
注意編碼格式,UFt8和gbk等 每種編碼格式獲得的字節(jié)數(shù)是不一樣的,