Check if SSH Private Key is Encrypted - c

Key pairs generated with ssh-keygen on macOS can have different formats.
The standard PEM ASN.1 object which is readable by macOS' SecKey APIs
A PEM with textual headers
OpenSSH Keys
OpenSSH Encrypted Keys
OpenSSH/BSD uses this non-standardized format here.
Now I only need to be able to check if a private key has a passphrase set or not, so I can prompt the user to enter it, without having to deal with the complexities of different key formats.
Is there a quick way on macOS via Swift or C API, to check if a key has a passphrase set?

The difference between the unencrypted and encrypted private keys is the fact that the key blob is encrypted. You need to decrypt the private key blob data before you can use the private key blob. So once the encrypted private key data is decoded, you can treat it the same as the unencrypted private key data.
A unencrypted private key blob PEM file looks like this:
—–BEGIN PRIVATE KEY—–
{base64 private key blob)
—–END PRIVATE KEY—–
The encrypted RSA private key PEM file looks like this:
—–BEGIN RSA PRIVATE KEY—–
Proc-Type: 4,ENCRYPTED
DEK-Info: {encryption algorithm},{salt}
{base64 encrypted private key blob)
—–END RSA PRIVATE KEY—–
e.g.
—–BEGIN RSA PRIVATE KEY—–
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-256-CBC,AB8E2B5B2D989271273F6730B6F9C687
{base64 encrypted private key blob)
—–END RSA PRIVATE KEY—–
So to decode the private key data you need to:
Parse the DEK-Info encryption algorithm and the salt (good idea to confirm the first line is: "Proc-Type: 4,ENCRYPTED" as well).
Decode the base64 encrypted private key blob.
Generate the encryption algorithm "key" and "IV" based on the salt and the passphrase
Decode the encrypted private key blob.
Once you have done that the decrypted private key blob can be treated just like the unencoded private key blob.
The number of supported encryption algorithm's are rather large, so you may like to support a sub-set of algorithms. e.g. "DES-EDE3-CBC", "AES-xxx-CBC", etc
To generate the IV you need to convert salt string to binary. The salt string is a hex encoded string, so convert each two strings characters into a byte using a hex string character to byte converter.
For the generation of the encryption algorithm key you need the key size (e.g. DES-EDE3-CBC is 192bits, AES-256-CBC is 256bits). Build up the key "bits" with a loop appending MD5 hash results to the key until generate all the key bits required.
The MD5 HASH loop generation will consist of:
First MD5 Hash: MD5 hash of the first 8 bytes of the IV and the Passphrase
All other MD5 Hashes is the MD5 hash of the last MD5 hash result and the first 8 bytes of the IV and the Passphrase
See the openssl source for EVP_BytesToKey method for an example of the key bits generation.
The encrypted private key blob can now be decoded using the selected encryption algorithm using the IV and KEY build above.

There are two ways that I would suggest. Either reading the command line output using readLine() and checking if it asks for a password then do something accordingly.
import Foundation
func runCommand(cmd : String, args : String...) -> (output: [String], error: [String], exitCode: Int32) {
var output : [String] = []
var error : [String] = []
let task = Process()
task.launchPath = cmd
task.arguments = args
let outpipe = Pipe()
task.standardOutput = outpipe
let errpipe = Pipe()
task.standardError = errpipe
task.launch()
let outdata = outpipe.fileHandleForReading.readDataToEndOfFile()
if var string = String(data: outdata, encoding: .utf8) {
string = string.trimmingCharacters(in: .newlines)
output = string.components(separatedBy: "\n")
}
let errdata = errpipe.fileHandleForReading.readDataToEndOfFile()
if var string = String(data: errdata, encoding: .utf8) {
string = string.trimmingCharacters(in: .newlines)
error = string.components(separatedBy: "\n")
}
task.waitUntilExit()
let status = task.terminationStatus
return (output, error, status)
}
//Sample usage
let (output, error, status) = runCommand(cmd: "/usr/local/bin/node", args: "--version")
print("program exited with status \(status)")
if output.count > 0 {
print("program output:")
print(output)
//HERE YOU CAN CHECK IF PASSWORD REQUEST HAS BEEN MADE
}
if error.count > 0 {
print("error output:")
print(error)
}
The example code will return your node version installed if there is one, but you could use it to check if a password prompt has been made by the system for the RSA Key.
The other way could be perhaps using a third-party library like SwiftyRSA or BlueRSA which might help with validation.

I implemented my own OpenSSH check for the 2 most common formats
For one I'm checking the PEM headers for DEK-Info for Linux-style SSH PEMs
For OpenSSH style keys I manually parse the format using the class below
import Foundation
private let opensshMagic = "openssh-key-v1"
public class SSHPrivateKey {
public struct OpenSSHKey {
let cipherName: String
let kdfName: String
let kdfOptions: Data
let numKeys: Int
var isEncrypted: Bool {
return cipherName != "none"
}
}
public let data: Data
public init(data: Data) {
self.data = data
}
public func openSSHKey() -> OpenSSHKey? {
// #define AUTH_MAGIC "openssh-key-v1"
//
// byte[] AUTH_MAGIC
// string ciphername
// string kdfname
// string kdfoptions
// int number of keys N
// string publickey1
// string publickey2
// ...
// string publickeyN
// string encrypted, padded list of private keys
guard let magic = opensshMagic.data(using: .utf8) else {
return nil
}
if data.prefix(magic.count) != magic {
return nil
}
var offset = magic.count + 1
guard let cipherName = data.consumeString(offset: &offset),
let kdfName = data.consumeString(offset: &offset) else {
return nil
}
let kdfOptions = data.consumeBytes(offset: &offset)
let numKeys = data.consumeUInt32(offset: &offset)
return OpenSSHKey(cipherName: cipherName,
kdfName: kdfName,
kdfOptions: kdfOptions,
numKeys: Int(numKeys))
}
}
private extension Data {
func consumeBytes(offset: inout Int) -> Data {
let n = Int(consumeUInt32(offset: &offset))
let b = Data(self[offset..<offset + n])
offset += n
return b
}
func consumeString(offset: inout Int) -> String? {
return consumeBytes(offset: &offset).utf8String
}
func consumeUInt8(offset: inout Int) -> UInt8 {
let v = self[offset] & 0xFF
offset += 1
return v
}
func consumeUInt32(offset: inout Int) -> UInt32 {
var i: UInt32 = 0
i = (i << 8) | UInt32(consumeUInt8(offset: &offset))
i = (i << 8) | UInt32(consumeUInt8(offset: &offset))
i = (i << 8) | UInt32(consumeUInt8(offset: &offset))
i = (i << 8) | UInt32(consumeUInt8(offset: &offset))
return i
}
}

Related

how to create encrypted password field [duplicate]

This question already has answers here:
how to encrypt the password column
(6 answers)
Closed 3 years ago.
I want to create a table and there is one field that I want to make encrypted but I don't know how to make it. It would be better if the solution is in SQL.
you can try these methods to encrypt and decrypt:
To encrypt:
public string Encrypt(string plainText)
{
if (plainText == null) throw new ArgumentNullException("plainText");
//encrypt data
var data = Encoding.Unicode.GetBytes(plainText);
byte[] encrypted = ProtectedData.Protect(data, null, Scope);
//return as base64 string
return Convert.ToBase64String(encrypted);
}
Decrypt Method:
public string Decrypt(string cipher)
{
if (cipher == null) throw new ArgumentNullException("cipher");
//parse base64 string
byte[] data = Convert.FromBase64String(cipher);
//decrypt data
byte[] decrypted = ProtectedData.Unprotect(data, null, Scope);
return Encoding.Unicode.GetString(decrypted);
}

Stream of Char to Stream of Byte/Byte Array

The following code takes a String s, converts into char array, filters digits from it, then converts it to string, then converts into byte array.
char charArray[] = s.toCharArray();
StringBuffer sb = new StringBuffer(charArray.length);
for(int i=0; i<=charArray.length-1; i++) {
if (Character.isDigit(charArray[i]))
sb.append(charArray[i]);
}
byte[] bytes = sb.toString().getBytes(Charset.forName("UTF-8"));
I'm trying to change the above code to streams approach. Following is working.
s.chars()
.sequential()
.mapToObj(ch -> (char) ch)
.filter(Character::isDigit)
.collect(StringBuilder::new,
StringBuilder::append, StringBuilder::append)
.toString()
.getBytes(Charset.forName("UTF-8"));
I think there could be a better way to do it.
Can we directly convert Stream<Character> to byte[] & skip the conversion to String in between?
First, both of your variants have the problem of not handling characters outside the BMP correctly.
To support these characters, there is codePoints() as an alternative to chars(). You can use appendCodePoint on the target StringBuilder to consistently use codepoints throughout the entire operation. For this, you have to remove the unnecessary .mapToObj(ch -> (char) ch) step, whose removal also eliminates the overhead of creating a Stream<Character>.
Then, you can avoid the conversion to a String in both cases, by encoding the StringBuilder using the Charset directly. In the case of the stream variant:
StringBuilder sb = s.codePoints()
.filter(Character::isDigit)
.collect(StringBuilder::new,
StringBuilder::appendCodePoint, StringBuilder::append);
ByteBuffer bb = StandardCharsets.UTF_8.encode(CharBuffer.wrap(sb));
byte[] utf8Bytes = new byte[bb.remaining()];
bb.get(utf8Bytes);
Performing the conversion directly with the stream of codepoints is not easy. Not only is there no such support in the Charset API, there is no straight-forward way to collect a Stream into a byte[] array.
One possibility is
byte[] utf8Bytes = s.codePoints()
.filter(Character::isDigit)
.flatMap(c -> c<128? IntStream.of(c):
c<0x800? IntStream.of((c>>>6)|0xC0, c&0x3f|0x80):
c<0x10000? IntStream.of((c>>>12)|0xE0, (c>>>6)&0x3f|0x80, c&0x3f|0x80):
IntStream.of((c>>>18)|0xF0, (c>>>12)&0x3f|0x80, (c>>>6)&0x3f|0x80, c&0x3f|0x80))
.collect(
() -> new Object() { byte[] array = new byte[8]; int size;
byte[] result(){ return array.length==size? array: Arrays.copyOf(array,size); }
},
(b,i) -> {
if(b.array.length == b.size) b.array=Arrays.copyOf(b.array, b.size*2);
b.array[b.size++] = (byte)i;
},
(a,b) -> {
if(a.array.length<a.size+b.size) a.array=Arrays.copyOf(a.array,a.size+b.size);
System.arraycopy(b.array, 0, a.array, a.size, b.size);
a.size+=b.size;
}).result();
The flatMap step converts the stream of codepoints to a stream of UTF-8 unit. (Compare with the UTF-8 description on Wikipedia) The collect step collects int values into a byte[] array.
It’s possible to eliminate the flatMap step by creating a dedicate collector which collects a stream of codepoints directly into a byte[] array
byte[] utf8Bytes = s.codePoints()
.filter(Character::isDigit)
.collect(
() -> new Object() { byte[] array = new byte[8]; int size;
byte[] result(){ return array.length==size? array: Arrays.copyOf(array,size); }
void put(int c) {
if(array.length == size) array=Arrays.copyOf(array, size*2);
array[size++] = (byte)c;
}
},
(b,c) -> {
if(c < 128) b.put(c);
else {
if(c<0x800) b.put((c>>>6)|0xC0);
else {
if(c<0x10000) b.put((c>>>12)|0xE0);
else {
b.put((c>>>18)|0xF0);
b.put((c>>>12)&0x3f|0x80);
}
b.put((c>>>6)&0x3f|0x80);
}
b.put(c&0x3f|0x80);
}
},
(a,b) -> {
if(a.array.length<a.size+b.size) a.array=Arrays.copyOf(a.array,a.size+b.size);
System.arraycopy(b.array, 0, a.array, a.size, b.size);
a.size+=b.size;
}).result();
but it doesn’t add to readability.
You can test the solutions using a String like
String s = "some test text 1234 ✔ 3 𝟝";
and printing the result as
System.out.println(Arrays.toString(utf8Bytes));
System.out.println(new String(utf8Bytes, StandardCharsets.UTF_8));
which should produce
[49, 50, 51, 52, -17, -68, -109, -16, -99, -97, -99]
12343𝟝
It should be obvious that the first variant is the simplest, and it will have reasonable performance, even if it doesn’t create a byte[] array directly. Further, it’s the only variant which can be adapted for getting other result charsets.
But even the
byte[] utf8Bytes = s.codePoints()
.filter(Character::isDigit)
.collect(StringBuilder::new,
StringBuilder::appendCodePoint, StringBuilder::append)
.toString().getBytes(StandardCharsets.UTF_8);
is not so bad, regardless of whether the toString() operation bears a copying operation.

DigestUtils.md5Hex() generates wrong hash value when passing String object

I'm trying to generate a md5 hash in Kotlin using the DigestUtils class from the org.apache.commons.codec. Here's the test code
#Test
fun md5Test(){
val userPassword: String = "123"
val md5Hash: String = "202cb962ac59075b964b07152d234b70"
assertEquals(md5Hash, DigestUtils.md5Hex(userPassword))
}
The problem is that when I run this test it fails and says that the generated md5 hash is 28c1a138574866e9c2e5a19dca9234ce
But... when I pass the String value instead of the object
assertEquals(md5Hash, DigestUtils.md5Hex("123"))
The test passes without errors
Why this is happening?
Here is a complete solution to get MD5 base64 hash:
fun getMd5Base64(encTarget: ByteArray): String? {
val mdEnc: MessageDigest?
try {
mdEnc = MessageDigest.getInstance("MD5")
// Encryption algorithmy
val md5Base16 = BigInteger(1, mdEnc.digest(encTarget)) // calculate md5 hash
return Base64.encodeToString(md5Base16.toByteArray(), 16).trim() // convert from base16 to base64 and remove the new line character
} catch (e: NoSuchAlgorithmException) {
e.printStackTrace()
return null
}
}
This is the most simple and complete solution in kotlin:
val hashedStr = String.format("%032x", BigInteger(1, MessageDigest.getInstance("MD5").digest("your string value".toByteArray(Charsets.UTF_8))))

Generating Chrome Packaged App .crx header with Java

I"m trying to build the header in Java with no luck.
(here is the spec: https://developer.chrome.com/extensions/crx)
Any ideas?
protected void geneateCrxHeader (OutputStream ins,byte[] zipArchive) throws NoSuchAlgorithmException, NoSuchProviderException, IOException, InvalidKeyException, SignatureException{
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
SecureRandom random = SecureRandom.getInstance("SHA1PRNG", "SUN");
keyGen.initialize(1024, random);
KeyPair pair = keyGen.generateKeyPair();
byte[] key = pair.getPublic().getEncoded();
Signature instance = Signature.getInstance("SHA1withRSA");
instance.initSign(pair.getPrivate());
instance.update(zipArchive);
byte[] hash = instance.sign();
byte[] magic = {0x43,0x72,0x32,0x34,0x02,0x00,0x00,0x00,0x00,0x01,0x0E,0x0B,0x00,0x00,0x08,0x00};
ins.write(magic);
ins.write(key);
ins.write(hash);
}
and I get CRX_EXCESSIVELY_LARGE_KEY_OR_SIGNATURE..
I must be using wrong keygen.
in the docs they do say :
"..the contents of the author's RSA public key, formatted as an X509 SubjectPublicKeyInfo block. .."
i wonder if that is what i'm not doing correctly...
p.s Java crypto is a new frontier for me , so pls don't laugh if I did something totally dumb.
Here is some code that I whipped up and didn't test that illustrates producing just the header. I based this solely on reading the spec.
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.security.Signature;
public static byte[] generateCrxHeader(byte[] extensionContents) throws Exception {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
SecureRandom random = new SecureRandom();
keyGen.initialize(1024, random);
KeyPair pair = keyGen.generateKeyPair();
Signature sigInstance = Signature.getInstance("SHA1withRSA");
sigInstance.initSign(pair.getPrivate());
sigInstance.update(extensionContents);
byte [] signature = sigInstance.sign();
byte [] subjectPublicKeyInfo = pair.getPublic().getEncoded();
final int headerLength = 4 + 4 + 4 + 4 + subjectPublicKeyInfo.length + signature.length;
ByteBuffer headerBuf = ByteBuffer.allocate(headerLength);
headerBuf.order(ByteOrder.LITTLE_ENDIAN);
headerBuf.put(new byte[]{0x43,0x72,0x32,0x34}); // Magic number
headerBuf.putInt(2); // Version
headerBuf.putInt(subjectPublicKeyInfo.length); // public key length
headerBuf.putInt(signature.length); // signature length
headerBuf.put(subjectPublicKeyInfo);
headerBuf.put(signature);
final byte [] header = headerBuf.array();
return header;
}

How do I run an encryption program multiple times to strengthen the encode?

Here is my code so far. I need to run the encode part of the code 5 times and then decode the encode the same number of times. I figured out how to encode the message but now I can't figure out how to run the "encode" or "decode" variable back through the code to strengthen the ecryption.
public class Codes
{
/**
* Encode and decode a message using a key of values stored in
* a queue.
*/
public static void main(String[] args)
{
int[] key = {7, 6, 5, 2, 8, 5, 8, 6, 4, 1};
Integer keyValue;
String encoded = "", decoded = "";
String message = "Queues are useful for encoding messages.";
Queue<Integer> encodingQueue = new LinkedList<Integer>();
Queue<Integer> decodingQueue = new LinkedList<Integer>();
// load key queues
for (int scan = 0; scan < key.length; scan++)
{
encodingQueue.add(key[scan]);
decodingQueue.add(key[scan]);
}
// encode message
for (int scan = 0; scan < message.length(); scan++)
{
keyValue = encodingQueue.remove();
encoded += (char) (message.charAt(scan) + keyValue);
encodingQueue.add(keyValue);
}
System.out.println ("Encoded Message:\n" + encoded + "\n");
// decode message
for (int scan = 0; scan < encoded.length(); scan++)
{
keyValue = decodingQueue.remove();
decoded += (char) (encoded.charAt(scan) - keyValue);
decodingQueue.add(keyValue);
}
System.out.println ("Decoded Message:\n" + decoded);
}
}
as of right now I am receiving this output:
Encoded Message:
X{jwmx(gvf'{xgnzt&jpy&jpktlorh'sju{fokw/
Decoded Message:
Queues are useful for encoding messages.
In order to complete this program I need the output to look like this:
Encoded Message 1: X{jwmx(gvf'{xgnzt&jpy&jpktlorh'sju{fokw/
Encoded Message 2: _?oyu}0mzg.?}iv•|,nq?,orsytuvi.yow?kwq{0
Encoded Message 3: f?t{}?8s~h5??k~??2rr?2tt{~|{zj5•ty?p•w•1
Encoded Message 4: m?y}??#y?i<??m???8vs?8yv????~k<?y{?u?}?2
Encoded Message 5: t?~•??H•?jC??o???>zt?>~x?????lC?~}?z???3
Decoded Message 5: m?y}??#y?i<??m???8vs?8yv????~k<?y{?u?}?2
Decoded Message 4: f?t{}?8s~h5??k~??2rr?2tt{~|{zj5•ty?p•w•1
Decoded Message 3: _?oyu}0mzg.?}iv•|,nq?,orsytuvi.yow?kwq{0
Decoded Message 2: X{jwmx(gvf'{xgnzt&jpy&jpktlorh'sju{fokw/
Decoded Message 1: Queues are useful for encoding messages.
I estimate that in order to make this happen I need to use a loop to run the "encode" and "decode" variables back through the program. However I cannot figure out how to make that happen.
This will be easier if you use separate functions for the encode() and decode() operations:
class Codes {
public static void main(String[] args) {
...
}
private static String encode(String plaintext, Queue<Integer> encodingQueue) {
...
}
private static String decode(String ciphertext, Queue<Integer> decodingQueue) {
...
}
}
Does that help?

Resources