I am using iaik pkcs11 java trying to wrap an AES key with RSA public key.
Can someone please help me what is the issue with above params/code?
I am trying to wrap an AES key with RSA public key. Getting issue as Mechanism invalid
Mechanism mechanism = Mechanism.get(PKCS11Constants.CKM_RSA_PKCS_OAEP);
RSAPkcsOaepParameters rsaPkcsOaepParameters =
new RSAPkcsOaepParameters(
Mechanism.get(PKCS11Constants.CKM_SHA256),
RSAPkcsParameters.MessageGenerationFunctionType.SHA256,
RSAPkcsOaepParameters.SourceType.EMPTY,
null);
mechanism.setParameters(rsaPkcsOaepParameters);
session.wrapKey(mechanism, wrappingKey, keyToWrap);
wrappingKey is an RSA public key handle and keyToWrap is an AES key
iaik.pkcs.pkcs11.wrapper.PKCS11Exception: CKR_MECHANISM_PARAM_INVALID
at iaik.pkcs.pkcs11.wrapper.PKCS11Implementation.C_WrapKey(Native Method) ~[iaik-pkcs11-wrapper-1.6.4.jar:?]
at iaik.pkcs.pkcs11.Session.wrapKey(Session.java:1433) ~[iaik-pkcs11-wrapper-1.6.4.jar:?]
As far as I know you should use RSAPkcsOaepParameters.SourceType.DATA_SPECIFIED (CKZ_DATA_SPECIFIED as specified in PKCS#11):
public static Mechanism getRsaOaepSha256Mechanism() {
Mechanism mechanism = Mechanism.get(PKCS11Constants.CKM_RSA_PKCS_OAEP);
RSAPkcsOaepParameters rsaPkcsOaepParameters =
new RSAPkcsOaepParameters(
Mechanism.get(PKCS11Constants.CKM_SHA256),
RSAPkcsParameters.MessageGenerationFunctionType.SHA256,
RSAPkcsOaepParameters.SourceType.DATA_SPECIFIED,
null);
mechanism.setParameters(rsaPkcsOaepParameters);
return mechanism;
}
Good luck with your project!
Related
I'm reverse engineering some code which is decrypting data, hoping I'll be able to encrypt it back and obtain the same data it started with, for reasons that would make this question too long and off-topic.
public void Test() throws Exception {
String pk_enc = //...
String hashStr_64 = //...
byte[] hashStr_encrypted = Base64.decode(hashStr_64);
X509EncodedKeySpec e = new X509EncodedKeySpec(Base64.decode(pk_enc));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
RSAPublicKey RSApublicKey = (RSAPublicKey) keyFactory.generatePublic(e);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
cipher.init(2, RSApublicKey); // '2' means decrypt
byte[] hashStr_decrypted = cipher.doFinal(hashStr_encrypted);
String hashStr_result = new String(hashStr_decrypted);
// Now in reverse...
Cipher cipher1 = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
// instantiating a new cipher or using the original one makes no difference
cipher1.init(1, RSApublicKey); // '1' means encrypt
byte[] hashStr_encrypted_reverse = cipher1.doFinal(hashStr_decrypted);
String hashStr_64_reverse = Base64.encode(hashStr_encrypted_reverse);
}
All the code before // Now in reverse... cannot be changed, but that doesn't mean it's impossible to convert hashStr_result back to hashStr_64, right?
However, the code I've wrote after, that should do just that, doesn't work.
hashStr_encrypted_reverse is different from hashStr_encrypted. Why is that and how can I fix it?
Another sign that something went wrong in the encryption is what happens if I try to decrypt again...
// Decrypt again
Cipher cipher2 = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
cipher2.init(2, RSApublicKey);
byte[] hashStr_decrypted_again = cipher.doFinal(hashStr_encrypted_reverse);
This throws:
javax.crypto.BadPaddingException
I don't really care, but maybe it could help answer the question.
Terminology will be confusing. There are 4 RSA operations, best described as: signing, verifying, encrypting, decrypting. Mapping these to a lower-level and using only the language of encryption and decryption, these map as follows:
sign-verify pair
signing -> encrypt with private key
verifying -> decrypt with public key
encrypt-decrypt pair
encrypting -> encrypt with public key
decrypting -> decrypt with private key.
As you can see, each pair of operations has the private key on one side and the public key on the other.
As #JamesKPolk said in his comment, this isn't how RSA works. RSA is an asymmetric encryption algorithm: there are two keys, public and private. A symmetric algorithm (e.g., AES) has a single key, which is used for both encryption and decryption, and that key must be kept safe, except to the sending and receiving parties.
Why asymmetric encryption?
You can encrypt with a public key (typically someone else's key that they've shared with you), and they must use their private key to decrypt it. Anyone can have the public key (that's why it's public), but it cannot be used to read the encrypted message. This is where you are having your problem.
Also, you can encrypt a message with a private key (typically your own), and anyone else can use your public key to decrypt it. This is how digital signatures are implemented: for example, you would encrypt a digest of a document, and anyone can verify your signature if they have your public key, but no one else could have signed it.
I need to generate public/private key for RSA algorithm on IOS device and send public key to server with encrypted text. Server must read public key and decrypt user message.
I have code on swift:
func generateKeys(){
var publicKey: SecKey?
var privateKey: SecKey?
let publicKeyAttr: [NSObject: NSObject] = [kSecAttrIsPermanent:true as NSObject, kSecAttrApplicationTag:"publicTag".data(using: String.Encoding.utf8)! as NSObject]
let privateKeyAttr: [NSObject: NSObject] = [kSecAttrIsPermanent:true as NSObject, kSecAttrApplicationTag:"privateTag".data(using: String.Encoding.utf8)! as NSObject]
var keyPairAttr = [NSObject: NSObject]()
keyPairAttr[kSecAttrKeyType] = kSecAttrKeyTypeRSA
keyPairAttr[kSecAttrKeySizeInBits] = 4096 as NSObject
keyPairAttr[kSecPublicKeyAttrs] = publicKeyAttr as NSObject
keyPairAttr[kSecPrivateKeyAttrs] = privateKeyAttr as NSObject
_ = SecKeyGeneratePair(keyPairAttr as CFDictionary, &publicKey, &privateKey)
var error:Unmanaged<CFError>?
if #available(iOS 10.0, *) {
if let cfdata = SecKeyCopyExternalRepresentation(publicKey!, &error) {
let data:Data = cfdata as Data
let b64Key = data.base64EncodedString(options: .lineLength64Characters)
print("public base 64 : \n\(b64Key)")
}
if let cfdata = SecKeyCopyExternalRepresentation(privateKey!, &error) {
let data:Data = cfdata as Data
let b64Key = data.base64EncodedString(options: .lineLength64Characters)
print("private base 64 : \n\(b64Key)")
}
}
let encrypted = encryptBase64(text: "test", key: publicKey!)
let decrypted = decpryptBase64(encrpted: encrypted, key: privateKey!)
print("decrypted \(String(describing: decrypted))")
self.dismiss(animated: true, completion: nil);
}
func encryptBase64(text: String, key: SecKey) -> String {
let plainBuffer = [UInt8](text.utf8)
var cipherBufferSize : Int = Int(SecKeyGetBlockSize(key))
var cipherBuffer = [UInt8](repeating:0, count:Int(cipherBufferSize))
// Encrypto should less than key length
let status = SecKeyEncrypt(key, SecPadding.PKCS1, plainBuffer, plainBuffer.count, &cipherBuffer, &cipherBufferSize)
if (status != errSecSuccess) {
print("Failed Encryption")
}
let mudata = NSData(bytes: &cipherBuffer, length: cipherBufferSize)
return mudata.base64EncodedString()
}
func decpryptBase64(encrpted: String, key: SecKey) -> String? {
let data : NSData = NSData(base64Encoded: encrpted, options: .ignoreUnknownCharacters)!
let count = data.length / MemoryLayout<UInt8>.size
var array = [UInt8](repeating: 0, count: count)
data.getBytes(&array, length:count * MemoryLayout<UInt8>.size)
var plaintextBufferSize = Int(SecKeyGetBlockSize(key))
var plaintextBuffer = [UInt8](repeating:0, count:Int(plaintextBufferSize))
let status = SecKeyDecrypt(key, SecPadding.PKCS1, array, plaintextBufferSize, &plaintextBuffer, &plaintextBufferSize)
if (status != errSecSuccess) {
print("Failed Decrypt")
return nil
}
return NSString(bytes: &plaintextBuffer, length: plaintextBufferSize, encoding: String.Encoding.utf8.rawValue)! as String
}
This code returns public key in PKCS1. I found the library: SwCrypt
This code helps me to convert PKCS1 into PKCS8 and read public key with java
SwKeyConvert.PublicKey.pemToPKCS1DER(publicKeyPEM)
But I can't decrypt user message. Can you help me with message decryption? I wrote small unit test.
import org.junit.Test;
import javax.crypto.Cipher;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import static org.junit.Assert.assertNotNull;
public class TestExample {
String publicKeyContent = "MIMAAiMwDQYJKoZIhvcNAQEBBQADgwACDwAwggIKAoICAQC4K4zr1jTi4SSypXbrNeGd2HbYlrDRIPsPcL5a4JwGUKXwi+Rpf8Xh0D4dcRRH+Rtd5F66aqdGnhCBKtU5XsmlT+QssIggihI0iF3LEPsMlKapDrDdSbWmuitVDSSlulReMcN3hEUl8AzlNyu817snZtYESiFxm87QV6xZAcrWzvIdyiStBbngCT/v76tOZDX56IIRGoLMi3WND7538PqqYheh2+oZk05O+Bf5LZc6YteTRLLOSyIIxesoABo8tvaFyIo2ihMcnDRnGAzOMNTLXiQdj2scAMCVr3oiLpU48+Iw8ptOUBDQioW15FsYd3ugZhUX+/mFtMFsYkJyYjyG5HCqAs2/wm6eIjjy1QQwUF2hB8Z7sqyF5KrVZOv6Q7+pB83tT02ZXcDXCdsiP10G3sA4kjc/r9TuQHjCIwZa1LO4tPaO8qAzlROHIkQ4FhdaAM9U9DUq3nBywQLcEVQmXeH1OA1ve96QbMQoN+SRPh0Kq6W0U4TbzvMskQ7bePKDjiWP2fdtgSfrnOsyJaLi04n+hDsgiMfd4N9tauSMpCY6H9l7yYPc5Z+3qG2ANhteZGa7wT1OZoGLkZV0OurnA4xkzwcB7h0RVEvABB9dtl6S60FK1NELQy6sC/HCcivo9sJ+C1g2Sln+8qEdiju86X5ja5pGiRhJAxwSp2ZKgwIDAQAB";
String encryptedMessage = "g81SOC9XOD9zq5qfyhkdP/7ronNb82g3ueDtEh711L43zPSgrFksLEdIud/1fiDcV6N97RD41vb/iXtCg2/Gu6XliEhCaoG28reetG1cBndKF9UzQw9cYChp54S1wnhBkAAZQ4Of3c77DtPBCL4gcgv2ilBTm7o+NR2wXunfJ7Olbbau+7C1pa+Qv/+sz45r4gJmQ1MfGjHtw9e/U/3vjL9BfCEPn9Mo2zAZhkI81S0Ewth+csHwb3YTlE8mtHni1fvLRVXjvHk+57U3keoYPZk+93ytFL6pqkWMk+9VbLuUFHXn1mpSMiEr9GRN6XKRvEbbPp5lI9WjwRvtWfmRm5gLY76QinTrPb0KJg7oWmEoQie5o9W6MOkD+8vYV/SkkLT855SB3O57QLKCZmlSPlccE6GWfglHhAwRwrcTDY1bO/xH38gvYYPaAJMtJKtOVrqGxNkIUPwCCkdBa9JQwDSyTYxeh8AxC0ACs9cYVjMPrmC9zIZuRbmcneIGSugtzMZmI9qbLtW1aMlWuGrVyVhJlcCZuTJXWyBgx8xj8coX9YwUXSi1A4dL/Hl5Sme+HhAQs7OcH6ZZpsPmIIozXxHgOMhUo8k++cWg6+pudSoB2tr4NhxX/ID2jd1ELsg1C6mbxaKaGgXwfU9w4ZngbRxGTBlKWXwUP/xBa5BARZ4=";
#Test
public void encryptTest() throws Exception {
PublicKey publicKey = convertPublicKey(publicKeyContent);
assertNotNull(publicKey);
String s = decryptString(publicKey, encryptedMessage);
assertNotNull(s);
}
private PublicKey convertPublicKey(String publicKey) throws RSAAlgorithmException {
try {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
//generate public key
byte[] publicBytes = Base64.getDecoder().decode(publicKey);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicBytes);
return keyFactory.generatePublic(keySpec);
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
throw new RSAAlgorithmException("Unable to generate public key from string " + publicKey + " . " + e.getMessage());
}
}
private String decryptString(PublicKey publicKey, String value) throws Exception {
byte[] decodedBytes;
try {
Cipher c = Cipher.getInstance("RSA/ECB/PKCS1Padding");
c.init(Cipher.DECRYPT_MODE, publicKey);
decodedBytes = c.doFinal(value.getBytes());
} catch (Exception e) {
System.out.println("Error = " + e);
throw new Exception(e);
}
return new String(decodedBytes);
}
}
I have next error:
java.lang.Exception: javax.crypto.IllegalBlockSizeException: Data must not be longer than 512 bytes
In an asymmetric cryptosystem, you have a key pair consisting of both a public and a private key.
You encrypt with the public key and you decrypt with the private key. The public key can be shared (publicly) with other parties, enabling them to send you encrypted messages. The private key is kept secret so that only you can decrypt messages encrypted with your public key.
You normally don't encrypt messages directly with RSA, since the message has to be shorter than the modulus and it might have security implications. What you do instead is, you generate a random key for a symmetric encryption scheme, for example AES-256-CTR (or AES-256-GCM if you need authentication in addition to secrecy), encrypt the message with the symmetric encryption scheme, encrypt the key for the symmetric cipher with the asymmetric encryption scheme and send both the (asymmetrically) encrypted key and the (symmetrically) encrypted message to the receiver.
The receiver will first use his/her private key to decrypt the key for the symmetric encryption scheme, then use that to decrypt the actual message. This is sometimes referred to as "hybrid encryption" and it enables the message to be (more or less) arbitrarily long.
So, what you have to do is the following.
You have to generate a key pair for the receiver of the encrypted message. Therefore, if your communication is one-way (iOS device sends data to server, but no data ever comes back), you need to generate a key pair for your server only. If your server needs to talk back, you need to generate a key pair for your client as well.
In order to send an encrypted message to the server, the client needs to have the public key of your server. Therefore, you have to somehow transfer it there. The problem is that this transfer needs to be secure, otherwise an attacker may impersonate the server, present you his/her public key instead (for which he/she knows the private counterpart), intercept all traffic, decrypt it with his/her private key, re-encrypt it with the server's public key and pass it on to the server. This is called a man in the middle attack and enables the attacker to intercept (and possibly manipulate) all communication between you and the server. Therefore, your best choice might be not to exchange public keys at all but rather to embed them into the application. This will prevent man in the middle attacks, as long as the application code can be shared by an authenticated means.
When you want to send a message to the server, generate a random symmetric encryption key (with a cryptographically secure random number generator - this is not your language's default "random" function), encrypt the message with it and an appropriate symmetric encryption scheme, which you choose according to your requirements (e. g. authentication required? then use AES-GCM - only secrecy required? then use AES-CTR). Most encryption schemes also require a random (unpredictable) initialization vector which you also generate with a CSPRNG and have to send along to the receiver since it's required for decryption, but needs not be kept secret.
Encrypt the key for the symmetric encryption scheme with an asymmetric encryption scheme and the server's public key. RSA-PKCS1 is "dated". I'd try to use RSA-OAEP instead since it has more desirable security properties. Send the encrypted key to the server.
The server decrypts the key for the symmetric encryption scheme with the asymmetric encryption scheme and his private key (which is kept secret). Then it decrypts the message with the symmetric encryption scheme.
Since most of this is complicated and a lot of subtle details can lead to security breaches, I'd suggest you do not implement this yourself. I'd suggest you just use TLS (possibly with a restricted parameter set) and implement your own certificate validator where you compare the server's public key to a known-good value to get rid of the entire PKI stuff, which costs money and also is not very secure in the first place. At least, that's how I would do it.
Alternatively, if you want to roll out your own, "proprietary" protocol, you can try to use one of the more "developer friendly" cryptographic libraries, especially NaCl. This abstracts away a lot of the "gory details" and chooses lots of sane defaults for you, which cannot be overridden, all of which makes it a lot harder to implement insecure protocols.
Keep in mind this is not to say you're "too dumb". It's just the proper way of doing these things. When it comes to crypto, the less "DIY", the better. The more widespread the crypto is, that you use, the more it gets reviewed and the quicker flaws will get fixed, so using something like NaCl, which is used in thousands of applications, is pretty neat. As long as other NaCl applications are secure, your application is (probably) secure as well. When a breach is found, NaCl will get updated, you just update the library in your application and are automatically safe, so you're left with (almost) no need for internal review and patching and your windows of vulnerability will (usually) be short.
I'm working on an android application where I'd like the user to be able to encrypt messages using other's public keys. The system would generate a public/private keypair and then messages can be sent to other users secretly.
I'm creating an Encryption class which will handle the encryption/decryption of messages. Unfortunately I'm having some problems.
In this method, I'd like to pass the user's secret (private key) as well as the message they want to encrypt. I'd like the secret to be user-defined (like "MySecretPassword").
public static void lock(String secret, String textToEncrypt) {
try {
//Convert the public key string into a key
byte[] encodedPublicKey = Base64.decode(secret.getBytes("utf-8"),Base64.DEFAULT);
X509EncodedKeySpec spec = new X509EncodedKeySpec(encodedPublicKey);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publickey = keyFactory.generatePublic(spec); //Crash Here
PrivateKey privateKey = keyFactory.generatePrivate(spec);
//Encrypt Message
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publickey);
byte[] encryptedBytes = cipher.doFinal(textToEncrypt.getBytes());
Log.d(TAG,"Encrypted: "+new String(encryptedBytes));
} catch (Exception e) {
e.printStackTrace();
}
}
The exception is as follows:
java.security.spec.InvalidKeySpecException: java.lang.RuntimeException: error:0c0740b0:ASN.1 encoding routines:ASN1_get_object:TOO_LONG
What am I missing here? Am I missing something obvious or am I misunderstanding how these tools work? I've used this javascript library for public/private key encryption before and am trying to do something similar here. I'd appreciate it if somebody could point me in the right direction :)
A secret is not a public key.
You encrypt with the public key of the recipient. That value is public, which means that anybody can look it up. You need to get the value of the other party's public key and feed it into your code, not send in your own private key. The proper way to do this does not involve any secrets!
Normally one does not directly encrypt a message with RSA, instead they encrypt an AES key (or other symmetric key) with RSA and use the AES key to encrypt the message. If your messages are really short, you could use RSA directly, but it won't work for long messages.
Here are a couple links showing how to implement RSA on Android:
RSA using SpongyCastle
RSA encryption in Android and Java
i'm triying encrypt a password. firstly i will set a rsa public key, after encyrpt a password. but my results are not equals between java and javascript code. is there a way or sample. Could you help me? thanks.
here is my javascript code
var rsa = new RSAKey();
rsa.setPublic(rasn, rase);
var res = rsa.encrypt("myPassword");
return restotal;
here is my java code
RSA1 rsa = new RSA1(new BigInteger(rasn.getBytes()),new BigInteger(rase.getBytes()));
String text1 = "myPassword";
BigInteger plaintext = new BigInteger(text1.getBytes());
BigInteger ciphertext = rsa.encrypt(plaintext);
System.out.println(ciphertext);
Not sure what your RSA1 class exactly does, but you can check a nice sample here.
Your results will never be same, because before RSA encryption, your data will be appended with random bytes called padding.
If you want to test your encryption, decrypt the result back with your private key and see if it works. You can do this between javascript and java if you wish.
I am working on Android application for Encryption and decryption using RSA algorithm.
My intention is to decry-pt a file which is encrypted by server using RSACertificate.der and RSAPrivatekey.p12 files.
Now I have a Example.encriptedfile, RSACertificat.der and RSAPrivatekey.p12 files
I would like to decrypt the above example.encrypted file using above keys in JAVA
The implementation for getting Privatekey
And Decryption code using Cipher is
The file is example.encrypted file.
byte[] descryptedData = null;
try {
byte[] data = new byte[(int) file.length()];
new FileInputStream(file).read(data)
KeyStore keystore = KeyStore.getInstance("PKCS12");
keystore.load(con.getAssets().open("rsaPrivate.p12"), "password".toCharArray());
pk = (PrivateKey)keystore.getKey("1", "password".toCharArray());
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, pk );
descryptedData = cipher.doFinal(data);
} catch (Exception e) {
e.printStackTrace();
}
return new String(descryptedData);
The exception getting for the fallowing code is
java.security.InvalidKeyException: unknown key type passed to RSA
at com.android.org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi.engineInit(CipherSpi.java:277)
at com.android.org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi.engineInit(CipherSpi.java:381)
at javax.crypto.Cipher.init(Cipher.java:519)
at javax.crypto.Cipher.init(Cipher.java:479)
So can any one provide the suggestions and solutions to implement this
Thanks in advance.
But same
If it's encrypted with the private key it isn't encrypted at all, as anyone who can get the public key can decrypt it, which could be anybody.
If the intention was really to encrypt it, it should have been encrypted with the public key.
Possibly however the intention was to sign it, in which case what you have to do is verify it against the public key. For which you need the public key, which is in the certificate. So you load the certificate into a Certificate, using the java.security.cert API and then use a java.security.Signature object to verify the signature with, using the public key obtained from the Certificate.