標籤:
rsa 具體是什麼 這個就不在多說。演算法實現啊應用啊 已經有很多了。今天記錄下 這種特殊的需求,前台簽名,後台驗證
Java後台產生 密匙對
pom.xml 添加BC 依賴
<dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15on</artifactId> <version>1.51</version> </dependency>
RSATools
package com.oscgc.securevideo.server.tool.rsa;import java.io.IOException;import java.io.StringWriter;import java.security.*;import java.security.interfaces.RSAPrivateKey;import java.security.interfaces.RSAPublicKey;import java.security.spec.PKCS8EncodedKeySpec;import java.security.spec.X509EncodedKeySpec;import org.bouncycastle.openssl.PEMWriter;import org.bouncycastle.util.io.pem.PemObject;/** * Created by Yq on 2015/6/10. */public class RsaKeyTools { public static final String PEM_PUBLICKEY = "PUBLIC KEY"; public static final String PEM_PRIVATEKEY = "PRIVATE KEY"; /** * generateRSAKeyPair * * @param keySize * @return */ public static KeyPair generateRSAKeyPair(int keySize) { KeyPairGenerator generator = null; SecureRandom random = new SecureRandom(); Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); try { generator = KeyPairGenerator.getInstance("RSA", "BC"); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (NoSuchProviderException e) { e.printStackTrace(); } generator.initialize(keySize, random); KeyPair keyPair = generator.generateKeyPair(); return keyPair; } /** * convertToPemKey * * @param publicKey * @param privateKey * @return */ public static String convertToPemKey(RSAPublicKey publicKey, RSAPrivateKey privateKey) { if (publicKey == null && privateKey == null) { return null; } StringWriter stringWriter = new StringWriter(); try { PEMWriter pemWriter = new PEMWriter(stringWriter, "BC"); if (publicKey != null) { pemWriter.writeObject(new PemObject(PEM_PUBLICKEY, publicKey.getEncoded())); } else {
//此處產生的privatekey 的格式是 PKCS#8 的格式 pemWriter.writeObject(new PemObject(PEM_PRIVATEKEY, privateKey.getEncoded())); } pemWriter.flush(); } catch (IOException e) { e.printStackTrace(); } return stringWriter.toString(); } public static byte[] sign(String data, byte[] privateKey) throws Exception { PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKey); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PrivateKey privateKey2 = keyFactory.generatePrivate(pkcs8EncodedKeySpec); Signature signature = Signature.getInstance("SHA1WithRSA"); signature.initSign(privateKey2); signature.update(data.getBytes()); return signature.sign(); } //後台測試簽名的時候 要和前台保持一致,所以需要將結果轉換 private static String bytes2String(byte[] bytes) { StringBuilder string = new StringBuilder(); for (byte b : bytes) { String hexString = Integer.toHexString(0x00FF & b); string.append(hexString.length() == 1 ? "0" + hexString : hexString); } return string.toString(); } public static boolean verify(String data, byte[] publicKey, byte[] signatureResult) { try { X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKey); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PublicKey publicKey2 = keyFactory.generatePublic(x509EncodedKeySpec); Signature signature = Signature.getInstance("SHA1WithRSA"); signature.initVerify(publicKey2); signature.update(data.getBytes()); return signature.verify(signatureResult); } catch (Exception e) { e.printStackTrace(); } return false; }
//前台的簽名結果是將byte 中的一些 負數轉換成了正數,
//但是後台驗證的方法需要的又必須是轉換之前的 public static byte[] hexStringToByteArray(String data) { int k = 0; byte[] results = new byte[data.length() / 2]; for (int i = 0; i + 1 < data.length(); i += 2, k++) { results[k] = (byte) (Character.digit(data.charAt(i), 16) << 4); results[k] += (byte) (Character.digit(data.charAt(i + 1), 16)); } return results; } public static void main(String[] args) { String str = "coder"; KeyPair k = generateRSAKeyPair(1024); String publicKey = convertToPemKey((RSAPublicKey) k.getPublic(), null); String privateKey = convertToPemKey(null, (RSAPrivateKey) k.getPrivate()); System.out.println("publicKey__\n" + publicKey); System.out.println("privateKey_\n" + privateKey); try { byte[] signautreResult = sign(str, k.getPrivate().getEncoded()); String signatureStr = bytes2String(signautreResult); byte[] signatureResult2 = hexStringToByteArray(signatureStr); boolean b = verify(str, k.getPublic().getEncoded(), signatureResult2); System.out.print("iii " + b); } catch (Exception e) { e.printStackTrace(); } } }
Javascript 簽名用到的lib 是 jsrsasign 包含:
- Signature - RSA/RSAPSS/ECDSA/DSA digital signtature class wrapper of Java JCE style
- MessageDigest - cryptographic hash calculation class wrapper of Java JCE style
- MAC - message authentication code hash calculation class wrapper of Java JCE style
- ASN.1 encoder/generator
- ASN.1 structure for X.509 ceritificate, CRL and CSR(PKCS#10) generation
- ASN.1 structure for CMS SignedData generation
- ASN.1 structure for RFC 3161 TimeStamp generation
- ASN.1 structure for RFC 5126 CAdES Long Term Signature generation
- simple ASN.1 data parser
- simple X.509 certificate parser/reader
- KEYUTIL - loading RSA/EC/DSA private/public key from PEM formatted PKCS#1/5/8 and X.509 certificate
- JSON Web Siguature(JWS), JSON Web Token(JWT) and JSON Web Key(JWK)
更多的詳細 github 地址:https://kjur.github.io/jsrsasign/
在官網給定的簽名例子代碼如下:
function doSign() { var rsa = new RSAKey(); rsa.readPrivateKeyFromPEMString(document.form1.prvkey1.value); var hashAlg = document.form1.hashalg.value; var hSig = rsa.signString(document.form1.msgsigned.value, hashAlg); document.form1.siggenerated.value = linebrk(hSig, 64);}
這裡我們需要改動一下:
rsa.readPrivateKeyFromPEMString(document.form1.prvkey1.value);
官方api 中對這個方法有這樣的說明:
readPrivateKeyFromPEMString(keyPEM)read PKCS#1 private key from a string
這個方法傳入的privatekey 是需要 PKCS#1 格式的,但是後台 產生出來的private key 是PKCS#8的格式的,這裡就不能用這個方法,簽名會通不過。
查看jsrsasign 的 api
KEYUTIL - loading RSA/EC/DSA private/public key from PEM formatted PKCS#1/5/8 and X.509 certificate
因此 js 產生RSAkey 對象
rsa=KEYUTIL.getKey(document.form1.prvkey1.value);
這個方法支援PKCS#8 pem 格式的privatekey 可以通過簽名。
jsrsasign 前台簽名,Java後台驗證前台簽名