常用数字签名算法RSA与DSA的Java程序内实现示例
RSA加密算法
我们来回顾一下RSA的加密算法。我们从公钥加密算法和签名算法的定义出发,用比较规范的语言来描述这一算法。
RSA公钥加密体制包含如下3个算法:KeyGen(密钥生成算法),Encrypt(加密算法)以及Decrypt(解密算法)。
密钥生成算法以安全常数作为输入,输出一个公钥PK,和一个私钥SK。安全常数用于确定这个加密算法的安全性有多高,一般以加密算法使用的质数p的大小有关。越大,质数p一般越大,保证体制有更高的安全性。在RSA中,密钥生成算法如下:算法首先随机产生两个不同大质数p和q,计算N=pq。随后,算法计算欧拉函数。接下来,算法随机选择一个小于的整数e,并计算e关于的模反元素d。最后,公钥为PK=(N,e),私钥为SK=(N,d)。
加密算法以公钥PK和待加密的消息M作为输入,输出密文CT。在RSA中,加密算法如下:算法直接输出密文为
解密算法以私钥SK和密文CT作为输入,输出消息M。在RSA中,解密算法如下:算法直接输出明文为。由于e和d在下互逆,因此我们有:
所以,从算法描述中我们也可以看出:公钥用于对数据进行加密,私钥用于对数据进行解密。当然了,这个也可以很直观的理解:公钥就是公开的密钥,其公开了大家才能用它来加密数据。私钥是私有的密钥,谁有这个密钥才能够解密密文。否则大家都能看到私钥,就都能解密,那不就乱套了。
下面就来看一下Java中的简单实现:
packagecom.stone.security; importjava.security.KeyPair; importjava.security.KeyPairGenerator; importjava.security.PrivateKey; importjava.security.PublicKey; importjava.util.Arrays; importjavax.crypto.Cipher; /** *RSA算法公钥加密非对称加密 */ publicclassRSA{ publicstaticfinalStringKEY_ALGORITHM="RSA"; publicstaticfinalStringCIPHER_ALGORITHM_ECB1="RSA/ECB/PKCS1Padding"; publicstaticfinalStringCIPHER_ALGORITHM_ECB2="RSA/ECB/OAEPWithSHA-1AndMGF1Padding";//不能用 publicstaticfinalStringCIPHER_ALGORITHM_ECB3="OAEPWithSHA-256AndMGF1Padding";//不能用 staticPublicKeypublicKey; staticPrivateKeyprivateKey; staticCiphercipher; staticKeyPairkeyPair; publicstaticvoidmain(String[]args)throwsException{ method1("斯柯达U*(Sfsad7f()*^%%$"); method2("斯柯达U*(Sfsad7f()*^%%$"); method3("斯柯达U*(Sfsad7f()*^%%$"); } /** *公钥加密,私钥解密使用默认CIPHER_ALGORITHM_ECB1 *@paramstr *@throwsException */ staticvoidmethod1(Stringstr)throwsException{ KeyPairGeneratorkeyGenerator=KeyPairGenerator.getInstance(KEY_ALGORITHM); KeyPairkeyPair=keyGenerator.generateKeyPair(); publicKey=keyPair.getPublic(); privateKey=keyPair.getPrivate(); cipher=Cipher.getInstance(KEY_ALGORITHM); cipher.init(Cipher.ENCRYPT_MODE,publicKey);//公钥加密 byte[]encrypt=cipher.doFinal(str.getBytes()); System.out.println("公钥加密后1:"+Arrays.toString(encrypt)); cipher.init(Cipher.DECRYPT_MODE,privateKey);//私钥解密 byte[]decrypt=cipher.doFinal(encrypt); System.out.println("私钥解密后1:"+newString(decrypt)); } /** *私钥加密,公钥解密使用默认CIPHER_ALGORITHM_ECB1 *@paramstr *@throwsException */ staticvoidmethod2(Stringstr)throwsException{ KeyPairGeneratorkeyGenerator=KeyPairGenerator.getInstance(KEY_ALGORITHM); KeyPairkeyPair=keyGenerator.generateKeyPair(); publicKey=keyPair.getPublic(); privateKey=keyPair.getPrivate(); cipher=Cipher.getInstance(KEY_ALGORITHM); cipher.init(Cipher.ENCRYPT_MODE,privateKey);//私钥加密 byte[]encrypt=cipher.doFinal(str.getBytes()); System.out.println("私钥加密后2:"+Arrays.toString(encrypt)); cipher.init(Cipher.DECRYPT_MODE,publicKey);//公钥解密 byte[]decrypt=cipher.doFinal(encrypt); System.out.println("公钥解密后2:"+newString(decrypt)); } /** *私钥加密,公钥解密使用CIPHER_ALGORITHM_ECB1=RSA/ECB/PKCS1Padding *@paramstr *@throwsException */ staticvoidmethod3(Stringstr)throwsException{ KeyPairGeneratorkeyGenerator=KeyPairGenerator.getInstance(KEY_ALGORITHM); KeyPairkeyPair=keyGenerator.generateKeyPair(); publicKey=keyPair.getPublic(); privateKey=keyPair.getPrivate(); cipher=Cipher.getInstance(CIPHER_ALGORITHM_ECB1); cipher.init(Cipher.ENCRYPT_MODE,privateKey);//私钥加密 byte[]encrypt=cipher.doFinal(str.getBytes()); System.out.println("私钥加密后3:"+Arrays.toString(encrypt)); cipher.init(Cipher.DECRYPT_MODE,publicKey);//公钥解密 byte[]decrypt=cipher.doFinal(encrypt); System.out.println("公钥解密后3:"+newString(decrypt)); } }
DSA算法和数字签名
DSA一般用于数字签名和认证。
DSA是Schnorr和ElGamal签名算法的变种,被美国NIST作为DSS(DigitalSignatureStandard)。
DSA是基于整数有限域离散对数难题的,其安全性与RSA相比差不多。
在DSA数字签名和认证中,发送者使用自己的私钥对文件或消息进行签名,接受者收到消息后使用发送者的公钥
来验证签名的真实性。DSA只是一种算法,和RSA不同之处在于它不能用作加密和解密,也不能进行密钥交换,
只用于签名,它比RSA要快很多.
packagecom.stone.security; importjava.security.Key; importjava.security.KeyFactory; importjava.security.KeyPair; importjava.security.KeyPairGenerator; importjava.security.PrivateKey; importjava.security.PublicKey; importjava.security.SecureRandom; importjava.security.Signature; importjava.security.spec.PKCS8EncodedKeySpec; importjava.security.spec.X509EncodedKeySpec; importjava.util.HashMap; importjava.util.Map; importsun.misc.BASE64Decoder; importsun.misc.BASE64Encoder; /** *DSA-DigitalSignatureAlgorithm是Schnorr和ElGamal签名算法的变种,被美国NIST作为DSS(DigitalSignatureStandard)。 *简单的说,这是一种更高级的验证方式,用作数字签名。不单单只有公钥、私钥,还有数字签名。私钥加密生成数字签名,公钥验证数据及签名。 *如果数据和签名不匹配则认为验证失败!即传输中的数据可以不再加密,接收方获得数据后,拿到公钥与签名验证数据是否有效 */ publicclassDSA{ /** *不仅可以使用DSA算法,同样也可以使用RSA算法做数字签名 */ publicstaticfinalStringKEY_ALGORITHM="RSA"; publicstaticfinalStringSIGNATURE_ALGORITHM="MD5withRSA";*/ publicstaticfinalStringKEY_ALGORITHM="DSA"; publicstaticfinalStringSIGNATURE_ALGORITHM="DSA"; publicstaticfinalStringDEFAULT_SEED="$%^*%^()(HJG8awfjas7";//默认种子 publicstaticfinalStringPUBLIC_KEY="DSAPublicKey"; publicstaticfinalStringPRIVATE_KEY="DSAPrivateKey"; publicstaticvoidmain(String[]args)throwsException{ Stringstr="!@#$!#^$#&ZXVDF呆军工路爱着你*()_+"; byte[]data=str.getBytes(); Map<String,Object>keyMap=initKey();//构建密钥 PublicKeypublicKey=(PublicKey)keyMap.get(PUBLIC_KEY); PrivateKeyprivateKey=(PrivateKey)keyMap.get(PRIVATE_KEY); System.out.println("私钥format:"+privateKey.getFormat()); System.out.println("公钥format:"+publicKey.getFormat()); //产生签名 Stringsign=sign(data,getPrivateKey(keyMap)); //验证签名 booleanverify1=verify("aaa".getBytes(),getPublicKey(keyMap),sign); System.err.println("经验证数据和签名匹配:"+verify1); booleanverify=verify(data,getPublicKey(keyMap),sign); System.err.println("经验证数据和签名匹配:"+verify); } /** *生成密钥 * *@paramseed种子 *@return密钥对象 *@throwsException */ publicstaticMap<String,Object>initKey(Stringseed)throwsException{ System.out.println("生成密钥"); KeyPairGeneratorkeygen=KeyPairGenerator.getInstance(KEY_ALGORITHM); SecureRandomsecureRandom=newSecureRandom(); secureRandom.setSeed(seed.getBytes()); //Modulussizemustrangefrom512to1024andbeamultipleof64 keygen.initialize(640,secureRandom); KeyPairkeys=keygen.genKeyPair(); PrivateKeyprivateKey=keys.getPrivate(); PublicKeypublicKey=keys.getPublic(); Map<String,Object>map=newHashMap<String,Object>(2); map.put(PUBLIC_KEY,publicKey); map.put(PRIVATE_KEY,privateKey); returnmap; } /** *生成默认密钥 * *@return密钥对象 *@throwsException */ publicstaticMap<String,Object>initKey()throwsException{ returninitKey(DEFAULT_SEED); } /** *取得私钥 * *@paramkeyMap *@return *@throwsException */ publicstaticStringgetPrivateKey(Map<String,Object>keyMap)throwsException{ Keykey=(Key)keyMap.get(PRIVATE_KEY); returnencryptBASE64(key.getEncoded());//base64加密私钥 } /** *取得公钥 * *@paramkeyMap *@return *@throwsException */ publicstaticStringgetPublicKey(Map<String,Object>keyMap)throwsException{ Keykey=(Key)keyMap.get(PUBLIC_KEY); returnencryptBASE64(key.getEncoded());//base64加密公钥 } /** *用私钥对信息进行数字签名 *@paramdata加密数据 *@paramprivateKey私钥-base64加密的 *@return *@throwsException */ publicstaticStringsign(byte[]data,StringprivateKey)throwsException{ System.out.println("用私钥对信息进行数字签名"); byte[]keyBytes=decryptBASE64(privateKey); PKCS8EncodedKeySpeckeySpec=newPKCS8EncodedKeySpec(keyBytes); KeyFactoryfactory=KeyFactory.getInstance(KEY_ALGORITHM); PrivateKeypriKey=factory.generatePrivate(keySpec);//生成私钥 //用私钥对信息进行数字签名 Signaturesignature=Signature.getInstance(SIGNATURE_ALGORITHM); signature.initSign(priKey); signature.update(data); returnencryptBASE64(signature.sign()); } /** *BASE64Encoder加密 *@paramdata要加密的数据 *@return加密后的字符串 */ privatestaticStringencryptBASE64(byte[]data){ BASE64Encoderencoder=newBASE64Encoder(); Stringencode=encoder.encode(data); returnencode; } /** *BASE64Decoder解密 *@paramdata要解密的字符串 *@return解密后的byte[] *@throwsException */ privatestaticbyte[]decryptBASE64(Stringdata)throwsException{ BASE64Decoderdecoder=newBASE64Decoder(); byte[]buffer=decoder.decodeBuffer(data); returnbuffer; } /** *校验数字签名 *@paramdata加密数据 *@parampublicKey *@paramsign数字签名 *@return *@throwsException */ publicstaticbooleanverify(byte[]data,StringpublicKey,Stringsign)throwsException{ byte[]keyBytes=decryptBASE64(publicKey); X509EncodedKeySpeckeySpec=newX509EncodedKeySpec(keyBytes); KeyFactorykeyFactory=KeyFactory.getInstance(KEY_ALGORITHM); PublicKeypubKey=keyFactory.generatePublic(keySpec); Signaturesignature=Signature.getInstance(SIGNATURE_ALGORITHM); signature.initVerify(pubKey); signature.update(data); returnsignature.verify(decryptBASE64(sign));//验证签名 } }