Last active
March 13, 2020 20:23
-
-
Save mikofski/7028865 to your computer and use it in GitHub Desktop.
MATLAB version of javaDigest EncryptionUtil from http://javadigest.wordpress.com/2012/08/26/rsa-encryption-example/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
classdef EncryptionUtil | |
%% A class of static encryption methods | |
% attributions: | |
% | |
% * http://javadigest.wordpress.com/2012/08/26/rsa-encryption-example/ | |
% * http://stackoverflow.com/a/19435226/1020470 | |
%% Constants | |
properties (Constant) | |
ALGORITHM = 'RSA' % Encryption algorithm. | |
% Names of the key files. | |
HOME = getenv('HOME');MATHOME = fullfile('Documents','MATLAB'); | |
NAME = fullfile(EncryptionUtil.HOME,EncryptionUtil.MATHOME,mfilename); | |
PRIVATE_KEY_FILE = fullfile(EncryptionUtil.NAME,'keys','private.key') | |
PRIVATE_ENCKEY = fullfile(EncryptionUtil.NAME,'keys','privateEncoded.key') | |
PRIVATE_JSCH_KEY = fullfile(EncryptionUtil.NAME,'keys','privateJsch.key') | |
% String to hold name of the public key file. | |
PUBLIC_KEY_FILE = fullfile(EncryptionUtil.NAME,'keys','public.key') | |
PUBLIC_ENCKEY = fullfile(EncryptionUtil.NAME,'keys','publicEncoded.key') | |
PUBLIC_JSCH_KEY = fullfile(EncryptionUtil.NAME,'keys','publicJsch.key') | |
PUBLIC_JSCH_SECSH = fullfile(EncryptionUtil.NAME, ... | |
'keys','publicJschSECSH.key') | |
% BASE64 Encoder | |
BASE64ENCODER = org.apache.commons.codec.binary.Base64; | |
end | |
%% Static Methods | |
methods (Static) | |
%% Generate keys using Java, save as Java Encoded, and write SSH keys | |
% requires Jsch (http://www.jcraft.com/jsch/) | |
function generateKey(keysize) | |
% GENERATEKEY(KEYSIZE) | |
% Generate key which contains a pair of private and public key | |
% using KEYSIZE bytes. Store the set of keys in Prvate.key and | |
% Public.key files. Default size is 1024-bits. | |
if nargin<1,keysize = 1024;end % set default | |
try | |
%% java.security.KeyPair | |
keyGen = java.security.KeyPairGenerator.getInstance( ... | |
EncryptionUtil.ALGORITHM); | |
keyGen.initialize(keysize); | |
key = keyGen.generateKeyPair; | |
privateKeyFile = java.io.File(EncryptionUtil.PRIVATE_KEY_FILE); | |
publicKeyFile = java.io.File(EncryptionUtil.PUBLIC_KEY_FILE); | |
% Create files to store public and private key | |
if ~isempty(privateKeyFile.getParentFile) | |
privateKeyFile.getParentFile.mkdirs; | |
end | |
privateKeyFile.createNewFile; | |
if ~isempty(publicKeyFile.getParentFile) | |
publicKeyFile.getParentFile.mkdirs; | |
end | |
publicKeyFile.createNewFile; | |
% Saving the Public key in a file | |
publicKeyOS = java.io.ObjectOutputStream( ... | |
java.io.FileOutputStream(publicKeyFile)); | |
publicKeyOS.writeObject(key.getPublic); | |
publicKeyOS.close; | |
% Saving the Private key in a file | |
privateKeyOS = java.io.ObjectOutputStream( ... | |
java.io.FileOutputStream(privateKeyFile)); | |
privateKeyOS.writeObject(key.getPrivate); | |
privateKeyOS.close; | |
%% save Java Encoded Keys | |
% save encoded public key (X.509) | |
fw = java.io.FileWriter(EncryptionUtil.PUBLIC_ENCKEY); | |
fw.write(java.lang.String( ... | |
EncryptionUtil.BASE64ENCODER.encode( ... | |
key.getPublic.getEncoded))) | |
fw.close | |
% save encoded public key (PKCS#8) | |
fw = java.io.FileWriter(EncryptionUtil.PRIVATE_ENCKEY); | |
fw.write(java.lang.String( ... | |
EncryptionUtil.BASE64ENCODER.encode( ... | |
key.getPrivate.getEncoded))) | |
fw.close | |
catch ME | |
publicKeyOS.close; | |
privateKeyOS.close; | |
fw.close | |
rethrow(ME) | |
end | |
%% com.jcraft.jsch.KeyPair | |
JSCH = com.jcraft.jsch.JSch; % create new JSch object | |
RSA = com.jcraft.jsch.KeyPair.RSA; % RSA | |
try | |
% Creates a new key pair with default key size of 1024 bits. | |
keyGenJsch = com.jcraft.jsch.KeyPair.genKeyPair(JSCH, RSA, keysize); | |
keyGenJsch.writePrivateKey(EncryptionUtil.PRIVATE_JSCH_KEY) | |
keyGenJsch.writePublicKey(EncryptionUtil.PUBLIC_JSCH_KEY, ... | |
'testReadWriteSSHKeyPair') | |
keyGenJsch.writeSECSHPublicKey( ... | |
EncryptionUtil.PUBLIC_JSCH_SECSH,'testReadWriteSSHKeyPair') | |
catch ME | |
rethrow(ME) | |
end | |
end | |
%% checks to make sure keys exist before testing | |
function tf = areKeysPresent | |
% The method checks if the pair of public and private key has been | |
% generated. | |
privateKey = java.io.File(EncryptionUtil.PRIVATE_KEY_FILE); | |
publicKey = java.io.File(EncryptionUtil.PUBLIC_KEY_FILE); | |
if (privateKey.exists && publicKey.exists) | |
tf = true; | |
return | |
end | |
tf = false; | |
end | |
function tf = areEncKeysPresent | |
% The method checks if the pair of public and private key has been | |
% generated. | |
privateKey = java.io.File(EncryptionUtil.PRIVATE_ENCKEY); | |
publicKey = java.io.File(EncryptionUtil.PUBLIC_ENCKEY); | |
if (privateKey.exists && publicKey.exists) | |
tf = true; | |
return | |
end | |
tf = false; | |
end | |
function tf = areJschKeysPresent | |
% The method checks if the pair of public and private key has been | |
% generated. | |
privateKey = java.io.File(EncryptionUtil.PRIVATE_JSCH_KEY); | |
publicKey = java.io.File(EncryptionUtil.PUBLIC_JSCH_KEY); | |
publicSECSHKey = java.io.File(EncryptionUtil.PUBLIC_JSCH_SECSH); | |
if (privateKey.exists && publicKey.exists && publicSECSHKey.exists) | |
tf = true; | |
return | |
end | |
tf = false; | |
end | |
%% Encrypt text using Java public key | |
function cipherText = encrypt(text,key) | |
% Encrypt the plain text using public key. | |
try | |
% get an RSA cipher object and print the provider | |
cipher = javax.crypto.Cipher.getInstance( ... | |
EncryptionUtil.ALGORITHM); | |
% encrypt the plain text using the public key | |
cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, key); | |
cipherText = cipher.doFinal(uint8(text)); | |
catch ME | |
rethrow(ME) | |
end | |
end | |
%% Decrypt text using Java private key | |
function decryptedText = decrypt(text, key) | |
% Decrypt text using private key. | |
try | |
% get an RSA cipher object and print the provider | |
cipher = javax.crypto.Cipher.getInstance( ... | |
EncryptionUtil.ALGORITHM); | |
% decrypt the text using the private key | |
cipher.init(javax.crypto.Cipher.DECRYPT_MODE, key); | |
decryptedText = char(cipher.doFinal(text)); | |
catch ME | |
rethrow(ME) | |
end | |
end | |
%% test keys by encrypting/decrypting text | |
function test(originalText) | |
% TEST(ORIGINALTEXT) encrypt/decrypt ORIGINALTEXT | |
% Test the EncryptionUtil | |
try | |
% Check if the pair of keys are present else generate those. | |
if ~EncryptionUtil.areKeysPresent | |
% Method generates a pair of keys using the RSA algorithm | |
% and stores it in their respective files | |
EncryptionUtil.generateKey; | |
end | |
% encryption text. | |
if nargin<1,originalText = 'Text to be encrypted';end | |
% read public key Object from file. | |
inputStream = java.io.ObjectInputStream( ... | |
java.io.FileInputStream(EncryptionUtil.PUBLIC_KEY_FILE)); | |
publicKey = inputStream.readObject; | |
inputStream.close | |
% Encrypt the string using the public key. | |
cipherText = EncryptionUtil.encrypt(originalText, publicKey); | |
% read private key object from file. | |
inputStream = java.io.ObjectInputStream( ... | |
java.io.FileInputStream(EncryptionUtil.PRIVATE_KEY_FILE)); | |
privateKey = inputStream.readObject; | |
inputStream.close | |
% Decrypt the cipher text using the private key. | |
plainText = EncryptionUtil.decrypt(cipherText, privateKey); | |
% Printing the Original, Encrypted and Decrypted Text. | |
java.lang.System.out.println(['Original Text: ',originalText]); | |
java.lang.System.out.println(['Encrypted Text: ', ... | |
char(cipherText)']); | |
java.lang.System.out.println(['Decrypted Text: ',plainText']); | |
catch ME | |
inputStream.close | |
rethrow(ME) | |
end | |
end | |
function testEncoded(originalText) | |
% Test the EncryptionUtil with encoded keys | |
% private keys are encoded with PKCS8EncodedKeySpec | |
% public keys are encoded with X509EncodedKeySpec | |
try | |
% Check if the pair of keys are present else generate those. | |
if ~EncryptionUtil.areEncKeysPresent | |
% Method generates a pair of keys using the RSA algorithm | |
% and stores it in their respective files | |
EncryptionUtil.generateKey; | |
end | |
% encryption text | |
if nargin<1,originalText = 'Text to be encrypted';end | |
% read encoded public key from file | |
keybuf = java.io.BufferedReader(java.io.FileReader( ... | |
EncryptionUtil.PUBLIC_ENCKEY)); | |
keyln = keybuf.readLine; | |
encKey = EncryptionUtil.BASE64ENCODER.decode(keyln.getBytes); | |
% only one line! | |
keybuf.close | |
% generate public key from encoded bytes using Spec & KeyFactory | |
pubKeySpec = java.security.spec.X509EncodedKeySpec(encKey); | |
keyFactory = java.security.KeyFactory.getInstance('RSA'); | |
publicKey = keyFactory.generatePublic(pubKeySpec); | |
% Encrypt the string using the public key | |
cipherText = EncryptionUtil.encrypt(originalText, publicKey); | |
% read encoded private key from file | |
keybuf = java.io.BufferedReader(java.io.FileReader( ... | |
EncryptionUtil.PRIVATE_ENCKEY)); | |
keyln = keybuf.readLine; | |
encKey = EncryptionUtil.BASE64ENCODER.decode(keyln.getBytes); | |
% only one line! | |
keybuf.close | |
% generate private key from encoded bytes using Spec & KeyFactory | |
privKeySpec = java.security.spec.PKCS8EncodedKeySpec(encKey); | |
keyFactory = java.security.KeyFactory.getInstance('RSA'); | |
privateKey = keyFactory.generatePrivate(privKeySpec); | |
% Decrypt the cipher text using the private key. | |
plainText = EncryptionUtil.decrypt(cipherText, privateKey); | |
% Printing the Original, Encrypted and Decrypted Text | |
java.lang.System.out.println(['Original Text: ',originalText]); | |
java.lang.System.out.println(['Encrypted Text: ', ... | |
char(cipherText)']); | |
java.lang.System.out.println(['Decrypted Text: ',plainText']); | |
catch ME | |
keybuf.close | |
rethrow(ME) | |
end | |
end | |
function testJschSECSH(originalText) | |
% Test the EncryptionUtil with Jsch keys | |
try | |
% Check if the pair of keys are present else generate those. | |
if ~EncryptionUtil.areJschKeysPresent | |
% Method generates a pair of keys using the RSA algorithm | |
% and stores it in their respective files | |
EncryptionUtil.generateKey; | |
end | |
% encryption text | |
if nargin<1,originalText = 'Text to be encrypted';end | |
% key factory | |
keyFactory = java.security.KeyFactory.getInstance('RSA'); | |
% read OpenSSH public key from file | |
[keyln,totalLines] = EncryptionUtil.readJschKeyFile( ... | |
EncryptionUtil.PUBLIC_JSCH_SECSH); | |
% join lines | |
keystr = EncryptionUtil.joinLines(keyln,totalLines,2,1); | |
% convert SSH RSA public key | |
publicKey = EncryptionUtil.convertSSHPublicKey(keystr,keyFactory); | |
% read OpenSSH private key from file | |
[keyln,totalLines] = EncryptionUtil.readJschKeyFile( ... | |
EncryptionUtil.PRIVATE_JSCH_KEY); | |
% join lines | |
keystr = EncryptionUtil.joinLines(keyln,totalLines,1,1); | |
% convert SSH RSA private key | |
privateKey = EncryptionUtil.convertSSHPrivateKey(keystr,keyFactory); | |
% compare keys | |
assert(publicKey.getModulus.equals(privateKey.getModulus), ... | |
'EncryptionUtil:testJschSECSH','Modulus does not match.') | |
assert(publicKey.getPublicExponent.equals(privateKey.getPublicExponent), ... | |
'EncryptionUtil:testJschSECSH','Exponent does not match.') | |
% Encrypt the string using the public key | |
cipherText = EncryptionUtil.encrypt(originalText, publicKey); | |
% Decrypt the cipher text using the private key. | |
plainText = EncryptionUtil.decrypt(cipherText, privateKey); | |
% Printing the Original, Encrypted and Decrypted Text | |
java.lang.System.out.println(['Original Text: ', originalText]); | |
java.lang.System.out.println(['Encrypted Text: ', char(cipherText)']); | |
java.lang.System.out.println(['Decrypted Text: ', plainText']); | |
catch ME | |
rethrow(ME) | |
end | |
end | |
%% Read key file into Java String Array | |
function [keyln,totalLines] = readJschKeyFile(file, nlines) | |
if nargin<2,nlines = 10;end | |
% open file | |
try | |
keybuf = java.io.BufferedReader(java.io.FileReader(file)); | |
lnIdx = 1; % line index | |
keyln = javaArray('java.lang.String',nlines); % string array | |
% read lines into string array | |
keyln(lnIdx) = keybuf.readLine; | |
fprintf('key line: %s\n',char(keyln(lnIdx))) % read line | |
while ~isempty(keyln(lnIdx)) | |
lnIdx = lnIdx+1; % line index | |
keyln(lnIdx) = keybuf.readLine; | |
fprintf('key line: %s\n',char(keyln(lnIdx))) | |
end | |
keybuf.close | |
% total lines minus last line | |
totalLines = lnIdx-1;fprintf('total lines: %d\n',totalLines) | |
catch ME | |
keybuf.close | |
rethrow(ME) | |
end | |
end | |
%% Concatenate String Array | |
function keystr = joinLines(keyln,totalLines,headers,footers) | |
% join lines | |
keystr = java.lang.StringBuilder; | |
for lnIdx = headers+1:totalLines-footers | |
keystr.append(keyln(lnIdx)); | |
end | |
% key string | |
keystr = keystr.toString;fprintf('key: %s\n',char(keystr)) | |
end | |
%% Read key data (modulus, exponent, ...) | |
function [buf,idx] = lenval(encKey,idx) | |
% get the number of bytes to read and read them into BUF | |
% return position of pointer, IDX, after reading BUF | |
len = double(swapbytes(typecast(encKey(idx:idx+3),'uint32'))); | |
idxs = idx+4:idx+3+len; | |
buf = encKey(idxs); | |
idx = idx+4+len; | |
end | |
%% Convert SSH public key to Java | |
function publicKey = convertSSHPublicKey(keystr, keyFactory) | |
% ASN.1 PKCS#1 (v2.1) format (see rfc3447) | |
% http://en.wikipedia.org/wiki/Abstract_Syntax_Notation_One | |
% http://en.wikipedia.org/wiki/PKCS1 | |
% http://tools.ietf.org/html/rfc3447 | |
% base64 decode | |
encKey = EncryptionUtil.BASE64ENCODER.decode(keystr.getBytes); | |
fprintf('number of Bytes: %d\n',numel(encKey)); % number of bytes | |
% key type [5:11] | |
[buf,idx] = EncryptionUtil.lenval(encKey,1); | |
fprintf('position: %d\n',idx) | |
keyType = char(buf)'; % public key type | |
assert(strcmp(keyType,'ssh-rsa'), ... | |
'EncryptionUtil:testJschSECSH', ... | |
'Key type "%s" is not "ssh-rsa".',keyType) | |
% public exponent [16:18] | |
[buf,idx] = EncryptionUtil.lenval(encKey,idx); | |
fprintf('position: %d\n',idx) | |
exp = java.math.BigInteger(1, buf); % exponent | |
% modulus [23:151] | |
[buf,idx] = EncryptionUtil.lenval(encKey,idx); | |
fprintf('position: %d\n',idx) | |
mod = java.math.BigInteger(1, buf); % modulus | |
% public key | |
publicKey = keyFactory.generatePublic( ... | |
java.security.spec.RSAPublicKeySpec(mod, exp)); | |
end | |
%% convert SSH private key to Java | |
function privateKey = convertSSHPrivateKey(keystr, keyFactory) | |
% ASN.1 PKCS#1 (v2.1) format (see rfc3447) | |
% http://en.wikipedia.org/wiki/Abstract_Syntax_Notation_One | |
% http://en.wikipedia.org/wiki/PKCS1 | |
% http://tools.ietf.org/html/rfc3447 | |
% base64 decode | |
encKey = EncryptionUtil.BASE64ENCODER.decode(keystr.getBytes); | |
fprintf('number of Bytes: %d\n',numel(encKey)); % number of bytes | |
% asn.1 header & key length [1] | |
prvkeyLen = swapbytes(typecast(encKey(3:4),'uint16')); | |
asn1seq = cellstr(dec2hex(typecast(encKey(1:2),'uint8'))); | |
asn1seq = ['x',asn1seq{:}]; % ASN.1 Sequence (x3082) | |
assert(strcmp(asn1seq,'x3082'), ... | |
'EncryptionUtil:testJschSECSH', ... | |
'Key is not an ASN.1 sequence. Header contains %s.',asn1seq) | |
fprintf('asn.1 header: %s == asn.1 sequence\nlength: %d bytes\n', ... | |
asn1seq,prvkeyLen) | |
% TODO: refactor to remove reduntancy, DER process repeats! | |
% key version [5] | |
idx = 5; | |
[verLen,idx] = EncryptionUtil.lenDer(encKey,idx); | |
keyVer = typecast(encKey(idx+1:idx+verLen),'uint8'); | |
fprintf('version: %d\n',keyVer) | |
% modulus [8] | |
idx = idx+verLen+1;fprintf('position: %d\n',idx) | |
[modLen,idx] = EncryptionUtil.lenDer(encKey,idx); | |
mod = java.math.BigInteger(1, encKey(idx+1:idx+modLen)); | |
% public exponent [140] | |
idx = idx+modLen+1;fprintf('position: %d\n',idx) | |
[pubExpLen,idx] = EncryptionUtil.lenDer(encKey,idx); | |
pubExp = java.math.BigInteger(1, encKey(idx+1:idx+pubExpLen)); | |
% private exponent [145] | |
idx = idx+pubExpLen+1;fprintf('position: %d\n',idx) | |
[prvExpLen,idx] = EncryptionUtil.lenDer(encKey,idx); | |
prvExp = java.math.BigInteger(1, encKey(idx+1:idx+prvExpLen)); | |
% prime P [276] | |
idx = idx+prvExpLen+1;fprintf('position: %d\n',idx) | |
[primePLen,idx] = EncryptionUtil.lenDer(encKey,idx); | |
primeP = java.math.BigInteger(1, encKey(idx+1:idx+primePLen)); | |
% prime Q [343] | |
idx = idx+primePLen+1;fprintf('position: %d\n',idx) | |
[primeQLen,idx] = EncryptionUtil.lenDer(encKey,idx); | |
primeQ = java.math.BigInteger(1, encKey(idx+1:idx+primeQLen)); | |
% prime exponent P [410] | |
idx = idx+primeQLen+1;fprintf('position: %d\n',idx) | |
[primeExpPLen,idx] = EncryptionUtil.lenDer(encKey,idx); | |
primeExpP = java.math.BigInteger(1, encKey(idx+1:idx+primeExpPLen)); | |
% prime exponent Q [477] | |
idx = idx+primeExpPLen+1;fprintf('position: %d\n',idx) | |
[primeExpQLen,idx] = EncryptionUtil.lenDer(encKey,idx); | |
primeExpQ = java.math.BigInteger(1, encKey(idx+1:idx+primeExpQLen)); | |
% prime coefficient [543] | |
idx = idx+primeExpQLen+1;fprintf('position: %d\n',idx) | |
[coeffLen,idx] = EncryptionUtil.lenDer(encKey,idx); | |
coeff = java.math.BigInteger(1, encKey(idx+1:idx+coeffLen)); | |
% private key | |
privateKey = keyFactory.generatePrivate( ... | |
java.security.spec.RSAPrivateCrtKeySpec(mod, pubExp, prvExp, ... | |
primeP, primeQ, primeExpP, primeExpQ, coeff)); | |
end | |
%% write Java public key in OpenSSH format | |
function publicSSHKey = writeSSHPublicKey(publicKey,filename,comment) | |
% write SSH public key | |
if nargin<2,filename = 'publicSSH.key';end | |
if nargin<3 | |
if ispc | |
comment = sprintf('%s@%s', ... | |
getenv('USERNAME'),getenv('COMPUTERNAME')); | |
else | |
comment = sprintf('%s@%s', ... | |
getenv('USERNAME'),getenv('HOSTNAME')); | |
end | |
end | |
mod = publicKey.getModulus; | |
exp = publicKey.getPublicExponent; | |
keyTypeLen = typecast(swapbytes(uint32(7)),'int8')'; | |
keyType = int8('ssh-rsa')'; | |
modBytes = mod.toByteArray; | |
modLen = typecast(swapbytes(uint32(numel(modBytes))),'int8')'; | |
expBytes = exp.toByteArray; | |
expLen = typecast(swapbytes(uint32(numel(expBytes))),'int8')'; | |
encKey = [keyTypeLen;keyType;expLen;expBytes;modLen;modBytes]; | |
encKey = EncryptionUtil.BASE64ENCODER.encode(encKey)'; | |
encKey = char(typecast(encKey,'uint8'))'; | |
BEGINSSH = '---- BEGIN SSH2 PUBLIC KEY ----'; | |
ENDSSH = '---- END SSH2 PUBLIC KEY ----'; | |
publicSSHKey = sprintf('%s\nComment: "%s"\n',BEGINSSH,comment); | |
lineLen = 70;nlines = floor(numel(encKey)/lineLen); | |
for l = 1:nlines | |
publicSSHKey = [publicSSHKey, ... | |
sprintf('%64s\n',encKey(lineLen*(l-1)+1:lineLen*l))]; %#ok<AGROW> | |
end | |
publicSSHKey = [publicSSHKey, ... | |
sprintf('%s\n%s\n',encKey(l*lineLen+1:end),ENDSSH)]; | |
try | |
fid = fopen(filename,'wt'); | |
fwrite(fid,publicSSHKey); | |
fclose(fid); | |
catch ME | |
fclose(fid); | |
throw(ME) | |
end | |
end | |
%% write Java private key in OpenSSH format | |
function privateSSHKey = writeSSHPrivateKey(privateKey,filename) | |
% write SSH private key | |
if nargin<2,filename = 'privateSSH.key';end | |
mod = privateKey.getModulus; | |
pubExp = privateKey.getPublicExponent; | |
prvExp = privateKey.getPrivateExponent; | |
primeP = privateKey.getPrimeP; | |
primeQ = privateKey.getPrimeQ; | |
expP = privateKey.getPrimeExponentP; | |
expQ = privateKey.getPrimeExponentQ; | |
coeff = privateKey.getCrtCoefficient; | |
% ASN.1 header sequence | |
integer = int8(2); % asn.1 integer type | |
asn1seq = typecast(uint8(cellfun(@hex2dec,{'30';'82'})),'int8'); | |
keyVer = int8(0); % version 0 | |
verLen = numel(keyVer); % version length in bytes | |
% convert big integers to byte arrays | |
modBytes = mod.toByteArray; | |
pubExpBytes = pubExp.toByteArray; | |
prvExpBytes = prvExp.toByteArray; | |
primePBytes = primeP.toByteArray; | |
primeQBytes = primeQ.toByteArray; | |
expPBytes = expP.toByteArray; | |
expQBytes = expQ.toByteArray; | |
coeffBytes = coeff.toByteArray; | |
% byte array lengths | |
modLen = numel(modBytes); | |
pubExpLen = numel(pubExpBytes); | |
prvExpLen = numel(prvExpBytes); | |
primePLen = numel(primePBytes); | |
primeQLen = numel(primeQBytes); | |
expPLen = numel(expPBytes); | |
expQLen = numel(expQBytes); | |
coeffLen = numel(coeffBytes); | |
% key length in bytes | |
keyLen = sum([2,verLen,3,modLen,2,pubExpLen,3,prvExpLen, ... | |
2,primePLen,2,primeQLen,2,expPLen,2,expQLen,2,coeffLen]); | |
keyLen = typecast(swapbytes(uint16(keyLen)),'int8')'; % length of key | |
% byte array lengths | |
verLen = EncryptionUtil.derlen(verLen); | |
modLen = EncryptionUtil.derlen(modLen); | |
pubExpLen = EncryptionUtil.derlen(pubExpLen); | |
prvExpLen = EncryptionUtil.derlen(prvExpLen); | |
primePLen = EncryptionUtil.derlen(primePLen); | |
primeQLen = EncryptionUtil.derlen(primeQLen); | |
expPLen = EncryptionUtil.derlen(expPLen); | |
expQLen = EncryptionUtil.derlen(expQLen); | |
coeffLen = EncryptionUtil.derlen(coeffLen); | |
% make DER | |
encKey = [asn1seq;keyLen; ... % ASN.1 Sequence & key length | |
integer;verLen;keyVer; ... % key version | |
integer;modLen;modBytes; ... % modulus | |
integer;pubExpLen;pubExpBytes; ... % public exponent | |
integer;prvExpLen;prvExpBytes; ... % private exponent | |
integer;primePLen;primePBytes; ... % prime P | |
integer;primeQLen;primeQBytes; ... % prime Q | |
integer;expPLen;expPBytes; ... % prime P exponent | |
integer;expQLen;expQBytes; ... % prime Q exponent | |
integer;coeffLen;coeffBytes]; % CRT coefficient | |
encKey = EncryptionUtil.BASE64ENCODER.encode(encKey)'; | |
encKey = char(typecast(encKey,'uint8'))'; | |
BEGINRSA = '-----BEGIN RSA PRIVATE KEY-----'; | |
ENDRSA = '-----END RSA PRIVATE KEY-----'; | |
privateSSHKey = sprintf('%s\n',BEGINRSA); | |
lineLen = 64;nlines = floor(numel(encKey)/lineLen); | |
for l = 1:nlines | |
privateSSHKey = [privateSSHKey, ... | |
sprintf('%64s\n',encKey(lineLen*(l-1)+1:lineLen*l))]; %#ok<AGROW> | |
end | |
privateSSHKey = [privateSSHKey, ... | |
sprintf('%s\n%s\n',encKey(l*lineLen+1:end),ENDRSA)]; | |
try | |
fid = fopen(filename,'wt'); | |
fwrite(fid,privateSSHKey); | |
fclose(fid); | |
catch ME | |
fclose(fid); | |
throw(ME) | |
end | |
end | |
%% DER value length | |
% DER parses anything over 7 bits into more than 2 bytes | |
function len = derlen(len) | |
% convert length as double to DER bytes | |
if len<128 | |
% return length in 1-byte if less than 128 | |
len = typecast(uint8(len),'int8'); | |
return | |
else | |
% number of bits required | |
nlen = nextpow2(len+1); % number of bits | |
end | |
if nlen == 8 | |
% 8-bit integer (1-byte) | |
len = typecast([uint8(128+1);uint8(len)],'int8'); % append # of bytes | |
elseif nlen == 9 | |
% 16-bit integer (2-bytes) | |
len = [typecast(uint8(128+2),'int8'), ... | |
typecast(swapbytes(uint16(len)),'int8')]'; % append # of bytes | |
else | |
error('EncryptionUtil:derlen','Length is greater than 16-bits.') | |
end | |
end | |
function [len,idx] = lenDer(encKey,idx) | |
% convert DER bytes to length as double | |
assert(encKey(idx)==2,'EncryptionUtil:lenDer', ... | |
'The private key was not parsed correctly.') | |
idx = idx+1; % increment pointer | |
len = double(typecast(encKey(idx),'uint8')); | |
if len<128 | |
% return if less than 128, length stored in only 1-byte | |
return | |
else | |
% number of bytes required, [1 or 2] only | |
nlen = mod(len,128); % number of bytes, [1 or 2] only | |
end | |
if nlen == 1 | |
% 8-bit integer (1-byte) | |
idx = idx+nlen; % increment pointer | |
len = double(typecast(encKey(idx),'uint8')); | |
elseif nlen == 2 | |
% 16-bit integer (2-byte) | |
idx = idx+nlen; % increment pointer | |
len = double(swapbytes(typecast(encKey(idx-1:idx),'uint16'))); | |
else | |
error('EncryptionUtil:lenDer','Length is greater than 16-bits.') | |
end | |
end | |
%% write Java key pair in OpenSSH format | |
function [publicSSHKey,privateSSHKey] = writeSSHKeys(publicKey,privateKey,varargin) | |
pubvarargin = varargin; | |
if numel(pubvarargin)>0 | |
pubvarargin{1} = [pubvarargin{1},'.pub']; | |
end | |
if numel(varargin)>0 | |
varargin = varargin(1); | |
end | |
publicSSHKey = EncryptionUtil.writeSSHPublicKey(publicKey,pubvarargin{:}); | |
privateSSHKey = EncryptionUtil.writeSSHPrivateKey(privateKey,varargin{:}); | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%% initialize workspace | |
close('all'),clear('all'),clc | |
rmdir('keys','s') % delete old keys | |
%% test encryption | |
PANGRAM = 'The quick brown fox jumped over the lazy dog.'; | |
EncryptionUtil.testJschSECSH(PANGRAM) % run test | |
%% make SSH keys in workspace | |
keyFactory = java.security.KeyFactory.getInstance('RSA'); % make a key factory | |
% read public OpenSSH key | |
[keyln, totlines] = EncryptionUtil.readJschKeyFile('keys/publicJschSECSH.key',7); | |
keystr = EncryptionUtil.joinLines(keyln,totlines,2,1); % key string | |
publicKey = EncryptionUtil.convertSSHPublicKey(keystr, keyFactory); % convert key | |
% read private OpenSSH key | |
[keyln, totlines] = EncryptionUtil.readJschKeyFile('keys/privateJsch.key'); | |
keystr = EncryptionUtil.joinLines(keyln,totlines,1,1); % key string | |
privateKey = EncryptionUtil.convertSSHPrivateKey(keystr, keyFactory); % convert key | |
%% write and compare keys | |
keyfiles = 'keys/test.key';comment = 'testReadWriteSSHKeyPair'; | |
[publicSSHKey,privateSSHKey] = EncryptionUtil.writeSSHKeys(publicKey,privateKey, ... | |
keyfiles,comment); | |
fprintf('\n%s\n%s\n',publicSSHKey,privateSSHKey) | |
visdiff('keys\privateJsch.key','keys\test.key') | |
visdiff('keys\publicJschSECSH.key','keys\test.key.pub') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment